Делегирование событий в javascript

Движение мыши

Каждый раз, когда перемещается курсов мыши, срабатывает событие «mousemove» из набора JavaScript mouse events. Оно может быть использовано для отслеживания положения мыши. Это применяется при реализации возможности перетаскивания элементов мышью.

В следующем примере программа выводит на экран панель и устанавливает обработчики событий таким образом, что при перетаскивании эта панель становится уже или шире:

<p>Потяните за край панели, чтобы изменить ее ширину:</p>
<div style="background: orange; width: 60px; height: 20px">
</div>
<script>
  var lastX; // Отслеживает последнюю позицию X мыши
  var rect = document.querySelector("div");
  rect.addEventListener("mousedown", function(event) {
    if (event.which == 1) {
      lastX = event.pageX;
      addEventListener("mousemove", moved);
      event.preventDefault(); // Предотвращает выделение
    }
  });

  function buttonPressed(event) {
    if (event.buttons == null)
      return event.which != 0;
    else
      return event.buttons != 0;
  }
  function moved(event) {
    if (!buttonPressed(event)) {
      removeEventListener("mousemove", moved);
    } else {
      var dist = event.pageX - lastX;
      var newWidth = Math.max(10, rect.offsetWidth + dist);
      rect.style.width = newWidth + "px";
      lastX = event.pageX;
    }
  }
</script>

Обратите внимание, что обработчик «mousemove» зарегистрирован для всего окна. Даже если во время изменения размеров мышь выходит за пределы панели, мы все равно обновляем ширину панели и прекращаем JavaScript touch events, когда клавиша мыши была отпущена

Мы должны прекратить изменение размера панели, когда пользователь отпускает клавишу мыши. К сожалению, не все браузеры устанавливают для событий «mousemove» свойство which. Существует стандартное свойство buttons, которое предоставляет аналогичную информацию, но оно также поддерживается не во всех браузерах. К счастью, все основные браузеры поддерживают что-то одно: либо buttons, либо which. Функция buttonPressed в приведенном выше примере сначала пытается использовать свойство buttons, и, если оно не доступно, переходит к which.

Когда курсор мыши наводится или покидает узел, запускаются события «mouseover» или «mouseout«. Они могут использоваться для создания эффектов при наведении курсора мыши, вывода какой-нибудь подписи или изменения стиля элемента.

Чтобы создать такой эффект, недостаточно просто начать его отображение при возникновении события «mouseover» и завершить после события «mouseout«. Когда мышь перемещается от узла к одному из его дочерних элементов, для родительского узла происходит событие «mouseout«. Хотя указатель мыши не покинул диапазон распространения узла.

Что еще хуже, эти JavaScript event распространяются так же, как и другие события. Когда мышь покидает один из дочерних узлов, для которого зарегистрирован обработчик, возникнет событие «mouseout«.

Чтобы обойти эту проблему, можно использовать свойство объекта события relatedTarget. В случае возникновения события «mouseover» оно указывает, на какой элемент был наведен курсор мыши до этого. А в случае возникновения «mouseout» — к какому элементу перемещается указатель. Мы будем изменять эффект наведения мыши только, когда relatedTarget находится вне нашего целевого узла.

В этом случае мы изменяем поведение, потому что курсор мыши был наведен на узел из-за его пределов (или наоборот):

<p>Наведите курсор мыши на этот <strong>абзац</strong>.</p>
<script>
  var para = document.querySelector("p");
  function isInside(node, target) {
    for (; node != null; node = node.parentNode)
      if (node == target) return true;
  }
  para.addEventListener("mouseover", function(event) {
    if (!isInside(event.relatedTarget, para))
      para.style.color = "red";
  });
  para.addEventListener("mouseout", function(event) {
    if (!isInside(event.relatedTarget, para))
      para.style.color = "";
  });
</script>

Функция isInside отслеживает родительские связи заданного узла или пока не будет достигнута верхняя часть документа (когда node равен нулю). Либо не будет найден родительский элемент, который нам нужен.

