Ускоряем обработку событий
Давайте рассмотрим, как можно использовать методы «ненавязчивого» JavaScript для максимального ускорения обработки событий в браузере. Мы можем уменьшить число приемников событий, которые назначены документу, путем определения одного приемника для контейнера и проверки в обработчике, из какого дочернего элемента всплыло это событие.
Предположим, что у нас основная навигация по сайту включает шесть ссылок сверху, четырем из которых требуются обработчики событий, чтобы поменять у них атрибут href. У этих четырех ссылок атрибут class выставлен в bundle.
Скорее всего, ситуация будет выглядеть следующим образом.
var MenuNavigation = {
init: function() {
var navigation = document.getElementById('mainNav');
var links = navigation.getElementsByTagName('a');
for ( var i = 0, j = links.length; i if ( /bundle/i.test(links[i].className) ) {
links[i].onclick = this.onclick;
}
}
},
onclick: function() {
this.href = this.href + '?name=value';
return true;
}
}
В этом фрагменте довольно много лишнего. Во-первых, метод getElementsByTagName просматривает каждый дочерний DOM-узел в элементе mainNav, чтобы найти все ссылки. Затем мы еще раз пробегаем по всему найденному массиву, чтобы проверить имя класса каждой ссылки. Это пустая трата процессорного времени на каждом этапе. И это замедление загрузки страницы на уровне JavaScript-логики.
Немного усложним
Можно прикрепить один-единственный обработчик событий к элементу mainNav, чтобы затем отслеживать все клики на ссылки внутри него:
var MenuNavigation = {
init: function() {
var navigation = document.getElementById('mainNav');
navigation.onclick = this.onclick;
},
onclick: function(e) {
if ( /bundle/i.test(e.target.className) ) {
e.target.href = e.target.href + '?name=value';
}
return true;
}
}
Простота и элегантность данного подхода должны быть очевидны, но у него есть и некоторое количество преимуществ в плане производительности:
Чем меньше приемников событий прикреплено к документу, тем лучше. Они все загружаются в память и в чрезвычайных случаях могут сильно замедлить работу браузеров. Также это увеличивает число замыканий, что чревато утечками памяти. Подробнее рассказывается далее в этой главе.
Загружается меньше кода на странице. Одной из главных проблем для сложных веб-приложений является задержка при загрузке JavaScript для исполнения и визуализации документа. Два цикла из первого примера отсутствуют во втором.
«Исполнение по требованию». Второй пример выполняет немного больше действий, когда вызывается конечный обработчик событий, но это лучше, чем выполнять все действия при загрузке страницы, когда мы даже не знаем, будет ли запущен каждый конкретный обработчик событий. Ссылок на странице может быть сотня, а пользователь нажмет только одну или две из них.
Боремся с Internet Explorer
Есть одна небольшая проблема при использовании изложенного выше кода. Определение целевого элемента у события, на самом деле, не является просто вызовом e.target. В Internet Explorer необходимо использовать e.srcElement. Самым простым решением для устранения этой проблемы является небольшая функция getEventTarget. Ниже представлена наиболее актуальная версия.
function getEventTarget(e) {
var e = e || window.event;
var target = e.target || e.srcElement;
if (target.nodeType == 3) { // боремся с Safari
target = target.parentNode;
}
return target;
}
Переопределение событий в настоящее время является самой распространенной практикой, если речь заходит о большом числе обработчиков событий (например, о карте с сотнями точек, к которым назначены обработчики событий-кликов). Лучше всего для этого по умолчанию использовать простой, интуитивно понятный и хорошо оптимизированный метод для применения в качестве шаблона в программировании на стороне клиента, и он не должен требовать сотен строчек JavaScript-библиотек для своей работы.