Простое объяснение делегирования событий в javascript

Захват указателя

Захват указателя – особая возможность событий указателя.

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

Основной метод:

elem.setPointerCapture(pointerId) – привязывает события с данным pointerId к elem. После такого вызова все события указателя с таким pointerId будут иметь elem в качестве целевого элемента (как будто произошли над elem), вне зависимости от того, где в документе они произошли.

Другими словами, меняет всех дальнейших событий с данным на .

Эта привязка отменяется:

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

Захват указателя используется для упрощения операций с переносом (drag’n’drop) элементов.

В качестве примера давайте вспомним реализацию слайдера из статьи Drag’n’Drop с событиями мыши.

Мы делаем элемент для слайдера – полоску с «ползунком» () внутри.

Затем он работает так:

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

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

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

Захват указателя позволяет привязать к и избежать любых подобных проблем:

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

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

Вот основной код:

Полное демо:

Таким образом, мы имеем два бонуса:

  1. Код становится чище, поскольку нам больше не нужно добавлять/удалять обработчики для всего документа. Удаление привязки происходит автоматически.
  2. Если в документе есть какие-то другие обработчики , то они не будут нечаянно вызваны, пока пользователь находится в процессе перетаскивания слайдера.

Существует два связанных с захватом события:

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

Пример:Onclick В JavaScript, используя метод addEventListener()

И последний элемент теории Onclick — использование метода addEventListener

Нам опять понадобится кнопка

<button id=»onclick_v_addEventListener»>Это второй способ реализации Onclick в JavaScript</button>

Опять применяем querySelector — обращаемся к нашей кнопке, добавляем метод addEventListener, во внутрь помещаем событие click И второй параметр — это функция myFoo.

document.querySelector(«#onclick_v_addEventListener») .addEventListener(«click», myFoo);

Далее нам понадобится функция:

function myFoo() { alert(«Это третий способ реализации Onclick в JavaScript через addEventListener и вывод через alert»); }

Соберем весь код вместе:

<button id=»onclick_v_addEventListener»>Это третий способ реализации Onclick в JavaScript</button>

<script>

document.querySelector(«#onclick_v_addEventListener») .addEventListener(«click», myFoo);

function myFoo()

{

alert(«Это третий способ реализации Onclick в JavaScript через addEventListener и вывод через alert»);

}

</script>

Результат:

Это третий способ реализации Onclick в JavaScript

Отмена действия браузера

Есть два способа отменить действие браузера:

  • Основной способ – это воспользоваться объектом . Для отмены действия браузера существует стандартный метод .
  • Если же обработчик назначен через (не через ), то также можно вернуть из обработчика.

Возвращать не нужно

Обычно значение, которое возвращает обработчик события, игнорируется.

Единственное исключение – это из обработчика, назначенного через .

В других случаях не нужен, он никак не обрабатывается.

Рассмотрим меню для сайта, например:

Данный пример при помощи CSS может выглядеть так:

В HTML-разметке все элементы меню являются не кнопками, а ссылками, то есть тегами . В этом подходе есть некоторые преимущества, например:

  • Некоторые посетители очень любят сочетание «правый клик – открыть в новом окне». Если мы будем использовать или , то данное сочетание работать не будет.
  • Поисковые движки переходят по ссылкам при индексации.

Например, вот так:

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

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

События, вытекающие из других

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

Например, событие для поля приводит к фокусировке на нём и запускает событие . Если мы отменим событие , то фокусирования не произойдёт.

В следующем примере попробуйте нажать на первом – происходит событие . Но если вы нажимаете по второму элементу, то события не будет.

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

Координаты мыши: clientX(Y)/pageX(Y)

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

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

Если у вас есть окно 500×500, и мышь находится в центре, то и будут оба равны 250. Если вы затем проскроллируете документ вниз, налево или вверх, не двигая курсор — значения не изменятся, так как отсчитываются относительно окна, а не документа.

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

Если у вас есть окно 500×500, и мышь находится в центре, то и будут оба равны 250. Если вы затем проскроллируете на 250 пикселей вниз, станет равным 750.

Таким образом содержат координаты, на каком месте документа произошло событие, учитывая все прокрутки.

Свойства поддерживаются всеми браузерами, кроме Internet Explorer.

В IE их можно получить из , прибавив к ним .

Обычно оно находится в <body>: , но это не всегда так. Например, при выборе Strict DTD оно высчитывается для <html>: . Кроме того, тэга <body> может просто не быть в документе.

Поэтому мы сначала возьмем (если есть), затем проверим . Если нет ни того, ни того, то 0.

var html = document.documentElement
var body = document.body
e.pageX = e.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0)

Кроме того, в IE может быть немного сдвинут с позиции 0,0. Значение сдвига находится в , и его также необходимо учесть.

Этот код позволяет надежно получить для IE, в котором его изначально нет:

if (e.pageX == null && e.clientX != null ) { 
    var html = document.documentElement
    var body = document.body

    e.pageX = e.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0)
    e.pageY = e.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0)
}