Эффект наведения гораздо проще создать с помощью псевдоселектора CSS :hover, как показано в следующем примере. Но когда эффект наведения предполагает что-то более сложное, чем просто изменение стиля целевого узла, тогда нужно использовать прием с использованием событий «mouseover» и «mouseout» (JavaScript mouse events):

<style>
  p:hover { color: red }
</style>
<p>Наведите курсор мыши на этот <strong>абзац</strong>.</p>

readyState

В заключение
занятия отметим свойство

document.readyState

которое в момент
загрузки HTML-документа принимает
следующие значения:

  • «loading»
    – документ в процессе загрузки;

  • «interactive»
    – документ был полностью прочитан (парсинг документа завершен);

  • «complete»
    – документ был полностью прочитан и все ресурсы (изображения, стили и т.п.)
    тоже загружены.

В ряде случаев
это свойство бывает весьма полезно. Например, мы вызываем функцию, но не
уверены, что DOM-дерево
полностью построено. Поэтому, делаем такую проверку:

removeImage();
function removeImage() {
     if(document.readyState == "loading") {
          console.log("документ грузится, вешаем обработчик");
          document.addEventListener("DOMContentLoaded", removeImage);
     }
     else {
          console.log("удаляем изображение");
          document.body.remove(image);
     }
}

По аналогии
могут быть обработаны и остальные свойства.

Для полноты картины
пару слов о событии readystatechange, которое появилось до событий

DOMContentLoaded, load, unload, beforeunload

и в старых версиях
JavaScript процесс
загрузки документа контролировался через него. Например, так:

document.addEventListener('readystatechange', function() {
         console.log(document.readyState);
});

Теперь при
обновлении страницы мы можем увидеть изменение состояний свойства document.readyState в процессе
загрузки. Однако такой механизм отслеживания ушел в прошлое и сейчас уже нет смысла
о нем подробно говорить.

Итак, на этом
занятии мы с вами рассмотрели события

DOMContentLoaded,
load, unload, beforeunload

и поговорили о свойстве

document.readyState

которое
дополняет работу с этими событиями.

Видео по теме

JavaScipt (DOM) #1: объектная модель документа DOM и BOM

JavaScipt (DOM) #2: навигация по DOM — parentNode, nextSibling, previousSibling, chidNodes

JavaScipt (DOM) #3: методы поиска элементов в DOM: querySelector, querySelectorAll, getElementById

JavaScipt (DOM) #4: свойства DOM-узлов: nodeName, innerHTML, outerHTML, data, textContent, hidden

JavaScipt (DOM) #5: работа с нестандартными свойствами DOM-элементов: getAttribute, setAttribute, dataset

JavaScipt (DOM) #6: создание и добавление элементов DOM createElement, append, remove, insertAdjacentHTML

JavaScipt (DOM) #7: управление стилями — className, style, classList, getComputedStyle

JavaScipt (DOM) #8: метрики — clientWidth, scrollTop, scrollHeight, offsetLeft, offsetTop, clientLeft

JavaScipt (DOM) #9: HTML-документ: размеры (clientWidth, innerWidth), положение (pageYOffset, scrollBy)

JavaScipt (DOM) #10: расположение элементов — fixed, absolute, getBoundingClientRect, elementFromPoint

JavaScipt (DOM) #11: обработчики событий: onclick, addEventListener, removeEventListener, event

JavaScipt (DOM) #12: погружение и всплытие событий: stopPropagation, stopImmediatePropagation, eventPhase

JavaScipt (DOM) #13: делегирование событий, отмена действия браузера по умолчанию — preventDefault

JavaScipt (DOM) #14: события мыши mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter

JavaScipt (DOM) #15: события клавиатуры keydown, keyup, событие скроллинга scroll

JavaScipt (DOM) #16: навигация и обработка элементов форм (form) — document.forms, form.elements

JavaScipt (DOM) #17: фокусировка — focus, blur, focusin, focusout, tabindex, activeElement

JavaScipt (DOM) #18: события change, input, cut, copy, paste, submit элементов input и select

JavaScipt (DOM) #19: события при загрузке — DOMContentLoaded, load, unload, beforeunload, readyState

