× К оглавлению На главную Об авторе

Дата и время публикации:

Назначение и использование


1. Что это такое

События Объектной Модели Документа (Document Object Model – DOM) позволяют управляемому с их помощью языку программирования JS использовать различные обработчики и перехватчики на узлах элементов внутри дерева DOM документа HTML (или страницы WEB).

События случаются, когда конечный пользователь кликнул что-то на странице, навел мышкой указатель на соответствующий элемент или нажал кнопку на клавиатуре. Так же события иногда исходят от браузера после загрузки страницы, изменения размера окна или во время прокрутки пользователем её содержимого.

В тоже время, события случаются всегда, вопрос лишь в том – назначен ему обработчик или нет. Если ответ – Да, то он вызвается согласно порядку использования в момент перехода по ссылкам и загрузки иной полезной информации случившемуся событию.

Существуют два типа порядка наступления событий путем: всплытия (bubbling) и перехвата(capturing).

Перехват события(capturing event) начинаяется с самого крайнего элемента до внутреннего в том месте дерева DOM, где оно состоялось. Для примера, при нажатии на странице WEB первым будет проверен элемент <HTML>, затем <BODY> и т.д., пока обработка событий не достигнет элемента, которые был источником всей "суматохи".

Всплывающее событие(bubbling event) действует противоположным образом – оно начинается с проверки означенного события с любым присоединеным обработчиком, затем осуществляет "высплытие" по каждому родительскому элементу пока не достигнет элемента <HTML>.

Теперь остается вопрос, как это все реализуется и используется JS.

2. Использование

До ноября 2000 года, обработчик события назначался непосредственно каждому элементу, как показано в листинге 2.1

Листинг 2.1

1 <a href="http://www.w3.org/" id="my-link-id" onclick="alert('the W3C link clicked...')">Go to W3C</a>

Следующим шагом реализацией данного подхода стало назначение события внутри блока <script>, как показано в листинге 2.2

Листинг 2.2

1<script type="text/javascript">
2  document.getElementById("my-link-id").onclick = myFunction;
3    function myFunction() {
4       /* do something here */
5    }
6 </script>
7
8 <a id="my-link-id" href="http://www.w3.org/">My link</a>
9
Примечание Как показано в листинге 2.2, существует две области – в одной из которых находится контекст страницы, а в другой интерпретируемый код, тем самым осуществляется кэширование и управление кодом. В тоже время, когда поддержка JS в браузере отключена, элементы WEB, как и ссылка в строке 8, остаются работоспособными.

Проблемой данного подхода проявилось в том, что обрабатчики событий растеклись по всему коду без центрального управления и отсутствия возможности кэширования в браузере при подключении внешних файлов JS.

Затем, с выходом спецификации обработки событий уровня 2 объектной модели документа (the Document Object Model (DOM) Level 2 Events Specification), стал более детальный и продуманный способ управления событиями на странице WEB, который показан в листинге 2.3

Листинг 2.3

1 document.getElementById("my-link-id").addEventListener("click", myFunction, false);

Первый параметр "my-link-id" метода addEventListener() содержит названия события и оно не использует больше префикс "on". Второй параметр "click" указывает на функцию-обработчик, которая будет вызываться, когда случится событие. Третий параметр, так называемый useCapture, который контролирует выбор между двумя режимами наступления событий путем всплытия(bubbling events) и охватывания(capturing events). Если этот параметр отсутствует, как показано в листинге 2.4, предполагается его значение равно false.

При этом, если требуется использовать "всплытие", то в третьем параметре следует указывать false, а для формирования событий путем "охватывания" элементов – true.

Листинг 2.4

1 document.getElementById("my-link-id").addEventListener("click", myFunction);

Для удаления события из указанного элемента следует воспользоваться методом removeEventListener(), как показано в листинге 2.5, который схож с addEventListener(), но имеет противоположное действие.

Листинг 2.5

1 document.getElementById("my-link-id").addEventListener("click", myFunction);
2 document.getElementById("my-link-id").addEventListener("click", myFunction, true);
3 document.getElementById("my-link-id").addEventListener("click", myFunction, false);
...
7 document.getElementById("my-link-id").removeEventListener("click", listener, false);
8 document.getElementById("my-link-id").removeEventListener("click", listener, true);
9 document.getElementById("my-link-id").removeEventListener("click", listener); 

Таким образом, в строках 1 и 2 одна и таже функция-обработчик myFunction может быть назначена дважды одному и тому же событию, поэтому при удалении необходимо это делать дважды, как показано в строках 7 и 8. При этом, удаление в строки 9 обработчика, назначенного в строке 1, никогда не рассматривается отдельно, т.к. считается, что useCapture равен false.

3. Исключения и рекомендации

Как всегда исключением является MSIE, который не соблюдает спецификацию обработки событий уровня 2 объектной модели документа DOM. Поэтому, несмотря на малочисленное использование, необходимо иметь в виду РАЗРАБОТЧИКУ, что этот динозавр использует свой собственный, как показано в листинге 3.1

