События

Обработчики событий

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

Для реакции на определённые события (действия пользователя) назначаются обработчики событий.

Обработчик события - это функция, которая выполняется в тот момент, когда происходит событие.

Если пользователем инициируются сразу несколько событий, то они попадают в очередь и выполняются друг за другом. Например, простое с точки зрения пользователя нажатие на кнопку клавиатуры вызывает сразу три события в такой последовательности:

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

Назначить обработчик события можно несколькими способами:

  1. Самый простой способ задания обработчика - это использование атрибутов событий прямо в HTML-коде.

    Значением атрибутов событий указывается код JavaScript или название готовой функции. Если указывается просто код JavaScript, то браузер автоматически создаёт анонимную функцию, телом которой является исходный код.

    <p onmouseover="this.style.background = 'red';" onmouseout="this.style.background = 'transparent';">
       Наведите курсор 
    </p>
    

    Наведите курсор

    В данном примере браузер при создании DOM-дерева параграфу <p> присваивает два свойства onmouseover и onmouseout в виде анонимных функций.

    При использовании HTML-атрибутов для задания обработчика события разработчик ограничен стандартом HTML. Например, спецификация CSS тоже предлагает набор событий, но для них нет атрибутов. Если использовать нестандартный атрибут, то код не пройдёт валидацию. Использование же DOM-интерфейса расширяет возможности по созданию динамических страниц, позволяя устанавливать обработчики на любые события.


  2. Ещё один способ установки обработчика события - это прямое использование DOM свойств, то есть сразу записывать обработчик в свойство элемента.

    Имя свойства должно быть в нижнем регистре. В отличие от HTML-атрибутов DOM-свойства регистрозависимы.

    Значением свойства обязательно должна быть функция.

    <p id="exmp1"> Наведите курсор </p>
    <script>
      var par = document.getElementById("exmp1");
      par.onmouseover = function () 
        { this.style.background = 'green'; };
      par.onmouseout = function () 
        { this.style.background = 'transparent'; };
    </script>
    

    Наведите курсор

    Использование HTML-атрибутов или DOM-свойств позволяет устанавливать только по одному обработчику на каждое событие.


  3. Для установки и удаления любого количества обработчиков на одно событие используются методы addEventListener() и removeEventListener():

    addEventListener()

    Метод addEventListener() добавляет обработчик события.

    Синтаксис

    element.addEventListener(имя_события, обработчик[, стадия])
    

    Параметры

    имя_события
    Название события в кавычках.
    обработчик
    Функция-обработчик события (можно указать анонимную функцию или ссылку на готовую).
    стадия
    Необязательный аргумент. Он содержит информацию, на какой стадии сработает обработчик:
    • true - на стадии перехвата.
    • false (используется по умолчанию) - на стадии всплытия.
    <input type="button" value=" Кнопка ">
    <p>При нажатии на кнопку сработают оба обработчика.</p>
    <script>
      var button = document.body.firstElementChild;
      button.addEventListener('click', function() {alert('Обработчик 1');});
      button.addEventListener('click', function() {alert('Обработчик 2');});
    </script>
    

    removeEventListener()

    Метод removeEventListener() используется для удаления обработчика события, назначенного с помощью метода addEventListener(). Обязательно должны быть указаны те значения, которые использовались при назначении обработчика.

    Синтаксис

    element.removeEventListener(имя_события, обработчик[, стадия])
    

    Параметры

    имя_события
    Название события в кавычках.
    обработчик
    Функция-обработчик события. Должна указываться именно ссылка, чтобы браузер понимал, какой обработчик нужно удалить. Соответственно, если обработчик был назначен напрямую (анонимной функцией), то его удалить не удастся.
    стадия>
    Необязательный аргумент. Указывается, если использовался при назначении обработчика.
    <input type="button" value=" Кнопка ">
    <p id="d">Второй обработчик остался, так как он назначен анонимной функцией.</p>
    <script>
    function handler() {alert('Обработчик 1');}
      var button = document.body.firstElementChild;
      /* добавление обработчиков */
      button.addEventListener('click', handler);
      button.addEventListener('click', function() {alert('Обработчик 2');});
      /* удаление обработчиков */
      button.removeEventListener('click', handler);
      button.removeEventListener('click', function() {alert('Обработчик 2');});
    </script>
    

    Отличия IE8-

    При работе с событиями в IE8- есть много отличий. Как правило, они формальны – некое свойство или метод называются по-другому. Начиная с версии 9, также работают и стандартные свойства и методы.

    В IE8- вместо addEventListener/removeEventListener используются свои методы.

    Назначение обработчика осуществляется вызовом attachEvent:

    element.attachEvent("on" + event, handler);
    

    Удаление обработчика – вызовом detachEvent:

    element.detachEvent("on" + event, handler);
    

    Например:

    function handler() { alert( 'Спасибо!' ); }
    button.attachEvent("onclick", handler) // Назначение обработчика
      // ....
    button.detachEvent("onclick", handler) // Удаление обработчика
    

    Как видите, почти то же самое, только событие должно включать префикс on.

    Обработчики, назначенные с attachEvent не получают this!

    Можно объединить способы для IE<9 и современных браузеров, создав свои методы addEvent(elem, type, handler) и removeEvent(elem, type, handler):

    var addEvent, removeEvent;
    
    if (document.addEventListener) { // проверка существования метода
      addEvent = function(elem, type, handler) {
        elem.addEventListener(type, handler, false);
      };
      removeEvent = function(elem, type, handler) {
        elem.removeEventListener(type, handler, false);
      };
    } else {
      addEvent = function(elem, type, handler) {
        elem.attachEvent("on" + type, handler);
      };
      removeEvent = function(elem, type, handler) {
        elem.detachEvent("on" + type, handler);
      };
    }
    
    ...
    // использование:
    addEvent(elem, "click", function() { alert("Привет"); });
    

  4. Помимо выполнения назначенных обработчиков, события могут вызывать действия браузера по умолчанию. Эти действия как бы подразумеваются сами собой в зависимости от элемента страницы. Например:

    Событие Действие браузера
    click Клик по ссылке вызывает переход на новую страницу.
    click Клик по неактивному полю для ввода текста делает его активным.
    contextmenu Клик правой кнопкой мыши открывает контекстное меню.
    dblclick Двойной клик на тексте выделяет его.
    mousedown Нажатие левой кнопки мыши и удержание её над текстом начинает его выделение.
    mousewheel Движение колёсика мыши вызывает прокрутку страницы.
    keydown Нажатия на кнопки клавиатуры внутри текстового поля приводят к набору текста.
    keydown Нажатие на кнопку Enter в активном поле вызывает отправку формы на сервер.

    Обычно, если на конкретное событие устанавливается обработчик, то действия браузера по умолчанию не нужны. Тогда их просто можно отменить. Если обработчик установлен с помощью HTML-атрибута или DOM-свойства, тогда после выполнения всех своих действий обработчику необходимо вернуть значение false.

    Если обработчик события установлен с помощью метода addEventListener(), тогда для отмены действий браузера по умолчанию необходимо использовать метод preventDefault().