JavaScipt (DOM) #20: события load, error; атрибуты async, defer тега script

JavaScipt (DOM) #21: пример предзагрузки изображений с помощью javascript

JavaScipt (DOM) #22: пример создания начала игры арканоид

Погружение

Существует ещё одна фаза из жизненного цикла события – «погружение» (иногда её называют «перехват»). Она очень редко используется в реальном коде, однако тоже может быть полезной.

Стандарт DOM Events описывает 3 фазы прохода события:

  1. Фаза погружения (capturing phase) – событие сначала идёт сверху вниз.
  2. Фаза цели (target phase) – событие достигло целевого(исходного) элемента.
  3. Фаза всплытия (bubbling stage) – событие начинает всплывать.

Картинка из спецификации демонстрирует, как это работает при клике по ячейке , расположенной внутри таблицы:

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

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

Обработчики, добавленные через -свойство или через HTML-атрибуты, или через с двумя аргументами, ничего не знают о фазе погружения, а работают только на 2-ой и 3-ей фазах.

Чтобы поймать событие на стадии погружения, нужно использовать третий аргумент вот так:

Существуют два варианта значений опции :

  • Если аргумент (по умолчанию), то событие будет поймано при всплытии.
  • Если аргумент , то событие будет перехвачено при погружении.

Обратите внимание, что хоть и формально существует 3 фазы, 2-ую фазу («фазу цели»: событие достигло элемента) нельзя обработать отдельно, при её достижении вызываются все обработчики: и на всплытие, и на погружение. Давайте посмотрим и всплытие и погружение в действии:

Давайте посмотрим и всплытие и погружение в действии:

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

Если вы кликните по , то последовательность следующая:

  1. → → → (фаза погружения, первый обработчик)
  2. (фаза цели, срабатывают обработчики, установленные и на погружение и на всплытие, так что выведется два раза)
  3. → → → (фаза всплытия, второй обработчик)

Существует свойство , содержащее номер фазы, на которой событие было поймано. Но оно используется редко, мы обычно и так знаем об этом в обработчике.

Чтобы убрать обработчик , нужна та же фаза

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

На каждой фазе разные обработчики на одном элементе срабатывают в порядке назначения

Если у нас несколько обработчиков одного события, назначенных на один элемент, в рамках одной фазы, то их порядок срабатывания – тот же, в котором они установлены:

event.defaultPrevented

Свойство установлено в , если действие по умолчанию было предотвращено, и , если нет.

Рассмотрим практическое применение этого свойства для улучшения архитектуры.

Помните, в главе Всплытие и погружение мы говорили о и упоминали, что останавливать «всплытие» – плохо?

Иногда вместо этого мы можем использовать , чтобы просигналить другим обработчикам, что событие обработано.

Давайте посмотрим практический пример.

По умолчанию браузер при событии (клик правой кнопкой мыши) показывает контекстное меню со стандартными опциями. Мы можем отменить событие по умолчанию и показать своё меню, как здесь:

Теперь в дополнение к этому контекстному меню реализуем контекстное меню для всего документа.

При правом клике должно показываться ближайшее контекстное меню.

Проблема заключается в том, что когда мы кликаем по элементу , то мы получаем два меню: контекстное меню для кнопки и (событие всплывает вверх) контекстное меню для документа.

Как это поправить? Одно из решений – это подумать: «Когда мы обрабатываем правый клик в обработчике на кнопке, остановим всплытие», и вызвать :

Теперь контекстное меню для кнопки работает как задумано. Но цена слишком высока. Мы навсегда запретили доступ к информации о правых кликах для любого внешнего кода, включая счётчики, которые могли бы собирать статистику, и т.п. Это слегка неразумно.

Альтернативным решением было бы проверить в обработчике , было ли отменено действие по умолчанию? Если да, тогда событие было обработано, и нам не нужно на него реагировать.

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

event.stopPropagation() и event.preventDefault()

Как мы можем видеть, и (также известный как ) – это две разные функции. Они никак не связаны друг с другом.

Архитектура вложенных контекстных меню

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

Объект будет перехватывать любой клик правой кнопкой мыши, просматривать сохранённые обработчики и запускать соответствующий.

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

