Pull to refresh

Использование Delegate и Undelegate в jQuery 1.4.2

Reading time5 min
Views8.8K
Original author: Jordan Boesch
jQuery
В комментариях к анонсу версии 1.4.2 было много вопросов по поводу 2-х новых методов, появившихся в новой версии jQuery
Вы наверное слышали, что в jQuery 1.4.2 было добавлено 2 новых метода: .delegate() и .undelegete(). Эти два метода предназначены для тех же целей, что и методы .live() и .die() ссответсвенно, они только используют другой синтаксис.

Для новичков: .live() — метод в jQuery позволяющий вам привязать событие к элементу в документе, так же просто, как и к элементу, который появится в будущем. К примеру, Вы привязываете событие через .live():
   $('img.photo').live('click', function(){
    lightboxify(this);
   });

Затем добавляете несколько фотографий через AJAX:
   // append an image
   $('body').append('<img src="face.jpg" alt="silly face" class="photo"/>');


Событие click() привязано к новому изображению. Необходимости в новой привязки события нет. Удобно?

Не так давно, по поводу метода .live() развернулось несколько обсуждений (прим перев. ссылки на форум jQuery и paulirish.com). Одна из обсуждаемых проблем состояла в том, что метод .live() выдавал ошибку, если вы пытались использовать его в цепочке вызовов (alongside traversals). Например:
// FAILS
$('ul').find('li').next().live('click', function(){});
// FAILS
$('ul').parent().nextAll().live('click', function(){});


И даже при привязке к обычному DOM элементу возникают ошибки:
// FAILS
$(document.body).live('click', function(){});

К сожалению, если вы хотите использовать .live(), он должен быть «на вершине» цепочки:
// WORKS
$('ul li').live('click', function(){})

Так как это может вводить в заблуждение многих людей, использующих траверсинг (traversing) и цепочки вызовов предоставляемые jQuery, это вылилось в обсуждение синтаксиса метода .live(). Почему, выглядя как другие методы, он ведет себя по-другому? Впоследствие это отразилось в кардинальной перерабоки кода, разработчики решили ввести .delegate() и .undelegate() дополняющие .live() and .die().
Вот пример, как обычно используют .live() и .die() и как вы можете теперь использовать .delegate() и .undelegate():
Старый метод:
   // Using .live()
   $("table").each(function(){
    $("td", this).live("hover", function(){
     $(this).toggleClass("hover");
    });
   });
   // Using .die()
   $("table").each(function(){
     $("td", this).die("hover");
   });

Новый метод:
   // Using .delegate()
   $("table").delegate("td", "hover", function(){
    $(this).toggleClass("hover");
   });

   // Using .undelegate()
   $("table").undelegate("td", "hover");


Достоинство метода .delegate() в том, что он позволяет определить контекст. Это гарантирует, что нам не надо будет проходить по всей структуре DOM, чтобы выйти на нужный элемент. Метод .live() проходит весь путь по структуре DOM каждый раз, если только вы не определите контекст как-нибудь так:
$('td', $('table')[0]).live('hover', function(){})

Это выглядит ужасно!
Некторые думают, что delegate() похож на метод bind(). Как вы увидете ниже, синтаксис несколько отличается
   // .bind() way
   $('ul li').bind('click', function(e){
    // Do something with bind
   });
   
   // .delegate() way
   $('ul').delegate('li', 'click', function(e){
    // Do something with delegate
   });

Коротко, разница между .bind() и .delegate() в том, что .bind()только добавляет событие к элементу на странице. .delegate() отслеживает появление новых элементов и добавляет событие к ним, когда они повляются.

Баги метода .delegate()


.delegate() не позволяет, в отличии от .bind() обрабатывать массив событий (object map of events). Пример с .bind():
   // This works wonderfully
   $('ul li').bind({
    click: function(e){
     // Something on click
    },
    mouseover: function(e){
     // Something on mouse over
    }
   });

Ошибка происходит когда пытаемся сделать следующее:
   // FAILS!
   $('ul').delegate('li', {
    click: function(e){
     // Something on click
    },
    mouseover: function(e){
     // Something on mouse over
    }
   });

Я не уверен в причинах этого, но я думаю, не я один над этим размышляю.
Кстати, .bind() не имел этой возможности до версии jQuer 1.4. Если вы решите применить такой же способ в .live() и .delegate(), Роберт Катиц написал маленький кусочек кода, который вы можете включить в свой. Берите его тут.
Я рекомендую использовать патч Роберта Катица, приведеный выше, хотя, конечо, есть и другие методы. К примеру, вы можете соорудить свой собственный доработаный object map:
   var customObjMap = {
    click : function(e){
     // Something on click
    },
    mouseover : function(e){
     // Something on mouse over
    }
   };

   $('ol').delegate('li', 'click mouseover', function(e){
    if($.isFunction(customObjMap[e.type])){
     customObjMap[e.type].call(this, e);
    }
   });

* This source code was highlighted with Source Code Highlighter.


Другой баг обоих методов .delegate() и .live() состоит в том, что когда вы добавляете событие mouseenter и mouseleave к элементу, и затем проверяете его тип (e.type) в callback функции, он неправильно отображается как mouseover и mouseout. .bind() с другой стороны, как и ожидается показывает mouseenter и mouseleave. Вот пример:
$('ol').delegate('li', 'mouseenter', function(e){
  alert(e.type); // outputs mouseover
});
$('ol li').bind('mouseenter', function(e){
  alert(e.type); // outputs mouseenter
});

UPDATE: Этот баг пофиксен и в новых версиях jQuery все будет ОК.

В общем, баги не уменьшают преимуществ, которые методы .delegate() и .undelegte() обеспечивают. На самом деле хорошее дополнение к ядру jQuery.
Tags:
Hubs:
+35
Comments75

Articles