Листинг 3.1

1 document.getElementById("my-link-id").attachEvent("onclick", myFunction);
Примечание Как показано в листинге 3.1, attachEvent использует префикс "on" вместе с именем актуального события. При этом, он не включает никакую поддержку для определения фазы захвата. Так же нужно быть готовым, что ни у каждого пользователя данный метод будет работать, впрочем как существуют многие нестыковки между реализациями, что спровождается неустоявшимся поведением. Удаление присоединеного событию обработчика следует производить, как показано в листинге 3.1.1. Листинг 3.1.1
1 document.getElementById("my-link-id").detachEvent("onclick", myFunction);

По-мимо отличий в методах назначения и удаления обрабатчика событию, MSIE использует глобальный объект event, который вызывается всякий раз, когда происходит событие, несмотря на то, что W3C рекомендует к использованию способ передачи события тому, кому оно предназначено в виде отдельного объекта с использованием аргумента evt, как показано в листинге 3.2

Листинг 3.2

1 function eventCheck (evt) {
2   var eventReference = (typeof evt !== "undefined")? evt : event;
3   var eventTarget = (typeof eventReference.target !== "undefined") 
4                                 ? 
5   eventReference.target : eventReference.srcElement;
6 }

Таким образом, из-за нестыковок между WEB браузерами в реализациях моделей обработки событий стали многочисленные попытки со стороны WEB-разработчиков в поисках кросс-решений для охвата всех основных браузеров, как например приведенное в листинге 3.3

Листинг 3.3

1 function addEventOnСlick(myFunction){
2    if (window.addEventListener)
3       window.addEventListener("click", myFunction, false);
4    else if (window.attachEvent)
5       window.attachEvent("onclick",myFunction);
6    else window.onload = myFunction;
7  }

Cогласитесь для каждого типа события "копипастить" функции с одним и тем же набором команд не совсем удобно, что устраняется при помощи библиотеки JQuery или набора функций от Дина Эдварса, об использовании которых пойдет речь дальше.

Назначение обработчика событию производит функция addEvent, пример использования которой приводится в листинге 3.4

Листинг 3.4

     
1 <script type="text/javascript">
2 var link = $$("my-link-id");
3 addEvent(link,"click",clickOnMylink);
4 function clickOnMylink(){
5   console.log("Click on element has id "+this.id );
6 }  
7 </script>

В строке 2 функция addEvent() осуществляет назначение событию click обработчика объекту link для элемента, определенного в строке 8 листинга 2.2 и найденного по идентификатору my-link-id в строке 1. Сам обработчик clickOnMylink() определен в строках 3-4 ниже места использования, что допускается и привествуется в JS.

Как можно заметить, что в addEvent() третий аргумент useCapture пропущен, поэтому существует задача предотвращения "всплытия" для браузеров, поддерживающих объектной модели документу уровня 2.

Вариант решения этой задачи приведен в листинге 3.5 в виде глубоко закопанного пункта "Продукты" навигационного меню.

Листинг 3.5

     
 1 <nav>
 2  <ul>
 3    <li>
 5      <a href="http://www.mycompany.com/" id="stop-default">Главная</a>
 6    </li>
 7    <li>    
 8      <a href="http://www.mycompany.com/products/" id="stop-default">Продукты</a>
 9    </li>
10  </ul>	
11 </nav>

Cчитаем, что всем элементам <nav>, <ul>, <li> и <a> в строках 1-11 назначен обработчик атрибуту onclick, который при нажатии пользователя на ссылку сначала будет вызван для элемента <a>, затем элементам списка <li> и потом немаркированному списку <ul>.

В листинге 3.6 представлен пример того, как оставить в "живых" только те атрибуты элементов, на которые следует реагировать, и исключить "пробуждения" при случайном нажатии пользователя на элементы <ul> и <li>.

Листинг 3.6

     
 1 addEvent(document.getElementById("stop-default"), "click", cancelEventBubbling);
 2 function cancelEventBubbling (evt) {
 3 var eventReference = (typeof evt !== "undefined")? evt : event;
 4   if (eventReference.stopPropagation) {
 5    eventReference.stopPropagation();
 6   }
 7   else { 
 8       eventReference.cancelBubble = true;
 9   }
10 }

Удаление обработчика, например назначенного ранее атрибуту события элементам с идентификатором "stop-default", осуществляется с помощью функции removeEvent(), как показано в листинге 3.7

Листинг 3.7

     
 1 removeEvent(document.getElementById("stop-default"), "click", cancelEventBubbling);

4. Библиография


1) DOM events

2) Основы работы с событиями.Всплытие и перехват

3) Handling_events_with_JavaScript

4) Common Tag Attributes: Events attributes

5) HTML DOM addEventListener() Method

6) MDM.EventTarget.removeEventListener()

7) Add event handler for body.onload by javascript within part

8) attachEvent/detachEvent

9) Prototype.DOM section