Parameter Values

Parameter Description
event Required. A String that specifies the name of the event.Note: Do
not use the «on» prefix. For example, use «click» instead of «onclick».For a list of all HTML DOM events, look at our complete HTML DOM Event Object Reference.
function Required. Specifies the function to run when the event occurs. When
the event occurs, an event object is passed to the function as
the first parameter. The type of the event object depends on the specified event.
For example, the «click» event belongs to the MouseEvent object.
useCapture Optional. A Boolean value that specifies whether the event should be
executed in the capturing or in the bubbling phase. Possible values:

  • true — The event handler is executed in the capturing phase
  • false- Default. The event handler is executed in the bubbling phase

Действия по умолчанию

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

Для большинства типов событий обработчики JavaScript event вызываются до выполнения действий по умолчанию. Если не нужно, чтобы выполнялось поведение по умолчанию, нужно вызвать для объекта события метод preventDefault.

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

<a href="https://developer.mozilla.org/">MDN</a>
<script>
  var link = document.querySelector("a");
  link.addEventListener("click", function(event) {
    console.log("Nope.");
    event.preventDefault();
  });
</script>

Старайтесь не делать так, если у вас нет на это веских причин.

В зависимости от браузера некоторые события не могут быть перехвачены. В Google Chrome, например, сочетание клавиш (event keycode JavaScript) для закрытия текущей вкладки (Ctrl-W или Command-W) не может быть обработано с помощью JavaScript.

События мультимедиа

В процессе загрузки аудио/видео события происходят в следующем порядке: -> -> -> -> -> -> .

abort — событие возникает, когда прерывается загрузка аудио/видео. Это событие возникает именно когда загрузка медиа данных была прервана (отменена), а не, потому что возникла ошибка.

error — событие возникает, когда произошла ошибка при загрузке аудио/видео.

stalled — событие возникает, когда браузер пытается получить медиа данные, но данные недоступны.

suspend — событие возникает, когда браузер не может получить медиа данные, т.е. загрузка медиа останавливается по какой-то причине.

loadstart — событие происходит, когда браузер начинает искать указанное аудио/видео, т.е. когда начинается процесс загрузки.

durationchange — событие возникает, когда изменяется длительность аудио/видео. Если аудио/видео загружается, то длительность будет меняться от значения «NaN» до фактической длительности аудио/видео.

loadedmetadata — событие возникает, когда метаданные для указанного аудио/видео загружены. Метаданные аудио/видео состоят из: длительности, размера (только видео) и текстовой дорожки.

loadeddata — событие возникает, после того как первый кадр медиа загрузился.

progress — событие происходит, когда браузер загружает указанное аудио/видео.

canplay — событие возникает, когда браузер может начать воспроизведение указанного аудио/видео (т.е. когда буферизация потока аудио/видео достаточна для того чтобы браузер мог начать его воспроизводить).

canplaythrough — событие возникает в тот момент времени, когда браузер может проигрывать указанное медиа без остановки на буферизацию.

ended — событие происходит, когда воспроизведение аудио/видео завершилось (достигло конца)

Это событие может использоваться для вывода сообщений типа «Спасибо за внимание», «Спасибо за просмотр» и др.

pause — событие происходит, когда воспроизведение аудио/видео приостановлено пользователем или с помощью кода (программно).

play — событие происходит, когда начинается воспроизведение аудио/видео. Оно также происходит, когда аудио/видео было снято с паузы и начинает воспроизводиться.

playing — событие происходит, когда аудио/видео воспроизводится после того как оно было поставлено на паузу или остановилось для буферизации.

ratechange — событие происходит, когда изменяется скорость воспроизведения аудио/видео.

seeking — событие происходит, когда пользователь начинает перемещение ползунка (переход) к новой временной позиции проигрываемого аудио/видео.

seeked — событие происходит, когда пользователь закончил перемещение ползунка (переход) в новую временную позицию проигрываемого аудио/видео

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

timeupdate — событие происходит при изменении временной позиции воспроизводимого аудио/видео.Это событие происходит:
при воспроизведении потока аудио/видео.
при перемещении ползунка в новую временную позицию воспроизводимого аудио/видео.

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

volumechange – событие происходит каждый раз при изменении громкости воспроизводимого потока видео/аудио.Это событие происходит, при:
увеличении или уменьшении громкости;
отключении или включении звука.

waiting — событие происходит, когда видео останавливается для буферизации.

Объект «событие» (event)

Объект событие всегда передается обработчику и содержит массу полезной информации о том где и какое событие произошло.

Способов передачи этого объекта обработчику существует ровно два, и они зависят от способа его установки и от браузера.

В браузерах, работающих по рекомендациям W3C, объект события всегда передается в обработчик первым параметром.

Например:

function doSomething(event) {
	// event - будет содержать объект события
}

element.onclick = doSomething;

При вызове обработчика объект события будет передан ему первым аргументом.

Можно назначить и вот так:

element.onclick = function(event) {
	// event - объект события
}

Интересный побочный эффект — в возможности использования переменной при назначении обработчика в HTML:

<input type="button" onclick="alert(event)" value="Жми сюда не ошибешься"/>

Это работает благодаря тому, что браузер автоматически создает функцию-обработчик с данным телом, в которой первый аргумент .

В Internet Explorer существует глобальный объект , который хранит в себе информацию о последнем событии. А первого аргумента обработчика просто нет.

То есть, все должно работать так:

// обработчик без аргументов
function doSomething() {
	// window.event - объект события
}

element.onclick = doSomething;

Обратите внимание, что доступ к при назначении обработчика в HTML (см. пример выше) по-прежнему будет работать

Такой вот надежный и простой кросс-браузерный доступ к объекту события.

Можно кросс-браузерно получить объект события, использовав такой приём:

function doSomething(event) {
    event = event || window.event

    // Теперь event - объект события во всех браузерах.
}

element.onclick = doSomething

Как мы уже говорили раньше, при описании обработчика события в HTML-разметке для получения события можно использовать переменную с названием .

<input type="button" onclick="alert(event.type)" value="Нажми меня"/>

Этот код в действии:

Это совершенно кросс-браузерный способ, так как по стандарту — название первого аргумента функции-обработчика, которую автоматом создаст браузер; ну а в IE значение будет взято из глобального объекта .

addEventListener() Syntax

Here’s the syntax:

  • target: the HTML element you wish to add your event handler to. This element exists as part of the Document Object Model (DOM) and you may wish to learn about .
  • event: a string that specifies the name of the event. We already mentioned and events. For the curious, here’s a full list of HTML DOM events.
  • function: specifies the function to run when the event is detected. This is the magic that can allow your web pages to change dynamically.
  • useCapture: an optional Boolean value (true or false) that specifies whether the event should be executed in the capturing or bubbling phase. In the case of nested HTML elements (such as an within a ) with attached event handlers, this value determines which event gets executed first. By default, it’s set to false which means that the innermost HTML event handler is executed first (bubbling phase).

События при CSS переходе

  • – возникает при создании CSS перехода (т.е. когда он добавляется к набору запущенных переходов, но не обязательно он начался);
  • – происходит, когда CSS переход начинает выполняться;
  • – возникает, если CSS переход был отменен;
  • – происходит при завершении CSS перехода.

Пример:

<style>
#box {
  position: absolute;
  top: 15px;
  left: 15px;
  width: 100px;
  height: 100px;
  background-color: #03a9f4;
}
.transform {
  transform: translateX(300px);
  transition: transform 1s ease-in-out;
}
</style>
<script>
  document.addEventListener('DOMContentLoaded', () => {
    document.addEventListener('click', (e) => {
      const $target = e.target;
      if ($target.matches('#start')) {
        e.preventDefault();
        $box.classList.add('transform');
      } else if ($target.matches('#cancel')) {
        e.preventDefault();
        $box.classList.remove('transform');
      }
    });
    const $box = document.querySelector('#box');
    $box.addEventListener('transitionrun', () => {
      console.log('transitionrun');
    });
    $box.addEventListener('transitionstart', () => {
      console.log('transitionstart');
    });
    $box.addEventListener('transitioncancel', () => {
      console.log('transitioncancel');
    });
    $box.addEventListener('transitionend', () => {
      console.log('transitionend');
    });
  });
