English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Introduction
Why can jQuery unbind events without passing a callback function? As follows:
$("p").on("click",function(){ alert("The paragraph was clicked."); }); $("#box1").off("click");
Event binding and unbinding mechanism
When the on function is called, it generates an event data with the following structure:
{ type: type, origType: origType, data: data, handler: handler, guid: guid, selector: selector, needsContext: needsContext, namespace: namespace };
and add this data to the element's cache. In jQuery, each element can have a cache (created only when needed), which is essentially an attribute of the element. jQuery creates a queue for each type of event of an element to save event handler functions, so multiple event handler functions can be added to an element. The structure of the cache is as follows:
"div#box":{ //element "Jquery623873:{ //element cache "events":{ "click":[ { //element click event data type: type, origType: origType, data: data, handler: handler, guid: guid, selector: selector, needsContext: needsContext, namespace: namespace }; ], "mousemove":[ { type: type, origType: origType, data: data, handler: handler, guid: guid, selector: selector, needsContext: needsContext, namespace: namespace }; ] }; }; };
When you want to unbind an event, if the fn parameter is not specified, jQuery will take the queue of event handlers to be unbound from the cache of the element, take out the fn parameter, and then call removeEventListener to unbind.
Source code
The code comments may not be very clear, you can copy them out to see
The on, one, off methods in jQuery prototype:
Event binding starts here
jQuery.fn.extend({ on: function(types, selector, data, fn) { return on(this, types, selector, data, fn); }, one: function(types, selector, data, fn) { return on(this, types, selector, data, fn, 1 ); }, off: function(types, selector, fn) { //The code for handling parameters is omitted here return this.each(function() { jQuery.event.remove(this, types, fn, selector); }); }; });
Independent on function for one and on calls:
function on(elem, types, selector, data, fn, one) { var origFn, type; //The code for handling parameters is omitted here //Whether it is bound using one, if so, use a function proxy for the current event callback function, the proxy function only executes once //Here, the proxy pattern is used if (one === 1 ) origFn = fn; fn = function(event) { // Can use an empty set, since event contains the info jQuery().off(event); return origFn.apply(this, arguments); }; // Use the same guid so that the caller can remove using origFn fn.guid = origFn.guid || (origFn.guid = jQuery.guid++ ); }; /************************************************ *** jQuery puts all selected elements into an array and then *** Bind events to each element using the add method of the event object *************************************************/ return elem.each(function() { jQuery.event.add(this, types, fn, data, selector); }); };
You can also take a look at the code for handling parameters, which implements calls like on("click", function() {}) so that on: function(types, selector, data, fn) will not fail. It is actually an internal judgment, if the data, fn parameters are empty, then assign selector to fn
The event object is a key object for event binding:
Here, the work of binding events to elements and adding event information to element caches is handled:
jQuery.event = { add: function(elem, types, handler, data, selector) { var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.get(elem); //This line will check if elem is cached, and if not, it will create a cache and add it to the elem element. The form is like: elem["jQuery310057655476080253721"] = {}; // Don't attach events to noData or text/comment nodes (but allow plain objects) if (!elemData) { return; }; //Users can pass a custom data object to replace the event callback function, placing the event callback function in the handler attribute of this data object if (handler.handler) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; }; //Each event callback function will generate a unique id, which can be used to find/It will be used when removing if (!handler.guid) { handler.guid = jQuery.guid++; }; // If the element binds the event for the first time, it initializes the element's event data structure and the main callback function (main) //说明:每个元素有一个主回调函数,作为绑定多个事件到该元素时的回调的入口 如果(!events = elemData.events){ events = elemData.events = {}; }; //这里就是初始化主回调函数的代码 如果(!eventHandle = elemData.handle){ eventHandle = elemData.handle = function( e ) { // 丢弃jQuery.event.trigger()的第二个事件和 // 当页面已卸载后调用事件时 返回typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type &63; jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; }; // 处理事件绑定,考虑到可能会通过空格分隔传入多个事件,这里要进行多事件处理 types = (types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while(t-- ) tmp = rtypenamespace.exec( types[ t ] ) || []; 类型 = 原始类型 = tmp[ 1 ]; 命名空间 = (tmp[ 2 ] || "" ).split( "." ).sort(); // 在那里 *必须* 是类型,无需附加命名空间-只有处理器 如果(!类型){ 继续; }; // 如果事件更改其类型,则使用更改类型的特殊事件处理器 special = jQuery.event.special[type] || {}; // 如果定义了选择器,确定特殊事件API类型,否则给出类型 type = (selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[type] || {}; // Data object of the event callback function handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test(selector), namespace: namespaces.join(".") }, handleObjIn); // When binding this class event for the first time, an array is initialized as the event callback function queue, and each element has a queue for each type of event if (!(handlers = events[type])) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) if (elem.addEventListener) { elem.addEventListener(type, eventHandle); }; }; }; if (special.add) { special.add.call(elem, handleObj); if (!handleObj.handler.guid) { handleObj.handler.guid = handler.guid; }; }; // Add to the event callback function queue if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); }; // Keep track of which events have ever been used, for event optimization // Used to track which events have never been used, for optimization jQuery.event.global[ type ] = true; }; }; };
Pay close attention, as objects and arrays are passed by reference! For example, the code to save event data to the cache:
handlers = events[ type ] = []; if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); };
Changes to handlers will cause events[ type ] to change simultaneously.
dataPriv is the object that manages the cache:
Its job is to create a property for the element, which is an object, and then put the information related to this element into this object and cache it. So when you need to use the information of this object, you can get it just by knowing this object:
function Data() { this.expando = jQuery.expando + Data.uid++; }; Data.uid = 1; //Delete some unused code Data.prototype = { cache: function( owner ) { // Retrieve the cache, as the cache is an attribute of the target object var value = owner[ this.expando ]; // If the object has not been cached yet, create one if ( !value ) { value = {}; // We can accept data for non--element nodes in modern browsers, // but we should not, see #8335. // Always return an empty object. if ( acceptData( owner ) ) { // If it is a node unlikely to be stringify-ed or looped over // use plain assignment if (owner.nodeType) { owner[ this.expando ] = value; // Otherwise secure it in a non--enumerable property // configurable must be true to allow the property to be // deleted when data is removed } else { Object.defineProperty( owner, this.expando, { value: value, configurable: true }); }; }; }; return value; }, get: function( owner, key ) { return key === undefined &63; this.cache( owner ) : // Always use camelCase key (gh-2257) owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ]; }, remove: function( owner, key ) { var i, cache = owner[ this.expando ]; if ( cache === undefined ) { return; }; if ( key !== undefined ) { // Support array or space separated string of keys if ( jQuery.isArray( key ) ) { // If key is an array of keys... // We always set camelCase keys, so remove that. key = key.map( jQuery.camelCase ); } else { key = jQuery.camelCase( key ); // If a key with the spaces exists, use it. // Otherwise, create an array by matching non-whitespace key = key in cache &63; [ key ] : ( key.match( rnotwhite ) || [] ); }; i = key.length; while (-- ) delete cache[key[i]];} }; }; // Remove the expando if there's no more data if (key === undefined || jQuery.isEmptyObject(cache)) { // Support: Chrome <=35 - 45 // Webkit & Blink performance suffers when deleting properties // from DOM nodes, so set to undefined instead // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) if (owner.nodeType) { owner[this.expando] = undefined; } else { delete owner[this.expando]; }; }; }, hasData: function(owner) { var cache = owner[this.expando]; return cache !== undefined && !jQuery.isEmptyObject(cache); }; };
That is all the content of this article. I hope it will be helpful to everyone's learning and also hope everyone will support the Yelling Tutorial more.
Statement: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report abuse, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.)