Профессиональные курсы: Практика верстки HTML JavaScript для начинающих Программирование на PHP Онлайн-курсы по веб-технологиям

Предотвращение всплывания событий

В DOM & JavaScript события имеют свойство "всплывать". Например, если вы кликните по вложенному элементу, то обработчик клика вызовется сначала для этого элемента, а затем для всех родительских вплоть до document. Иногда "всплывание" мешает и его можно оборвать.

Поставим конкретную задачу

Есть HTML-документ с DIV-ом. Необходимо, чтобы при клике на любую часть документа появлялось окно-предупреждение с текстом "Document". Если же клик пришелся на DIV, то текстом окна должен быть "DIV".

Решение:

1) Создаем HTML-элемент (приведен только актуальный код):

<body>
    <div id="divId">DIV</div>
<body>

2) Запишем функции для добавления обработчиков событий и для обрыва всплывания событий.

function addHandler(object, event, handler, useCapture) {
    if (object.addEventListener) {
        object.addEventListener(event, handler, useCapture ? useCapture : false);
    } else if (object.attachEvent) {
        object.attachEvent('on' + event, handler);
    } else alert("Add handler is not supported");
}
function cancelBubbling(evt) {
    evt = evt || window.event;
    evt.cancelBubble = true;
}

3) Добавим обработчики, в один из которых поставим функцию cancelBubbling:

addHandler(
    document,
    "click",
    function() {alert("Document");}
);
addHandler(
    document.getElementById("divId"),
    "click",
    function(event) {
        alert("DIV");
        // Запрещаем всплывание событий после DIV-а
        cancelBubbling(event);
    }
);

4) Готово!

Функция cancelBubbling работает со свойством cancelBubble объекта event, которое и запрещает всплывание событий.

Александр Бурцев 2 мая 2007

© Все права на данную статью принадлежат порталу fastcoder.org. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.

Комментарии

aulizko 6 сентября 2008, 16:03 #
С вашего разрешения, дополню по двум пунктам:
1) Функция добавления обработчика события может быть записана в более комплексном виде (для совместимости со старыми и редкими браузерами):
function addEventHandler(node, type, f) {
    if (node.AddEventListener) {
        node.addEventListener (type, f, false);
    } else if (node.attachEvent) {
        node.attachEvent('on' + type, f);
    } else {
        node['on' + type] = f;
    }
}

Код в последнем условии выполняется и в старых браузерах, и в новых, так что, на самом деле, можно было бы всю функцию заменить на строку кода вида:
function addEventListener(node, type, f) {
    node['on' + type] = f;
}

2) cancelBubbling - это фишка Microsoft, для работы с W3C моделью событий нужно использовать метод stopPropagation() у объекта event. Так что вашу функцию cancelBubbling я бы записал вот так:
function cancelBubbling (e) {
    e = e || event;
    e.cancelBubble = true;
    if (e.stopPropagation) {
        e.stopPropagation();
    }
}

Ну а ваш метод - остановка всплытия в non-IE браузерах путем перехвата распространения события в trickling фазе, на мой взгляд, несколько не верно и не очевидно.
 
Bur 6 сентября 2008, 16:11 #
1) Дополнение к функции addHandler действительно будет работать, спасибо. А вот заменять всю функцию таким образом нельзя, т.к. текщуий addHandler добавляет еще один обработчик в стек, а node['on' + type] - полностью стек перезапишет и убьёт имеющиеся обработчики, что неприемлимо.

2) Тут вы не совсем правы. Событие может всплывать и просачиваться, причем последнее не реализовано в ИЕ. Но всплывание и его блокирование работает кроссбраузерно и функцию cancelBubbling лучше оставить в исходном виде.
 
aulizko 6 сентября 2008, 16:27 #
1) Согласен. Без множественных обработчиков событий не реализовать паттерны "Наблюдатель" и "Посредник".

2) А вот тут буду спорить :)
Лично я всегда третий параметр функции addEventListener устанавливаю в false. И сейчас объясню, почему.
Этот третий параметр указывает, на какой фазе распространения события передавать его в функцию обработчик - на стадии просачивания (true) или на стадии всплытия (false).
Таким образом, я в своем коде полностью отказываюсь от trickling-фазы (просачивания), и вот по каким причинам:
а) модель Bubbling намного проще для понимания (да, ведь приходят же новички, и их тоже надо учить); модель bubbling намного логичнее. Этот пункт - полное имхо.
б) Модель bubbling дает серьезные бонусы в экономии памяти при работе с большим количеством элементов (можно приаттачить обработчик функции только родительскому контейнеру, вместо того, чтобы приаттачивать 100+ обработчиков каждому из элементов)
в) в IE реализована только модель bubbling, и, насколько можно судить (по бетам в IE 8), Microsoft не будет добавлять просачивание событий. А это значит, что для того, чтобы код приложения был кроссбраузерным, проще везде использовать модель bubbling (несколько нелогично использовать две модели в одном приложении - это же в два раза больше кода).

Так что я всегда ловлю события на стадии всплытия, и останавливаю их распространения в non-IE браузерах с помощью stopPropagation().
 
 
Rambler's Top100 Flede HTML valid CSS valid