spam poison

Оптимизация JavaScript часть 3: Подписка на события


JavaScript и события


Сценарий: у вас есть элементы, и необходимо добавить какую-то функциональность к ним (например, когда пользовательно наводит мышку на элемент, или щелкает по элементам).

Это обычная задача в веб-разработке. И первая вещь, которую знает каждый, — это разный синтаксис подписки на события в Internet Explorer и Firefox: первый использует element.attachEvent, второй — element.addEventListeners. Поэтому Вам нужно добавлять проверки, какой браузер используется в каждом конкретном случае. В наиболее популярной библиотеке Prototype это уже стандартизировано, и Вы всегда можете использовать Event.observe. Давайте немного потестируем.

Я начну с подписки на события через определение браузера вручную:

// Attaching events
for (var i = items.length; i--; ) {
    if (items[i].addEventListener) {

        items[i].addEventListener('click', e_onclick, false);

    } else if (items[i].attachEvent) {

        items[i].attachEvent('onclick', e_onclick);
    }

}
// Detaching events
for (var i = items.length; i--; ) {
    if (items[i].removeEventListener) {

        items[i].removeEventListener('click', e_onclick, false);

    } else if (items[i].detachEvent) {

        items[i].detachEvent('onclick', e_onclick);
    }

}

Этот подход показал лучшее время: 188 и 203 ms в Internet Explorer 6 и 7, 125 и 141 ms в Firefox 1.5 and 2.0, и 63 ms в Opera 9, но есть одна проблема — утечки памяти: Internet Explorer обычно забывает почистить память, используемую обработчиками событий, когда вы переходите на другую страницу. Поэтому все JavaScript-фреймворки, реализующие функции подписки/отписывания от событий, предоставляют возможность автоматического удаления обработчиков при переходе на другую страницу. Давайте потестируем их.

Код для библиотеки Prototype:

// Attaching events
for (var i = items.length; i--; ) {
    Event.observe(items[i], 'click', e_onclick, false);

}
// Detaching events
for (var i = items.length; i--; ) {
    Event.stopObserving(items[i], 'click', e_onclick, false);

}

Это очень медленно, 6453 ms в Internet Explorer 6, и похоже на то, что все еще есть какие-то утечки памяти (становится все медленнее и медленнее со временем), и 365 – 653 ms в других браузерах.

Существует множество проблем и решений, связанных с подпиской на события (взгляните только на Advanced event registration models). Не так давно даже прошло соревнование PPK's addEvent() Recoding Contest (но не вздумайте использовать решение победителя в своих приложениях, оно крайне неэффективно и приводит к утечкам памяти).

Вместо него я решил протестировать невероятное и дерзкое решение от Dean Edwards, которое даже не использует методы addeventListener/attachEvent, но является полностью кросс-браузерным!

Использованный код:

// Attaching events
for (var i = items.length; i--; ) {

    addEvent(items[i], 'click', e_onclick);
}
// Detaching events

for (var i = items.length; i--; ) {
    removeEvent(items[i], 'click', e_onclick);

}

Результаты настолько же впечатляющие, как и при подписке на события вручную, этот подход невероятно быстр для использования в реальных приложениях. Безусловно, вы скажете: «Стоп, о чем ты говоришь? Я не хочу использовать дополнительные методы, ведь я уже использую Prototype в своем коде!» Остыньте, вам не нужно этого делать, просто скачайте библиотеку Low Pro (текущая версия — 0.4, не забывайте обновляться). Разработчики Ruby on Rails могут воспользоваться плагином UJS Rails Plugin в своих приложениях для улучшения производительности — он включает и оптимизации Low Pro.

No Method IE 6 IE 7 FF 1.5 FF 2.0 Opera 9
1 manually 203 188 125 141 63
2 prototype.js 6453 653 547 469 365
3 addEvent 783 344 94 141 62

Тест производительности: Подписка на события

Вы можете посмотреть тест и получить собственные результаты производительности здесь.

Выводы

  • Всегда используйте Low Pro с Prototype.js (конечно, если ваше приложение содержит продвинутую логику на стороне клиента).
  • Подписывайтесь на события вручную, если нужно выжать максимальную скорость из вашего приложения (например, когда вы манипулируете сотнями элементов). Но не забывайте об утечках памяти!
  • Избегайте подписки на события везде, где это возможно (например, используйте CSS селектор :hover вместо события onmouseover).
  • Будьте начеку, возможно завтра кто-то опубликует другие подсказки по оптимизации производительности.


Article information
Wroted: Thu, 10 May 2007 17:45:00 EEST
Autors: Дмитрий Штефлюк
Added by: AlexParamonov at Thu, 27 Nov 2008 17:46:03 EET
Translation information
Added by: AlexParamonov at Thu, 27 Nov 2008 17:47:32 EET
Modified by: AlexParamonov at Wed, 03 Dec 2008 16:50:51 EET
Copyright © 2008-2010 Alexander Paramonov
Valid XHTML 1.0 Transitional