Всплытие и перехват событий

Все интернет-страницы состоят из отдельных элементов (тегов). При выводе станицы на экран браузер создаёт DOM-дерево, отражающее структуру документа (связи между родительскими и дочерними элементами).

В DOM-модели каждый дочерний элемент является частью родительского элемента. Следовательно, если на элементе возникает какое-либо событие (например, клик мышью), то оно же возникает и на его родителе, и так далее вверх по DOM-дереву вплоть до корневого элемента (объект document). Если нескольким из таких элементов будут назначены обработчики события, то все они выполнятся.

В примере ниже клик мышью по элементу <mark> вызовет обработчик onclick и на родительском элементе <div>.

<div onclick="alert('Обработчик DIV')">
  <mark onclick="alert('Обработчик MARK')">MARK</mark> внутри DIV
</div>
MARK внутри DIV

Стадии обработки события

Чтобы знать, в какой последовательности будут выполняться обработчики, необходимо иметь представление о том, как браузер обрабатывает событие.

Само исходное событие для всех обработчиков является общим. Его обработка делится на три стадии:

Элемент, на котором возникает событие, называется целевым.

Стадия перехвата

При возникновении события браузер поочерёдно проходит от верхнего элемента DOM-дерева (document) вниз через все промежуточные элементы к целевому. Эта стадия обработки называется стадией перехвата. Обработчики запускаются до того, как событие дойдёт до целевого элемента. Событие как бы перехватывается. Отсюда и следует такое название стадии.

