📜 ⬆️ ⬇️

jQuery.live in detail

In view of the recent discussion of the speed of jQuery.live and prudence to write its own delegation of event handlers, I decided to sort out the work of jQuery.live. Those. The purpose of this topic is to identify all the features when using live binder and analyze the code. Without comparative characteristics, without bringing the best delegation methods.

The principle of live is based on the delegation of event handlers.

Delegation is a pattern based on 2 principles of javascript: event bubbling stage and the ability to determine the element that tracked the event.
')
The fact that delegation only tracks the ascent stage of an event explains the impossibility of hanging live-binders on blur, focus, mouseenter, mouseleave, change and submit events: all these events have no capture and ascent stages.



Consider the simplest example:

< html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  1. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  2. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  3. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  4. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  5. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  6. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  7. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  8. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  9. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  10. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  11. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  12. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  13. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  14. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  15. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
  16. < html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .
< html > < head > < script type ='text/javascript' src ='jquery-1.3.2.js' > </ script > <script type= 'text/javascript' > var divOnClick = function (_e) { console.log( 'div clicked' ); } $( '.ololo' ).live( 'click' , divOnClick); </ script > </ head > < body > < div class ="ololo" > < p > ololo </ p > </ div > </ body > </ html > * This source code was highlighted with Source Code Highlighter .


Let's see what happens when the event is hung on a div element.

  1. live: function (type, fn) {
  2. var proxy = jQuery. event .proxy (fn);
  3. proxy.guid + = this .selector + type;
  4. jQuery ( document ) .bind (liveConvert (type, this .selector), this .selector, proxy);
  5. return this ;
  6. }
* This source code was highlighted with Source Code Highlighter .


This is the live function itself.

First, a proxy function is created to be called in the context of the original object.

  1. proxy = proxy || function () { return fn.apply ( this , arguments); };
* This source code was highlighted with Source Code Highlighter .


Note that this is here in the context of the original object, i.e. points to $ ('. ololo')

Go back to live. The proxying function is assigned a unique guid.

The liveConvert function generates a special name for the event in the live namespace.

  1. function liveConvert (type, selector) {
  2. return [ "live" , type, selector.replace (/\./ g, "` " ) .replace (/ / g, " | " ).. join ( ". " );
  3. }
* This source code was highlighted with Source Code Highlighter .


In the example, the function will return the string: live.click.`ololo

At this moment, the event handler is delegated (in the click example) to the $ (document) object. Those. now simply all click events will catch the document object, not the original element.

bind in this case works in the context of $ (document) and adds an event handler to it.

  1. bind: function (type, data, fn) {
  2. return type == "unload" ? this .one (type, data, fn): this .each ( function () {
  3. jQuery event .add ( this , type, fn || data, fn && data);
  4. });
  5. },
* This source code was highlighted with Source Code Highlighter .


We will not analyze the whole jQuery.event.add method, we’ll dwell only on the places of interest to us.

  1. jQuery event = {
  2. add: function (elem, types, handler, data) {
  3. ...
* This source code was highlighted with Source Code Highlighter .


Parameters in this method are given: $ (document), "live.click.'ololo", ".ololo", and the original proxied function.

  1. jQuery.each (types.split (/ \ s + /), function (index, type) {
  2. var namespaces = type.split ( "." );
  3. type = namespaces.shift ();
  4. ...
  5. if (jQuery. event .specialAll [type])
  6. jQuery event .specialAll [type] .setup.call (elem, data, namespaces);
* This source code was highlighted with Source Code Highlighter .


Here, the namespace live is allocated and it is checked if there are any values ​​for the given key in the jQuery.event.specialAll hash. To live there is:

  1. specialAll: {
  2. live: {
  3. setup: function (selector, namespaces) {
  4. jQuery event .add ( this , namespaces [0], liveHandler);
  5. },
  6. ...
* This source code was highlighted with Source Code Highlighter .


Here we are interested only in the liveHandler function, which explains all the features of the jQuery.live method. I will give her code in full:

  1. function liveHandler ( event ) {
  2. var check = RegExp ( "(^ | \\.)" + event . type + "(\\. | $)" ),
  3. stop = true
  4. elems = [];
  5. jQuery.each (jQuery.data ( this , "events" ) .live || [], function (i, fn) {
  6. if (check.test (fn.type)) {
  7. var elem = jQuery ( event .target) .closest (fn.data) [0];
  8. if (elem)
  9. elems.push ({elem: elem, fn: fn});
  10. }
  11. });
  12. elems.sort ( function (a, b) {
  13. return jQuery.data (a.elem, "closest" ) - jQuery.data (b.elem, "closest" );
  14. });
  15. jQuery.each (elems, function () {
  16. if ( this .fn.call ( this .elem, event , this .fn.data) === false )
  17. return (stop = false );
  18. });
  19. return stop;
  20. }
* This source code was highlighted with Source Code Highlighter .


What is she doing:
First of all, it creates a simple regular expression to check that the event of interest is caught, for which there is an instance of the proxied function. We are interested in the click event in the example.

The 8th line of the listing already makes you wonder. For the object that caught the event, the parent element is searched for, for which a proxied function is defined (if any). In the example, <p> catches a click and in the liveHandler it will pop up to the parent <div> object.

In this place we see that, firstly, it is better to fix the events on the children than on their parents, so that the process of ascent is not delayed. In addition, each time a new parent object will be checked for compliance with the selector, in our case ".ololo", where a bottleneck is revealed for complex selectors.

If the click event tracks any other item for which there is no handler, then it will still search for the closing parent object for matching with the specified selector. If, for example, a child element has a nesting level of 100, then the script will trace to the parent object, without having managed to find the necessary element.

Then it simply calls the proxied handler function in the context of the original object.

What can we conclude from what we saw in the code?

First of all, if a live binder for an event is being stacked, then this event will be tracked in any case by the document element. Each time the event will pop up from the element that caught it to the parent element corresponding to the selector. Thus, you should not use live in large dom-structures, with a large number of elements and a high level of nesting.

Since live implies adding new elements corresponding to the specified selector, it would be logical to limit the location of their appearance, for example, if we assume the appearance of new <li> elements in the parent <ul>, it is reasonable to delegate event handling to it.

Those interested can make benchmarks depending on the level of elements on the page, in IE6 these dependences are visible.

I think about the features of live work to tell. I hope someone will be helpful :)

Source: https://habr.com/ru/post/64029/


All Articles