</script>

<div id="box"></div>

<a href="#" id="start">Start</a>
<a href="#" id="cancel">Cancel</a>

Adding Event Handlers

JavaScript provides you with a lot of different opportunities, which we will now review one by one. First, we will learn to simply add an event handler to an element. Then, we’ll try adding more than one at once.

Once we get the idea, we’ll see how event handlers should be applied to window objects. Carefully review the code examples provided to grasp the concepts.

Adding an Event Handler to an Element

When you add a JavaScript event listener, the function to be executed upon an event can be either anonymous or named. In the example below, you can see the anonymous function used. In this case, you need to define the code in the function:

Example Copy

In the example below you can see the named function being used. In this case, you are referencing an external function:

Example Copy

Add Many Event Handlers to the Same Element

The JavaScript method lets you add multiple event handlers to a single element, while not overwriting any previously assigned event handlers. Take a look at the example below:

Example Copy

Different event types may be specified as well:

Example Copy

Add an Event Handler to the Window Object

The JavaScript method lets you add event listeners to HTML DOM objects, such as HTML elements, the document that the HTML is in itself, the window object, and any other object that supports events (like the xmlHttpRequest object).

Example Copy

Добавление обработчика через свойство DOM объекта

Второй способ назначить обработчик — это использовать свойство .

Например, привяжем обработчик события к элементу (для этого события свойство будет ):

<!-- HTML код кнопки -->
<button type="button" id="my-btn">Нажми на меня</button>

<!-- Скрипт на JavaScript -->
<script>
// получим кнопку и сохраним ссылку на неё в переменную
const $btn = document.querySelector('#my-btn');
// добавим к $btn обработчик события click
$btn.onclick = function() {
  alert('Вы кликнули на кнопку!');
}
</script>

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

Другой вариант – это назначить уже существующую функцию.

Например:

function changeBgColor() {
  document.body.style.backgroundColor = `rgb(${Math.round(Math.random()*255)}, ${Math.round(Math.random()*255)}, ${Math.round(Math.random()*255)})`;
}

document.onclick = changeBgColor;

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

Например:

<!-- HTML код кнопок -->
<button type="button">Кнопка 1</button>
<button type="button">Кнопка 2</button>
<button type="button">Кнопка 3</button>

<!-- Скрипт на JavaScript -->
<script>
function message() {
  // this - обращаемся к кнопке для которой вызван обработчик
  alert(this.textContent);
}
// получим кнопки и сохраним ссылки на них в переменную $btns
const $btns = document.querySelectorAll('button');
// переберём кнопки и добавим к ним обработчик, используя onclick
$btns.forEach(function($element) {
  $element.onclick = message;
});
</script>

Кстати, когда обработчик задаётся через атрибут, то браузер самостоятельно при чтении такого HTML создаёт из значения этого атрибута функцию и присваивает её одноименному свойству этого элемента.

Например:

<button id="btn" type="button" onclick="alert('Вы кликнули на кнопку')">Кнопка</button>

<script>
const $element = document.querySelector('#btn');
// получим значение свойства onclick (как видно браузер туда автоматически записал функцию, которую создал на основании содержимого этого атрибута)
console.log($element.onclick);
</script>

Т.е., по сути, задание свойства через атрибут – это просто способ инициализации обработчика. Т.к. сам обработчик в этом случае тоже хранится в свойстве DOM-объекта.

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

<button id="btn" type="button">Кнопка</button>

<script>
  const $element = document.querySelector('#btn');

  $element.onclick = function () {
    alert(`id = ${this.id}`);
  }
  // заменит предыдущий обработчик
  $element.onclick = function () {
    alert(`text = ${this.textContent}`);
  }
</script>

Кстати, также не получится назначить несколько обработчиков, один через атрибут, а другой через свойство. Последний перепишет предыдущий.

<button id="btn" type="button" onclick="alert(`id = ${this.id}`);">Кнопка</button>