Обработчики события выполняются только в том случае, если для них задано выполнение на стадии перехвата. Для этого необходимо использовать значение true для третьего атрибута метода addEventListener(). Это единственный способ использовать обработчик на стадии перехвата.

Стадия перехвата используется очень редко, но иногда может пригодиться.

Стадия цели

После того, как событие опустилось до целевого элемента, стадия перехвата завершается и выполняются обработчики целевого элемента. Это вторая стадия - стадия цели. Очерёдность выполнения обработчиков на целевом элементе зависит только от очерёдности их назначения. Использование третьего аргумента метода addEventListener() никак не повлияет на порядок их запуска.

Стадия всплытия

Далее начинается последняя стадия. Событие проходит поочерёдно от целевого элемента через цепочку родителей до корневого элемента документа. Событие продвигается вверх по DOM-дереву. Оно будто всплывает. Отсюда и название - стадия всплытия.

На данной стадии выполняются все остальные обработчики. Обработчики, назначенные с помощью атрибутов событий и DOM-свойств всегда выполняются на стадии всплытия. В методе addEventListener() для использования стадии всплытия можно просто опустить третий аргумент.

Прохождение всех стадий продемонстрировано на примере ниже (запускается кликом по жёлтому элементу):

<style>
div { width: 60px; height: 100px;
      position: relative;  background: red;  }
aside { width: 80px; height: 60px;
      background: yellow;
      position: absolute;
      top: 20px; left: 70px;  }
</style>

<div>DIV
  <aside>ASIDE</aside>
</div>
<script>
  var div = document.getElementsByTagName('div')[0];
  var aside = document.getElementsByTagName('aside')[0];
  div.addEventListener('click', function() {alert('DIV - перехват')}, true);
  div.addEventListener('click', function() {alert('DIV - всплытие')});
  aside.addEventListener('click', function() {alert('ASIDE - всплытие')});
  aside.addEventListener('click', function() {alert('ASIDE - перехват')}, true);
</script>

Обработчики элемента <aside> запускаются в порядке назначения.

В этом примере наглядно видно, что событие всегда всплывает по дереву DOM, несмотря на то, что визуально событие не произошло над элементом <div>.

Создание и запуск события

Создание события

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

new Event(имя_события[, свойства])
имя_события
Указывается название события в кавычках.
Можно использовать не только стандартные события, но и придумывать свои собственные (тогда обработчик назначается с помощью метода addEventListener()).
свойства
Необязательный аргумент. Должен быть объектом с двумя свойствами: bubbles - всплывать ли событию (true/false).
  • cancelable - возможность отмены действий браузера по умолчанию (true/false).
Данные свойства будут унаследованы объектом события.
По умолчанию: {bubbles: false, cancelable: false}.

Запуск события

Чтобы сгенерировать событие на элементе, используется метод dispatchEvent().

element.dispatchEvent(объект_события)
объект_события

Ссылка на специально созданный объект события. Данный объект будет передан обработчикам события.

Область применения

Генерация событий в скрипте используется редко. Это вполне логично, ведь обработчики событий должны реагировать на действия, совершённые пользователем или браузером. А для выполнения команд, не связанных с этими действиями, можно использовать простые функции. Однако, есть случаи, когда генерация событий может пригодиться.

Можно выделить два основных случая для генерации событий в скрипте: