«Исполнение по требованию». Второй пример выполняет немного больше действий, когда вызывается конечный обработчик событий, но это лучше, чем выполнять все действия при загрузке страницы, когда мы даже не знаем, будет ли запущен каждый конкретный обработчик событий. Ссылок на странице может быть сотня, а пользователь нажмет только одну или две из них.
Боремся с 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-библиотек для своей работы.
Пойдем дальше
А что, если нам нужно добавить такой обработчик на все ссылки (или почти на все)? Правильно: тогда для контейнера всех этих ссылок стоит выбрать document.body. Ниже приведен пример кода, который позволяет так сделать.
var MenuNavigation = {
init: function() {
document.body.onclick = function(e) {
var target = getEventTarget(e);
if ( target && /bundle/i.test(target.className) ) {
target.href += '?name=value';
}
return true;
};
}
var getEventTarget = function(e) {
var e = e || window.event;
var target = e.target || e.srcElement;
// боремся с Safari и вложенностью
while ( !target.href || target.nodeType == 3 ) {
target = target.parentNode;
}();
return target;
}
}
window.onload = MenuNavigation.init;
Если мы собираемся обрабатывать все ссылки, то нужно учесть, что в них могут быть вложены и картинки, и другие теги, поэтому добавлено рекурсивное «всплытие» ссылки: проверяется родитель объекта, на котором сработало событие, и если у него не определен атрибут href, то перебор продолжается, иначе возвращаем искомый объект. Вложение ссылок друг в друга запрещено стандартами, так что если мы сами же проектируем HTML-код, то бояться нечего.
Обработка событий в браузерах
node.onclick = function(){
}
if (node.addEventListener)
node.addEventListener('click', function(e){}, false);
else
node.attachEvent('onclick', function(){});
if (node.attachEvent)
node.attachEvent('onclick', function(){});
else
node.addEventListener('click', function(e){}, false);
var addEvent = node.attachEvent || node.addEventListener;
addEvent(/*@cc_on 'on'+@*/'click', function(){}, false);
node[/*@cc_on !@*/0 ? 'attachEvent' : 'addEventListener']
(/*@cc_on 'on'+@*/'click', function(){}, false);
Работаем с событиями
Давайте рассмотрим, что мы можем извлечь из события после перехвата его с помощью соответствующего обработчика:
node[/*@cc_on !@*/0 ? 'attachEvent' : 'addEventListener']
(/*@cc_on 'on'+@*/'click', function(e){
var target = e.target || e.srcElement
// или
if (!e.target) {
e.target = e.srcElement
}
// или, если нам надо всего один раз
(e.target || e.srcElement).tagName
// true везде кроме IE, в котором this === window
this == node;
// отменяем всплытие события
if (e.stopPropagation)
e.stopPropagation()
else
e.cancelBubble
// или просто используем вариант, который
// для совместимости работает во всех браузерах.
e.cancelBubble = true
// убираем действие по умолчанию (в данном случае клик)
if (e.preventDefault)
e.preventDefault()
else
e.returnValue = false
// при attachEvent (как здесь) работает только в IE;
// при назначении напрямую (node.onclick) — везде
return false;
}, false):
7.3. Применение «ненавязчивого» JavaScript
В предыдущих разделах были представлены некоторые теоретические аспекты построения клиентской логики, ориентированной на максимальное быстродействие и адекватную ему замену в проблемных случаях. Ниже приведены практические решения по облегчению наиболее характерных сторон клиентского взаимодействия любого сайта: это счетчики посещений и размещение рекламы. Ведь они встречаются сейчас практически на любом веб-проекте.