Этот обработчик обновляет координаты мыши относительно документа.

function mouseShowHandler(e){
	e = e || window.event

	if (e.pageX == null && e.clientX != null ) { 
		var html = document.documentElement
		var body = document.body
	
		e.pageX = e.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0)
		e.pageY = e.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0)
	}

	
	document.getElementById('mouseX').value = e.pageX
	document.getElementById('mouseY').value = e.pageY
}

Координата X:
Координата Y:

Обработчик события как атрибут элемента

Это самый старый способ назначения обработчиков события и наименее используемый на данный момент. Для того чтобы его добавить, необходимо к имени события добавить приставку «on» и записать примерно такой код:

Обработчик события как атрибут

JavaScript

<button onclick=’alert(«Событие произошло!»)’>Нажми меня</button>

1 <button onclick=’alert(«Событие произошло!»)’>Нажмименя<button>

Нажми меня

Можно вызвать функцию, в которую можно передать один или несколько аргументов:

Обработчик события как атрибут элемента

JavaScript

<script>
function change(element, color){
element.style.backgroundColor = color;
}
</script>
<p onclick=»change(this, ‘red’)»>Кликни здесь, и этот абзац станет красным.
Но только один раз.</p>
<p onclick=»change(this, ‘lime’)»>Кликни здесь, и этот абзац станет зеленым.
Но только один раз.</p>

1
2
3
4
5
6
7
8
9

<script>

functionchange(element,color){

element.style.backgroundColor=color;

}
</script>

<ponclick=»change(this, ‘red’)»>Кликниздесь,иэтотабзацстанеткрасным.

Нотолькоодинраз.<p>

<ponclick=»change(this, ‘lime’)»>Кликниздесь,иэтотабзацстанетзеленым.

Нотолькоодинраз.<p>

Пример этого кода:

Кликни здесь, и этот абзац станет красным. Но только один раз.

Кликни здесь, и этот абзац станет зеленым. Но только один раз.

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

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

event.defaultPrevented

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

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

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

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

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

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

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

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

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

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

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

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

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

event.stopPropagation() и event.preventDefault()

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

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

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

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

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

Краткая история

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

  • Давным-давно, в прошлом, существовали только события мыши

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

    Но сенсорные устройства во многих аспектах мощнее, чем мышь. Например, они позволяют касаться экрана сразу в нескольких местах («мульти-тач»). Однако, события мыши не имеют необходимых свойств для обработки таких прикосновений.

  • Поэтому появились события касания (Touch events), такие как , , , которые имеют специфичные для касаний свойства (мы не будем здесь рассматривать их подробно, потому что события указателя ещё лучше).

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

  • Для решения этих задач был внедрён стандарт Pointer Events («События Указателя»). Он предоставляет единый набор событий для всех типов указывающих устройств.

К настоящему времени спецификация Pointer Events Level 2 поддерживается всеми основными браузерами, а Pointer Events Level 3 находится в разработке и почти полностью совместима с Pointer Events Level 2.

Если вы не разрабатываете под старые браузеры, такие как Internet Explorer 10, Safari 12, или более ранние версии, больше нет необходимости использовать события мыши или касаний – можно переходить сразу на события указателя.

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

javascript onclick без клика

javascript onclick без клика

но первое, что я подумал… вспомнил старый пошлый анекдот! дети, если вам нет 16, то закрываем страницу на этом месте!

Закрыли!? Я жду!

Теперь продолжим со взрослыми…

Встречается парочка на хате, а у парня был попугай! Девушка:

— я не могу заниматься этим, когда он смотрит…

Парень накинул тряпку на клетку.

И говорит попугаю! Будешь подсматривать — голову оторву!

Начали заниматься любовью!

— Давай я сверху, ты снизу!

— Давай!

— Давай ты сверху, я снизу!

— Давай!

— А теперь давай ты сверху, и я сверху!

Попугай:

— Пусть мне оторвут голову! Но это я должен увидеть!

Event Bubbling or Event Capturing?

There are two ways of event propagation in the HTML DOM, bubbling and capturing.

Event propagation is a way of defining the element order when an event occurs.
If you have a <p> element inside a <div> element, and the user clicks on the <p> element, which element’s
«click» event should be handled first?

In bubbling the inner most element’s event is handled first and then the outer:
the <p> element’s click event is handled first, then the <div> element’s click event.

In capturing the outer most element’s event is handled first and then the inner:
the <div> element’s click event will be handled first, then the <p> element’s click event.

With the addEventListener() method you can specify the propagation type by using the «useCapture» parameter:

addEventListener(event, function, useCapture);

The default value is false, which will use the bubbling propagation, when the value is set to true, the event uses the capturing propagation.

Example

document.getElementById(«myP»).addEventListener(«click», myFunction, true);
document.getElementById(«myDiv»).addEventListener(«click», myFunction, true);

JavaScript

JS Array
concat()
constructor
copyWithin()
entries()
every()
fill()
filter()
find()
findIndex()
forEach()
from()
includes()
indexOf()
isArray()
join()
keys()
length
lastIndexOf()
map()
pop()
prototype
push()
reduce()
reduceRight()
reverse()
shift()
slice()
some()
sort()
splice()
toString()
unshift()
valueOf()

JS Boolean
constructor
prototype
toString()
valueOf()

JS Classes
constructor()
extends
static
super

JS Date
constructor
getDate()
getDay()
getFullYear()
getHours()
getMilliseconds()
getMinutes()
getMonth()
getSeconds()
getTime()
getTimezoneOffset()
getUTCDate()
getUTCDay()
getUTCFullYear()
getUTCHours()
getUTCMilliseconds()
getUTCMinutes()
getUTCMonth()
getUTCSeconds()
now()
parse()
prototype
setDate()
setFullYear()
setHours()
setMilliseconds()
setMinutes()
setMonth()
setSeconds()
setTime()
setUTCDate()
setUTCFullYear()
setUTCHours()
setUTCMilliseconds()
setUTCMinutes()
setUTCMonth()
setUTCSeconds()
toDateString()
toISOString()
toJSON()
toLocaleDateString()
toLocaleTimeString()
toLocaleString()
toString()
toTimeString()
toUTCString()
UTC()
valueOf()

JS Error
name
message

JS Global
decodeURI()
decodeURIComponent()
encodeURI()
encodeURIComponent()
escape()
eval()
Infinity
isFinite()
isNaN()
NaN
Number()
parseFloat()
parseInt()
String()
undefined
unescape()

JS JSON
parse()
stringify()

JS Math
abs()
acos()
acosh()
asin()
asinh()
atan()
atan2()
atanh()
cbrt()
ceil()
clz32()
cos()
cosh()
E
exp()
expm1()
floor()
fround()
LN2
LN10
log()
log10()
log1p()
log2()
LOG2E
LOG10E
max()
min()
PI
pow()
random()
round()
sign()
sin()
sqrt()
SQRT1_2
SQRT2
tan()
tanh()
trunc()

JS Number
constructor
isFinite()
isInteger()
isNaN()
isSafeInteger()
MAX_VALUE
MIN_VALUE
NEGATIVE_INFINITY
NaN
POSITIVE_INFINITY
prototype
toExponential()
toFixed()
toLocaleString()
toPrecision()
toString()
valueOf()

JS OperatorsJS RegExp
constructor
compile()
exec()
g
global
i
ignoreCase
lastIndex
m
multiline
n+
n*
n?
n{X}
n{X,Y}
n{X,}
n$
^n
?=n
?!n
source
test()
toString()

(x|y)
.
\w
\W
\d
\D
\s
\S
\b
\B
\0
\n
\f
\r
\t
\v
\xxx
\xdd
\uxxxx

JS Statements
break
class
continue
debugger
do…while
for
for…in
for…of
function
if…else
return
switch
throw
try…catch
var
while

JS String
charAt()
charCodeAt()
concat()
constructor
endsWith()
fromCharCode()
includes()
indexOf()
lastIndexOf()
length
localeCompare()
match()
prototype
repeat()
replace()
search()
slice()
split()
startsWith()
substr()
substring()
toLocaleLowerCase()
toLocaleUpperCase()
toLowerCase()
toString()
toUpperCase()
trim()
valueOf()

Типы событий указателя

Схема именований событий указателя похожа на события мыши:

Событие указателя Аналагичное событие мыши

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

Замена на в коде

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

При этом поддержка сенсорных устройств «волшебным» образом улучшится. Хотя, возможно, кое-где понадобится добавить в CSS. Подробнее мы разберём это ниже, в секции про .

Как передать данные в другой тег по id при onclick

уникальным идентификаторомid<div id=»demo»></div>

Далее нам понадобится скрипт, который по нажатию, отправит какие-то данные в этот див:

<button onclick=»getElementById(‘demo’).innerHTML = Date()»>Сколько времени!?</button>
Сколько времени!?тегу

Как вызвать функцию по клику, как мы уже раньше говорили, что click можно вызвать
1). В первом случае меняем слово alert на название функции, а в скобках идущих после alert убираем содержимое…

В функции прописываем что-то…

<script> function my_super_foo(){alert(«Вызов функции в теге… по клику»); }</script>

<button onclick=»my_super_foo()»>Вызов функции в теге… по клику</button>

Результат вызова функции из тега

Вызов функции в теге… по клику2). Второй способ… идем во встрой способ… там даже и переделывать ничего не нужно… заменяем только само действие в внутри функции priner_id .innerHTML =»НОВЫЙ ТЕКСТ»; — готовый способ вызова функции по клику…
3). Третий способ аналогичен второму… копируем все, что описано в третье пункте, заменяем внутри функции на ваше действие…

В качестве примера рекомендую — страницу с кнопками — все события производится именно кнопками, на которых повешено событие onclick.

1). Как повесить на одну кнопку сразу две функции!? Или три!? Или четыре… так можно продолжать до бесконечности:

Если говорить о двух событиях на onclick, то во всех , кроме первого(в теге) — этот вопрос кажется немного странным! Почему!? Просто во внутрь функции ставишь не одно событие, а столько сколько захочешь!

Если говорить об onclick два события в теге, то внутри onclick прописываем стандартные действия как в js

<button onclick=»getElementById(‘demo’).innerHTML = Date();alert(‘Щелчок мыши!’)»>Два события.</button>

<kod id=»demo»></kod>

Два события.

2). Если мы возьмем и сделаем функцию, обрабатывающую нажатие на кнопку…, то там тоже нет никакой проблемы повесить на кнопку два, три, пять, 100 функций!

<button id=»new_id»>Две функции onclick на одну кнопку js</button>

<script>

new_id .addEventListener(«click», myFoo);

function myFoo()

{

alert(«одна функция onclick на одну кнопку js»);

alert(«Две функции onclick на одну кнопку js»);

alert(«Три функции onclick на одну кнопку js»);

}

</script>

Две функции onclick на одну кнопку js

Если , например нужно изменить где-то цвет, то можно посмотреть пример с помощью onclick

Код:

<button onclick=»getElementById(‘rezult’).setAttribute(‘style’, ‘color: red;’);»>Изменить цвет</button>

<div id=»rezult»>Здесь текст, который будет менять при нажатии</div>

Результат:

Изменить цвет
Здесь текст, который будет менять при нажатии

4.

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

Далее изменение текста и одновременное изменение цвета.

Код:

далее скрипт, либо можно выделить в отдельный файл скрипта…

<script> function foo() { 

document.getElementById(«rezult_1»).innerHTML = «привет»; 

document.getElementById(«rezult_1»).setAttribute(«style»,  «color: red»  ); 

}    

</script>

Результат:

Нажми здесь

Здесь текст, который будет менять при нажатии

addEventListener() Метод

Пример

Добавьте прослушиватель событий, который срабатывает при нажатии пользователем кнопки:

document.getElementById(«myBtn»).addEventListener(«click», displayDate);

Метод addEventListener () присоединяет обработчик событий к указанному элементу.

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

Можно добавить несколько обработчиков событий в один элемент.

Можно добавить несколько обработчиков событий одного типа в один элемент, т.е. два события «Click».

Прослушиватели событий можно добавлять к любому объекту DOM, а не только к элементам HTML. т.е. объект Window.

Метод addEventListener () упрощает управление реакцией события на восходящую маршрутизацию.

При использовании метода addEventListener (), JavaScript отделяется от HTML разметки, для лучшей читаемости и позволяет добавлять прослушиватели событий, даже если вы не контролируете разметку HTML.

Прослушиватель событий можно легко удалить с помощью метода RemoveEventListener ().

JS Tutorial

JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS 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 WhileJS BreakJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS ScopeJS HoistingJS Strict ModeJS this KeywordJS LetJS ConstJS Arrow FunctionJS ClassesJS DebuggingJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved WordsJS VersionsJS 2009 (ES5)JS 2015 (ES6)JS 2016JS 2017JS JSON

Погружение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Последнее обновление: 1.11.2015

Встроенные обработчики

В прошлой теме были рассмотрены встроенные обработчики (inline event handler), которые определяются в коде элемента с
помощью атрибутов:

<div id="rect" onclick="handler(event)"></div>

Хотя этот подход прекрасно работает, но он имеет кучу недостатков:

  • Код html смешивается с кодом JavaScript, в связи с чем становится труднее разрабатывать, отлаживать и поддерживать приложение

  • Обработчики событий можно задать только для уже созданных на веб-странице элементов. Динамически создаваемые элементы в этом случае
    лишаются возможности обработки событий

  • К элементу для одного события может быть прикреплен только один обработчик

  • Нельзя удалить обработчик без изменения кода

Свойства обработчиков событий

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

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<style>
	#rect{
		width:50px;
		height:50px;
		background-color:blue;
	}
	</style>
</head>
<body>
<div id="rect"></div>
<script>
function handler(e){
	
	alert(e.type);
}
document.getElementById("rect").onclick = handler;
</script>
</body>
</html>

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

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

Слушатели событий

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

Для работы со слушателями событий в JavaScript есть объект EventTarget, который определяет методы
addEventListener() (для добавления слушателя) и removeEventListener()
для удаления слушателя. И поскольку html-элементы DOM тоже являются объектами EventTarget, то они также имеют эти методы. Фактически слушатели представляют те же функции обработчиков.

Метод принимает два параметра: название события без префикса on и функцию обработчика этого события. Например:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<style>
	#rect{
		width:50px;
		height:50px;
		background-color:blue;
	}
	</style>
</head>
<body>
<div id="rect"></div>
<script>
var rect = document.getElementById("rect");

rect.addEventListener("click", function (e) {
	alert(e.type);
});
</script>
</body>
</html>

То есть в данном случае опять же обрабатывается событие click. И также можно было бы в качестве второго параметра название функции:

function handler(e){
	
	alert(e.type);
}
var rect = document.getElementById("rect");

rect.addEventListener("click", handler);

Удаление слушателя аналогично добавлению:

rect.removeEventListener("click", handler);

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

var clicks = 0;
function handlerOne(e){
	
	alert(e.type);
}
function handlerTwo(e){
	
	clicks++;
	var newNode = document.createElement("p");
	newNode.textContent = "произошло нажатие " + clicks;
	document.body.appendChild(newNode);
}
var rect = document.getElementById("rect");
// прикрепляем первый обработчик
rect.addEventListener("click", handlerOne);
// прикрепляем второй обработчик
rect.addEventListener("click", handlerTwo);

НазадВперед

Итого

Действий браузера по умолчанию достаточно много:

  • – начинает выделять текст (если двигать мышкой).
  • на – ставит или убирает галочку в .
  • – при нажатии на или при нажатии клавиши Enter в форме данные отправляются на сервер.
  • – при нажатии клавиши в поле ввода появляется символ.
  • – при правом клике показывается контекстное меню браузера.
  • …и многие другие…

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

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

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

Если событие по умолчанию отменено, то значение становится , иначе .

Сохраняйте семантику, не злоупотребляйте

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

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

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

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

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

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

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

Adblock
detector