Ввиду недавнего обсуждения скорости работы jQuery.live и благоразумности писать свое собственное делегирование обработчиков событий, решил по полочкам разобрать работу jQuery.live. Т.е. целью данного топика поставлена задача выявления всех особенностей при использовании live-биндера и анализ кода. Без сравнительных характеристик, без приведения оптимальных методов делегирования.
Принцип работы live основан на делегировании обработчиков событий.
Делегирование — паттерн, в основе которого лежат 2 принципа javascript: всплытие событий (event bubbling stage) и возможность определения элемента, отследившего событие.
Тот факт, что делегирование отслеживает исключительно стадию всплытия события, объясняет невозможность обвешивания live-биндеров на события blur, focus, mouseenter, mouseleave, change и submit: все эти события не имеют стадий захвата и всплытия.
Рассмотрим простейший пример:
<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.
Посмотрим, что происходит при обвешивании события на элемент div
return this; } * This source code was highlighted with Source Code Highlighter.
Так выглядит сама функция live.
Сначала создается proxy-функция для последующего вызова в контексте оригинального объекта
proxy = proxy || function(){ return fn.apply(this, arguments); }; * This source code was highlighted with Source Code Highlighter.
Отметим, что this здесь в контексте оригинального объекта, т.е. указывает на $('.ololo')
Возвращаемся к live. Проксирующей функции назначается уникальный guid.
Функция liveConvert специальным образом формирует имя события в пространстве имен live
function liveConvert(type, selector){ return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join("."); } * This source code was highlighted with Source Code Highlighter.
В примере функция вернет строку: live.click.`ololo
В этот момент происходит делегирование обработчика события (в примере click) объекту $(document). Т.е. теперь просто-напросто все события click будет ловить объект document, а не оригинальный элемент.
bind в данном случае работает в контексте $(document) и добавляет ему обработчик событий.
bind: function( type, data, fn ) { return type == "unload" ? this.one(type, data, fn) : this.each(function(){ jQuery.event.add( this, type, fn || data, fn && data ); }); }, * This source code was highlighted with Source Code Highlighter.
Весь метод jQuery.event.add разбирать не будем, остановимся только на интересных нам местах.
jQuery.event = { add: function(elem, types, handler, data) { ... * This source code was highlighted with Source Code Highlighter.
Параметрами в этот метод подаются: $(document), «live.click.'ololo», ".ololo", и исходная проксированная функция.
jQuery.each(types.split(/\s+/), function(index, type) { var namespaces = type.split("."); type = namespaces.shift(); ... if ( jQuery.event.specialAll[type] ) jQuery.event.specialAll[type].setup.call(elem, data, namespaces); * This source code was highlighted with Source Code Highlighter.
Здесь выделяется namespace live и проверяется, нету ли значения по данному ключу в хеше jQuery.event.specialAll. Для live есть:
specialAll: { live: { setup: function( selector, namespaces ){ jQuery.event.add( this, namespaces[0], liveHandler ); }, ... * This source code was highlighted with Source Code Highlighter.
Здесь нам интересна только функция liveHandler, которая и объясняет все особенности метода jQuery.live. Приведу ее код полностью:
function liveHandler( event ){ var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"), stop = true, elems = [];
jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){ if ( check.test(fn.type) ) { var elem = jQuery(event.target).closest(fn.data)[0]; if ( elem ) elems.push({ elem: elem, fn: fn }); } });
return stop; } * This source code was highlighted with Source Code Highlighter.
Что она делает: Прежде всего, создает простое регулярное выражение для проверки того, что поймали интересующее нас событие, для которого есть экземпляр проксированной функции. Нас в примере интересует событие click.
8я строчка листинга уже заставляет задуматься. Для объекта, который поймал событие, происходит поиск родительского элемента, для которого определена проксированная функция (если таковая имеется). В примере <p> ловит click и в liveHandler будет происходить всплытие до родительского объекта <div>.
В этом месте мы и видим, что во-первых, провешивать события лучше на детей чем на их родителей, чтобы процесс всплытия не затягивался. Кроме того, каждый раз новый родительский объект будет проверяться на соответствие селектору, в нашем случае ".ololo", где и выявляется узкое место для сложных селекторов.
Если событие click отслеживает какой-либо другой элемент, для которого нету обработчика, то для него всеравно будет выполняться поиск замыкающего родительского объекта на соответствие заданному селектору. Если например дочерний элемент имеет уровень вложенности 100, то скрипт произведет трассировку до родительского объекта, так и не сумев найти нужный элемент.
Далее просто вызывается проксированная функция-обработчик в контексте оригинального объекта.
Какой можно сделать вывод из того, что мы увидели в коде?
Прежде всего, если провешивается live-биндер для события, то это событие в любом случае будет отслеживаться элементом document. Каждый раз событие будет всплывать от поймавшего его элемента к родительскому, соответствующему селектору. Таким образом, не следует использовать live в больших dom-структурах, с большим количеством элементов и высоким уровнем вложенности.
Поскольку live подразумевает добавление новых элементов, соответствующих указанному селектору, то логично будет ограничить локацию их появления, скажем, если мы предполагаем появление новых <li>-элементов в родительском <ul>, то разумно делегировать обработку события ему.
Желающие могут сделать бенчмарки в зависимости от уровня элементов на странице, в ИЕ6 эти зависимости видны на глаз.
Думаю, об особенностях работы live рассказать получилось. Надеюсь, кому-то будет полезно: )
.
.
.
.
.
.
.
Детские электромобили и квадроциклы, детские автомобили .... Детский электромобиль на радио управлении GO Buggy GO!, Peg-Perego (Италия) - предназначен для ...
Мы предлагаем свои услуги по разработке компьютерных программ, ... Успешность разработки программного обеспечения при программировании на заказ напрямую. Разработка программ – процесс сложный, требующий высокой квалификации специалистов разработчиков и большого опыта решения программных задач.
Подбор домашнего персонала и поиск работы: няня гувернантка, няня домработница, работа с проживанием, частичная занятость, няня .