Сценарий: у вас есть элементы, и необходимо добавить какую-то функциональность к ним (например, когда пользовательно наводит мышку на элемент, или щелкает по элементам).
Это обычная задача в веб-разработке. И первая вещь, которую знает каждый, — это разный синтаксис подписки на события в 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). - Будьте начеку, возможно завтра кто-то опубликует другие подсказки по оптимизации производительности.