Это третья часть учебника по оптимизации 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 |
Вы можете посмотреть тест и получить собственные результаты производительности здесь.
:hover
вместо события onmouseover
).Автор: Штефлюк Дмитрий