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

May 10
2007 01:41 (Программирование, JavaScript) · English (17,723 views)

Это третья часть учебника по оптимизации 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).
  • Будьте начеку, возможно завтра кто-то опубликует другие подсказки по оптимизации производительности.

Ссылки на другие части

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

Подписаться на комментарии по RSS или TrackBack на 'Оптимизация JavaScript часть 3: Подписка на события'.

1
сказал 10.05.2007 в 8.17

Thanks for really interesting post and for mentioning Low Pro.

2
сказал 10.05.2007 в 12.47

Optimalizace JavaScriptu…

Dmitrij Štefljuk píše velmi zajímavý seriál o optimalizaci JavaScriptu. Doporučuji všem, co se zabývají JavaScriptem.

3
Enrique Melendez
сказал 17.05.2007 в 13.13

Great article,
One thing to take into consideration about applying events (apart from “how”) is “where”.

I mean, for example, it’s far better to attach ONE “mouseover” event to a whole “tbody” and capture the “td” source which fired it than to attach many of “mouseover”s to ALL “td”s…

So, for speed reasons it is crucial “where” do you attach your events…

4
сказал 17.05.2007 в 16.05

Thank you, Enrique
You right, of course. It’s better to use one event observer for parent element instead of separate observers for each child node. Great tip!

5
сказал 24.07.2007 в 6.28

[...] JavaScript optimization Part 3: Attaching events [...]

6
сказал 28.08.2007 в 22.16

[...] Что лучше использовать при подписке на события - фреймвор или делать это вручную? ответ здесь [...]

7
сказал 16.01.2008 в 12.54

очень много дельных советов можно почерпнуть из этой статьи

ps. OpenID не отработал

Fatal error: Call to undefined function curl_init() in /var/www/kpumuk/kpumuk.info/wp-content/plugins/openid/openid-classes.php on line 229
8
сказал 16.01.2008 в 13.07

Спасибо за ссылку и за баг :-) Будем посмотреть

9
Kniaź
сказал 03.02.2008 в 18.56

I’m sure also, that Your code using “manual” events setting would be little faster if you checking event’s attaching methods existence once. On attaching example:

// Attaching events
if (items[0].addEventListener) {
    for (var i = items.length; i--; ) {
        items[i].addEventListener('click', e_onclick, false);
    }
} else if (items[0].attachEvent) {
    for (var i = items.length; i--; ) {
        items[i].attachEvent('onclick', e_onclick);
    }
}

Look also on brilliant idea in one of last slide of Dan Webb’s presentation here:
http://www.danwebb.net/2007/11/22/media-ajax

btw. thx for the link to Low Pro :-)

Оставить отзыв

Вы можете использовать простые теги форматирования HTML (вроде <a>, <ul> and others). Чтобы вставить пример код, используйте <code lang="php">$a = "hello";</code> (поддерживаемые языки: ruby, php, yaml, html, csharp, javascript). Также Вы можете использовать <code>$a = "hello";</code>, синтаксис не будет подсвечен. Если вы не хотите использовать тег <code>, замените символ < на &lt;.

Отправить

 
Copyright © 2005 - 2008, Dmytro Shteflyuk