<script>
  const $element = document.querySelector('#btn');
  // заменит обработчик, инициализированный с помощью атрибута
  $element.onclick = function () {
    alert(`text = ${this.textContent}`);
  }
</script>

Примечания

 — это способ зарегистрировать обработчик события, описанный в документации W3C DOM. Вот список преимуществ его использования:

  • Позволяет добавлять множество обработчиков для одного события. Это особенно полезно для DHTML библиотек или Mozilla extensions, которые должны работать в условиях использования сторонних библиотек/расширений.
  • Предоставляет точный контроль фазы срабатывания(вызова) обработчика (захват или всплытие)
  • Срабатывает на любом DOM элементе, а не только на HTML элементах.

Ниже описан другой, .

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

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

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

В примере выше значение переменной  внутри  при вызове событием клика равно таблице ‘t’. Это противоположно поведению, которое возникает, если обработчик добавлен в HTML-разметке:

Значение переменной  внутри  при вызове событием клика будет равно ссылке на глобальный (window) объект (или  при использовании strict mode)

Note: В JavaScript 1.8.5 введён метод  , который позволяет указать значение, которое должно быть использовано для всех вызовов данной функции. Он позволяет вам легко обходить ситуации, в которых не ясно, чему будет равно this, в зависимости от того, в каком контексте будет вызвана ваша функция. заметьте, также, что вам будет необходимо иметь внешнюю ссылку на обработчик, чтобы вы могли удалить его позже.

Пример с использованием  и без него:

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

В Internet Explorer младше 9 версии, вы можете использовать  вместо стандартного . Для поддержки IE, пример выше может быть модифицирован следующим образом:

У  есть недостаток:  будет ссылаться на объект , а не на элемент, на котором он был вызван.

JS Tutorial

JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS LetJS ConstJS OperatorsJS ArithmeticJS AssignmentJS Data TypesJS FunctionsJS ObjectsJS EventsJS StringsJS String MethodsJS NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS DatesJS Date FormatsJS Date Get MethodsJS Date Set MethodsJS MathJS RandomJS BooleansJS ComparisonsJS ConditionsJS SwitchJS Loop ForJS Loop For InJS Loop For OfJS Loop WhileJS BreakJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS ScopeJS HoistingJS Strict ModeJS this KeywordJS Arrow FunctionJS ClassesJS JSONJS DebuggingJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved Words

Заключение

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

Каждое событие относится к определенному типу («keydown«, «focus» и так далее), который идентифицирует его. Большинство событий вызывается для конкретного элемента DOM, а затем распространяются на родительские узлы элемента. Это позволяет обработчикам, связанным с этими элементами, обрабатывать их.

Когда вызывается обработчик, ему передается объект события с дополнительной информацией о действии. Этот объект также содержит методы, позволяющие остановить дальнейшее распространение события (stopPropagation) или предотвратить обработку события браузером по умолчанию (preventDefault).

Нажатие клавиш порождает события «keydown«, «keypress» и «keyup«. Нажатие мыши порождает события «mousedown«, «mouseup» и «click«. Перемещение мыши — «mousemove«, «mouseenter» и «mouseout«.

JavaScript scroll event можно определить с помощью события «scroll«, а смена фокуса — «focus» и «blur«. После завершения загрузки документа для окна возникает событие «load«.

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

Итого

Делегирование событий – это здорово! Пожалуй, это один из самых полезных приёмов для работы с DOM.

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

Алгоритм:

  1. Вешаем обработчик на контейнер.
  2. В обработчике проверяем исходный элемент .
  3. Если событие произошло внутри нужного нам элемента, то обрабатываем его.

Зачем использовать:

  • Упрощает процесс инициализации и экономит память: не нужно вешать много обработчиков.
  • Меньше кода: при добавлении и удалении элементов не нужно ставить или снимать обработчики.
  • Удобство изменений DOM: можно массово добавлять или удалять элементы путём изменения и ему подобных.

Конечно, у делегирования событий есть свои ограничения:

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

Но обычно эта нагрузка настолько пустяковая, что её даже не стоит принимать во внимание.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector