Simple Analysis and Implementation of jquery binding principles

Source: Internet
Author: User
Tags delete cache

In jq, there is a data method that binds the relevant data to the dom element. When an event is bound to the dom using the jq method, a corresponding time list is generated.
You can refer to the following example (please check in firefox because objects in firefox support toSource ())
Copy codeThe Code is as follows:
<! DOCTYPE html>
<Html>
<Head>
<Meta http-equiv = "Content-Type" content = "text/html; charset = gb2312"/>
<Title> </title>
</Head>
<Body>
<Div id = "test"> </div>
<Script type = "text/javascript" src = "http://common.cnblogs.com/script/jquery.js"> </script>
<Script type = "text/javascript">
Window. onload = function (){
Alert ($. data ($ ('# test') [0], 'events'); // null
Alert ($. data ($ ('# test') [0], 'handle'); // null
$ ('# Test ')
. Bind ('click', function (){
Alert (1)
})
. Bind ('mouseover', function (){
Alert (2)
})
. Bind ('click', function (){
Alert (3)
})
. Bind ('click', function (){
Alert (4)
})
Alert ($. data ($ ('# test') [0], 'events'). toSource (); // time list
Alert ($. data ($ ('# test') [0], 'handle'). toSource (); // The executed Function
}
</Script>
</Body>
</Html>

Data is bound to the element.
The data source is a cache object.
When an element binds data, it adds a jQueryxxx attribute to the element, which is the timestamp for executing jq.
It should be noted that there is a uuid which is accumulated
The value of jQueryxxx is the uuid.
The cache key is the uuid.
Value is the data to be stored.
Data is very important for event binding ................................
Copy codeThe Code is as follows:
Function now (){
Return + new Date;
};
Var win = this,
Expando = "jQuery" + now (),
Uuid = 0,
Cache = {};
Win. data = function (elem, name, data ){
Var id = elem [expando];
If (! Id)
Id = elem [expando] = ++ uuid;
If (name &&! Cache [id])
Cache [id] = {};
If (data! = Undefined)
Cache [id] [name] = data;
Return name
? Cache [id] [name]
: Id;
}
Win. removeData = function (elem, name ){
Var id = elem [expando];
If (name ){
If (cache [id]) {
Delete cache [id] [name];
Name = "";
For (name in cache [id])
Break;
If (! Name)
RemoveData (elem );
}
} Else {
Try {
Delete elem [expando];
} Catch (e ){
If (elem. removeAttribute)
Elem. removeAttribute (expando );
}
Delete cache [id];
}
}
Win. each = function (object, callback, args ){
Var name, I = 0, length = object. length;
If (args ){
If (length = undefined ){
For (name in object)
If (callback. apply (object [name], args) === false)
Break;
} Else
For (; I <length ;)
If (callback. apply (object [I ++], args) === false)
Break;
} Else {
If (length = undefined ){
For (name in object)
If (callback. call (object [name], name, object [name]) === false)
Break;
} Else
For (var value = object [0];
I <length & callback. call (value, I, value )! = False; value = object [++ I]) {}
}
Return object;
}

Add events
In jq, The add method in jQuery. event is used.
Some functions are implemented in the add method.
Take the events and handle data bound to the element.
Events stores the event list.
The format is as follows:
{
Click: [{handler: function () {}, type: "click", guid: 'xx'} ......],
Mouse: [...]
}
Handle is the execution function.
(All execution functions are the same. They traverse the event list to execute corresponding events)
Then traverse types because multiple events can be bound.
The callback function also provides several attributes.
Assume that the callback function is handler.
Handler. guid = gevent. guid ++
Handler. type = name
Name should be a special name to facilitate deletion.
For example
$ ('# Xx ')
. Bind ('click', function (){})
. Bind ('click. D', handler)
The name is d.
You can delete only the d event and not the previous click event.
Finally, bind events to the element, but the executed functions are
Function (){
Gevent. handle. apply (arguments. callee. elem, arguments );
});
Copy codeThe Code is as follows:
Win. gevent = {
Guid: 1,
Add: function (elem, types, handler ){
If (elem. nodeType = 3 | elem. nodeType = 8)
Return;
If (elem. setInterval & elem! = Window)
Elem = window;
// An index with a unique identifier for the function to facilitate deletion of the event later
If (! Handler. guid)
Handler. guid = this. guid ++;
// Obtain the data under events handle of this element
Var events = data (elem, "events") | data (elem, "events ",{}),
Handle = data (elem, "handle") | data (elem, "handle", function (){
// Gevent. handle is the function that will be executed after various actions are triggered.
Gevent. handle. apply (arguments. callee. elem, arguments );
});
Handle. elem = elem;
// Retrieve the event name because it can be click mouseover.
Each (types. split (/\ s +/), function (index, type ){
Var namespaces = type. split (".");
// Obtain the event name
Type = namespaces. shift ();
// Remove the things after the vertex is a special name. You can specify to delete the vertex such as click. d When deleting the vertex.
// Use the event type to record this special name
Handler. type = namespaces. slice (). sort (). join (".");
// Obtain whether the event already exists in the events object
Var handlers = events [type];
// If this event does not exist, bind it to the element.
If (! Handlers ){
Handlers = events [type] = {};
If (elem. addEventListener)
Elem. addEventListener (type, handle, false );
Else if (elem. attachEvent)
Elem. attachEvent ("on" + type, handle );
}
// Put the bar function in the event list of the element.
Handlers [handler. guid] = handler;
});
Elem = null;
}
}

Gevent. hander is the function actually executed by binding events.
In gevent. hander, there are some special names, but I don't know what to do.
In the hander, encapsulate the event first.
For more information about packaging, see gevent. fix and setEvent.
It is mainly to make a copy of a native event and then combine incompatible methods into compatible writing methods.
Then, retrieve the events (event list) of the element)
Then, the system traverses the event list and determines whether the type is the key of the event list. Then, the system executes the event.
The return value is determined when the list function is executed.
If false is returned, event bubbles and default behaviors can also be organized.
Copy codeThe Code is as follows:
Win. gevent = {
Handle: function (event ){
Var all, handlers;
// Wrap the event
Event = arguments [0] = gevent. fix (event | window. event );

Event. currentTarget = this;

// Here ........
Var namespaces = event. type. split (".");
Event. type = namespaces. shift ();
All =! Namespaces. length;
Var namespace = RegExp ("(^ | \\.) "+ namespaces. slice (). sort (). join (". *\\. ") + "(\\. | $ )");
// List of events that take the behavior of this element
Handlers = (data (this, "events") |{}) [event. type];
// Traverse the event list to execute the execution
For (var j in handlers ){
Var handler = handlers [j];
If (all | namespace. test (handler. type )){
// Pass in a reference to the handler function itself
// So that we can later remove it
// The Annotation on jq is written in this way. reference the event handler to this event and remove it easily.
// However, the handler that does not use the event in the remove operation does not know what is the use of this event. When there are multiple events, this event is replaced.
Event. handler = handler;
// Execute the event and use the element to call the event. The this execution element ret in the event is the return value of the function.
Var ret = handler. apply (this, arguments );
// If a returned value is returned and the returned value is false, the event is blocked. The event is bubble and the default action of the event is blocked.
If (ret! = Undefined ){
Event. result = ret;
If (ret = false ){
Event. preventDefault ();
Event. stopPropagation ();
}
}
}
}
},
Props: "altKey attrChange attrName bubbles button cancelable charCode contains invalid currentTarget data detail eventPhase fromElement handler keyCode contains newValue extends pageX pageY prevValue contains too many screenY limit srcElement target toElement view wheelDelta which ". split (""),
Fix: function (event ){
// New setEvent will give the event an expando attribute. If there is a medium attribute, it indicates that the event has been generated and does not need to be encapsulated at the next time.
If (event [expando])
Return event;
// Keep an original event
// A new event is different from the original event.
Var originalEvent = event;
Event = new setEvent (originalEvent );
// For the attribute values of the original event, see this. props.
For (var I = this. props. length, prop; I ;){
Prop = this. props [-- I];
Event [prop] = originalEvent [prop];
}
// Synchronize the target element with event.tar get
If (! Event.tar get)
Event.tar get = event. srcElement | document; // Fixes #1925 where srcElement might not be defined either
// If a text node is found to have its parent node
If (event.tar get. nodeType = 3)
Event.tar get = event.tar get. parentNode;
If (! Event. relatedTarget & event. fromElement)
Event. relatedTarget = event. fromElement = event.tar get? Event. toElement: event. fromElement;
Return event;
}
}
Win. setEvent = function (src ){
// Allow instantiation without the 'new' keyword
// Event object
If (src & src. type ){
This. originalEvent = src;
This. type = src. type;
// Event type
} Else
This. type = src;
// TimeStamp is buggy for some events on Firefox (#3843)
// So we won't rely on the native value
This. timeStamp = now ();
// Mark it as fixed
This [expando] = true;
}
Function returnFalse (){
Return false;
}
Function returnTrue (){
Return true;
}
SetEvent. prototype = {
PreventDefault: function (){
Var e = this. originalEvent;
If (! E)
Return;
// If preventDefault exists run it on the original event
If (e. preventDefault)
E. preventDefault ();
// Otherwise set the returnValue property of the original event to false (IE)
E. returnValue = false;
},
StopPropagation: function (){
Var e = this. originalEvent;
If (! E)
Return;
// If stopPropagation exists run it on the original event
If (e. stopPropagation)
E. stopPropagation ();
// Otherwise set the cancelBubble property of the original event to true (IE)
E. cancelBubble = true;
},
StopImmediatePropagation: function (){
This. isImmediatePropagationStopped = returnTrue;
This. stopPropagation ();
},
IsImmediatePropagationStopped: returnFalse
};

A complete example
Copy codeThe Code is as follows:
<! DOCTYPE html PUBLIC "-// W3C // dtd xhtml 1.0 Transitional // EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<Html xmlns = "http://www.w3.org/1999/xhtml">
<Head>
<Meta http-equiv = "Content-Type" content = "text/html; charset = gb2312"/>
<Title> </title>
</Head>
<Body>
<Div id = "vv" style = "height: 200px; width: 200px; background-color: # f00; padding: 20px;">
<Div id = "xx" style = "height: 200px; width: 200px; background-color: #000000;"> </div>
</Div>
<Script type = "text/javascript">
(Function (doc, undefined ){
Function now (){
Return + new Date;
};
Var win = this,
Expando = "jQuery" + now (),
Uuid = 0,
Cache = {};
Win. data = function (elem, name, data ){
Var id = elem [expando];
If (! Id)
Id = elem [expando] = ++ uuid;
If (name &&! Cache [id])
Cache [id] = {};
If (data! = Undefined)
Cache [id] [name] = data;
Return name
? Cache [id] [name]
: Id;
}
Win. removeData = function (elem, name ){
Var id = elem [expando];
If (name ){
If (cache [id]) {
Delete cache [id] [name];
Name = "";
For (name in cache [id])
Break;
If (! Name)
RemoveData (elem );
}
} Else {
Try {
Delete elem [expando];
} Catch (e ){
If (elem. removeAttribute)
Elem. removeAttribute (expando );
}
Delete cache [id];
}
}
Win. each = function (object, callback, args ){
Var name, I = 0, length = object. length;
If (args ){
If (length = undefined ){
For (name in object)
If (callback. apply (object [name], args) === false)
Break;
} Else
For (; I <length ;)
If (callback. apply (object [I ++], args) === false)
Break;
} Else {
If (length = undefined ){
For (name in object)
If (callback. call (object [name], name, object [name]) === false)
Break;
} Else
For (var value = object [0];
I <length & callback. call (value, I, value )! = False; value = object [++ I]) {}
}
Return object;
}
Win. gevent = {
Guid: 1,
Add: function (elem, types, handler ){
If (elem. nodeType = 3 | elem. nodeType = 8)
Return;
If (elem. setInterval & elem! = Window)
Elem = window;
// An index with a unique identifier for the function to facilitate deletion of the event later
If (! Handler. guid)
Handler. guid = this. guid ++;
// Obtain the data under events handle of this element
Var events = data (elem, "events") | data (elem, "events ",{}),
Handle = data (elem, "handle") | data (elem, "handle", function (){
// Gevent. handle is the function that will be executed after various actions are triggered.
Gevent. handle. apply (arguments. callee. elem, arguments );
});
Handle. elem = elem;
// Retrieve the event name because it can be click mouseover.
Each (types. split (/\ s +/), function (index, type ){
Var namespaces = type. split (".");
// Obtain the event name
Type = namespaces. shift ();
// Remove the things after the vertex is a special name. You can specify to delete the vertex such as click. d When deleting the vertex.
// Use the event type to record this special name
Handler. type = namespaces. slice (). sort (). join (".");
// Obtain whether the event already exists in the events object
Var handlers = events [type];
// If this event does not exist, bind it to the element.
If (! Handlers ){
Handlers = events [type] = {};
If (elem. addEventListener)
Elem. addEventListener (type, handle, false );
Else if (elem. attachEvent)
Elem. attachEvent ("on" + type, handle );
}
// Put the bar function in the event list of the element.
Handlers [handler. guid] = handler;
});
Elem = null;
},
Remove: function (elem, types, handler ){
If (elem. nodeType = 3 | elem. nodeType = 8)
Return;
// Obtain the list of all actions of this element, such as {click :{}, mouseocer :{}}
Var events = data (elem, "events"), ret, index;
If (events ){
// Delete all events of this element if there are no different behavior types
// If the input is in the form of. xx, all actions including. xx are killed
If (types = undefined | (typeof types = "string" & types. charAt (0) = ".")){
For (var type in events)
This. remove (elem, type + (types | ""));
} Else {
// I don't know why
If (types. type ){
Handler = types. handler;
Types = types. type;
}
// Because deletion events can be deleted at a time, for example, click mouseover, and all objects to be traversed and deleted
Each (types. split (/\ s +/), function (index, type ){
Var namespaces = type. split (".");
Type = namespaces. shift ();
Var namespace = RegExp ("(^ | \\.) "+ namespaces. slice (). sort (). join (". *\\. ") + "(\\. | $ )");
If (events [type]) {
// If 3rd parameter functions are passed, delete the event.
If (handler)
Delete events [type] [handler. guid];
Else {
// Traverse all the actions of this one
For (var handle in events [type]) {
// Handle the removal of namespaced events
// Delete a function with a special name
// If there is no special naming regular, It is/^ |... | $/can match null, so it can also delete functions without special names.
If (namespace. test (events [type] [handle]. type ))
Delete events [type] [handle];
}
}
}
For (ret in events [type]) break;
// If events [type] becomes null, that is, {} deletes the binding event of this element.
If (! Ret ){
If (elem. removeEventListener)
Elem. removeEventListener (type, data (elem, "handle"), false );
Else if (elem. detachEvent)
Elem. detachEvent ("on" + type, data (elem, "handle "));
Ret = null;
Delete events [type];
}
});
}
For (ret in events) break;
// If the entire events of the element is found to be empty
// Clear the handle and all its references
If (! Ret ){
Var handle = data (elem, "handle ");
If (handle) handle. elem = null;
RemoveData (elem, "events ");
RemoveData (elem, "handle ");
}
}
},
Handle: function (event ){
Var all, handlers;
// Wrap the event
Event = arguments [0] = gevent. fix (event | window. event );
Event. currentTarget = this;
// Here ........
Var namespaces = event. type. split (".");
Event. type = namespaces. shift ();
All =! Namespaces. length;
Var namespace = RegExp ("(^ | \\.) "+ namespaces. slice (). sort (). join (". *\\. ") + "(\\. | $ )");
// List of events that take the behavior of this element
Handlers = (data (this, "events") |{}) [event. type];
// Traverse the event list to execute the execution
For (var j in handlers ){
Var handler = handlers [j];
If (all | namespace. test (handler. type )){
// Pass in a reference to the handler function itself
// So that we can later remove it
// The Annotation on jq is written in this way. reference the event handler to this event and remove it easily.
// However, the handler that does not use the event in the remove operation does not know what is the use of this event. When there are multiple events, this event is replaced.
Event. handler = handler;
// Execute the event and use the element to call the event. The this execution element ret in the event is the return value of the function.
Var ret = handler. apply (this, arguments );
// If a returned value is returned and the returned value is false, the event is blocked. The event is bubble and the default action of the event is blocked.
If (ret! = Undefined ){
Event. result = ret;
If (ret = false ){
Event. preventDefault ();
Event. stopPropagation ();
}
}
}
}
},
Props: "altKey attrChange attrName bubbles button cancelable charCode contains invalid currentTarget data detail eventPhase fromElement handler keyCode contains newValue extends pageX pageY prevValue contains too many screenY limit srcElement target toElement view wheelDelta which ". split (""),
Fix: function (event ){
// New setEvent will give the event an expando attribute. If there is a medium attribute, it indicates that the event has been generated and does not need to be encapsulated at the next time.
If (event [expando])
Return event;
// Keep an original event
// A new event is different from the original event.
Var originalEvent = event;
Event = new setEvent (originalEvent );
// For the attribute values of the original event, see this. props.
For (var I = this. props. length, prop; I ;){
Prop = this. props [-- I];
Event [prop] = originalEvent [prop];
}
// Synchronize the target element with event.tar get
If (! Event.tar get)
Event.tar get = event. srcElement | document; // Fixes #1925 where srcElement might not be defined either
// If a text node is found to have its parent node
If (event.tar get. nodeType = 3)
Event.tar get = event.tar get. parentNode;
If (! Event. relatedTarget & event. fromElement)
Event. relatedTarget = event. fromElement = event.tar get? Event. toElement: event. fromElement;
Return event;
}
}
Win. setEvent = function (src ){
// Allow instantiation without the 'new' keyword
// Event object
If (src & src. type ){
This. originalEvent = src;
This. type = src. type;
// Event type
} Else
This. type = src;
// TimeStamp is buggy for some events on Firefox (#3843)
// So we won't rely on the native value
This. timeStamp = now ();
// Mark it as fixed
This [expando] = true;
}
Function returnFalse (){
Return false;
}
Function returnTrue (){
Return true;
}
SetEvent. prototype = {
PreventDefault: function (){
Var e = this. originalEvent;
If (! E)
Return;
// If preventDefault exists run it on the original event
If (e. preventDefault)
E. preventDefault ();
// Otherwise set the returnValue property of the original event to false (IE)
E. returnValue = false;
},
StopPropagation: function (){
Var e = this. originalEvent;
If (! E)
Return;
// If stopPropagation exists run it on the original event
If (e. stopPropagation)
E. stopPropagation ();
// Otherwise set the cancelBubble property of the original event to true (IE)
E. cancelBubble = true;
},
StopImmediatePropagation: function (){
This. isImmediatePropagationStopped = returnTrue;
This. stopPropagation ();
},
IsImmediatePropagationStopped: returnFalse
};
}) (Document );
Var $ = function (id) {return document. getElementById (id )}
Var a = function () {alert (1 )}
Window. onload = function (){
Gevent. add ($ ('xx'), 'click', );
Gevent. add ($ ('xx'), 'click', function () {alert (1 )});
Gevent. add ($ ('xx'), 'click', function () {alert (2 )});
Gevent. add ($ ('xx'), 'click', function () {alert (3 )});
Gevent. add ($ ('xx'), 'click. xx', function () {alert (4 )});
}
</Script>
</Body>
</Html>

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.