введение
JavaScript
а такжеHTML
Взаимодействие между ними осуществляется посредством событий. События — это определенные моменты взаимодействия, происходящие в документе или окне браузера. Система событий браузера относительно сложна. Хотя все основные браузеры реализовали "DOM2
события класса", но сама эта спецификация не охватывает все типы событий, сDOM3
появляется класс,DOM
мероприятиеAPI
стать богаче. Кроме того, объектная модель браузера (BOM
) также поддерживает некоторые события, но эти события не связаны с объектной моделью документа (DOM
) непонятно, потому чтоBOM
События давно не имеют норм для подражания(HTML5
Инструкции были даны позже). Эта статья в основном знакомит с браузеромDOM
Система событий включает в себя три этапа потока событий и три разных способа обработчиков событий (DOM0
,DOM2
,IE
),учитываяIE
Как быть с совместимостью разницы между обработкой события и объектом события в , как применить свойства объекта события к практическим приложениям и различиям между ними, а также последовательности захвата события и всплытия.
В начале статьи я кратко представляю некоторые важные моменты этой статьи:
-
DOM
Существует три способа обработчика событий,DOM0
изonType
,IE9
нижеattachEvent
а такжеdetachEvent
,DOM2
изaddEventListener
а такжеremoveEventListener
. -
DOM2
Преимущество класса в том, что его можно пройти черезaddEventListener
Третий параметр, указывающий, следует ли захватывать или всплывать, и может быть одним и тем же.DOM
элементы регистрируют несколько обработчиков событий одного и того же типа;DOM0
Поддержка только одного обработчика событий для каждого события -
DOM0
а такжеDOM2
обработчики событий автоматически передаются вevent
объект; в IEevent
Объект зависит от метода указанного обработчика событий, поэтому в IE будетwindow.event
,event
два случая;event
Объекты имеют некоторые полезные свойства, такие как цель,currentTarget
,preventDefault
,stopPropagation
,stopImmediatePropagation
Ждать - для
DOM0
изontype
, методы привязки к поведению события элемента выполняются в фазе всплытия (или целевой фазе) поведения события текущего элемента. дляDOM2
изaddEventListener
, для максимальной совместимости обработчики событий в большинстве случаев добавляются в фазу всплытия событий. Особо не требуется, и регистрация обработчиков событий на этапе захвата событий не рекомендуется. - Следует учитывать совместимость обработки обработчиков событий.
DOM0
а такжеIE9
Следует учитывать следующие методы обработки событий, обработку совместимости объекта события и свойства объекта события.IE
разница в -
event.stopPropagation()
метод, чтобы предотвратить всплытие события к родительскому элементу, предотвращая выполнение любых обработчиков родительских событий (обычно мы думаемstopPropagation
Она используется для предотвращения всплытия событий, по сути, эта функция также может предотвращать захват событий) -
event.target
указывает на элемент, вызвавший инициирующее событие, иevent.currentTarget
Это элемент, привязанный к событию, только целевой элемент, на который нажали.event.target
будет равноevent.currentTarget
Большинство браузеров, поддерживающих потоковую передачу событий DOM, реализуют определенное поведение; хотя в спецификации «события уровня DOM2» явно указано, что этап захвата не включает цели событий, IE9, Safari, Chrome, Firefox и Opera 9.5 и более поздние версии события на объекте события во время фазы захвата. В результате есть две возможности манипулировать событиями на целевом объекте.
поток событий
Поток событий описывает порядок получения событий со страницы. Но интересно,IE
а такжеNetscape
Команда разработчиков на самом деле придумала две диаметрально противоположные концепции потока событий. Поток событий IE — это всплывающий поток событий, а стандартный поток событий браузера — это поток захвата событий. но,W3C
Для разработки стандарта был выбран компромисс: сначала захват, а затем барботирование (черезaddEventListene
Третий аргумент, заданный r, поддерживает как всплытие, так и захват). В частности, тот жеDOM
Элементы могут регистрироваться для нескольких событий одного и того же типа черезaddEventListener
зарегистрироваться на мероприятия,removeEventListener
отменить мероприятие.
Обратите внимание, что если зарегистрированное событие можно отменить, функция обратного вызова должна быть сохранена, в противном случае ее нельзя будет отменить.
DOM
Поток событий делится на три этапа:捕获阶段
,目标阶段
,冒泡阶段
. Сначала вызывается функция-обработчик фазы захвата, затем вызывается функция-обработчик целевой фазы и, наконец, вызывается функция-обработчик фазы всплытия. (На картинке ниже нет тега html)
(1) Фаза захвата: события изwindow
Стадия, на которой объекты распространяются сверху вниз к целевому узлу;
(2) Целевой этап: этап, на котором реальный целевой узел обрабатывает событие;
(3) Фаза пузыря: событие идет снизу вверх целевого узла.window
Фаза распространения объекта.
Захват идет сверху вниз, события сначалаwindow
объект, а затемdocument
(объект), затемhtml
ярлык (черезdocument.documentElement
Получатьhtml
метка), затемbody
ярлык (черезdocument.body
Получатьbody
ярлык), затем следуйте обычнымhtml
Структура передается слой за слоем и, наконец, достигает целевого элемента.
Процесс всплытия событий — это просто процесс, обратный захвату событий. Далее, давайте рассмотрим пример всплытия событий:
// 例3
<div id="outer">
<div id="inner"></div>
</div>
......
window.onclick = function() {
console.log('window');
};
document.onclick = function() {
console.log('document');
};
document.documentElement.onclick = function() {
console.log('html');
};
document.body.onclick = function() {
console.log('body');
}
outer.onclick = function(ev) {
console.log('outer');
};
inner.onclick = function(ev) {
console.log('inner');
};
как мы упоминали нижеonclick
Методы привязки к поведению события элемента выполняются в фазе всплытия (или целевой фазе) поведения события текущего элемента.
Уровень события DOM
DOM
Всего существует четыре уровня:DOM0级
,DOM1级
,DOM2级
а такжеDOM3级
. а такжеDOM
События делятся на 3 уровня:DOM 0
обработка событий уровня,DOM 2
обработка событий уровня иDOM 3
обработка событий уровня. из-заDOM 1
В классе нет контента, связанного с событиями, поэтому нетDOM 1
событие уровня. и потому, чтоIE
и другие браузеры вDOM2
Обработка событий различается на уровне, поэтому методы обработки событий можно разделить на три категории, а именно:DOM0
,DOM2
,IE
. Ниже изDOM
уровень повышен
События уровня DOM 0
el.onclick=function(){}
var btn = document.getElementById('btn');
btn.onclick = function(){
alert(this.innerHTML);
}
Если вы хотите привязать несколько событий одного типа к одному и тому же элементу/метке (например, привязать 3 события щелчка к элементу btn выше), это не разрешено.. привязка события DOM0, метод привязки к поведению элемента,Эти методы выполняются в фазе всплытия (или целевой фазе) текущего поведения события элемента..
События DOM уровня 2
el.addEventListener(event-name, callback, useCapture)
- event-name: имя события, может быть стандартным событием DOM
- callback: функция обратного вызова, когда событие срабатывает, функция будет введена с параметром текущего объекта события event
- useCapture: значение по умолчанию — false, что означает, что дескриптор события выполняется на этапе всплытия (или событие всплытия зарегистрировано), а значение true означает, что дескриптор события выполняется на этапе захвата (или зарегистрированное событие является событием захвата). )
var btn = document.getElementById('btn');
btn.addEventListener("click", test, false);
function test(e){
e = e || window.event;
alert((e.target || e.srcElement).innerHTML);
btn.removeEventListener("click", test)
}
//IE9-:attachEvent()与detachEvent()。
//IE9+/chrom/FF:addEventListener()和removeEventListener()
Браузеры IE ниже IE9 не поддерживают addEventListener() и removeEventListener(), вместо этого используйте attachEvent() и detachEvent(), поскольку IE9 и ниже не поддерживают захват событий, поэтому третьего параметра перед именем первого события нет. . Это можно сделать для совместимости:
События DOM уровня 3
Добавлено больше типов событий поверх событий уровня DOM 2.
-
События пользовательского интерфейса, срабатывающие, когда пользователь взаимодействует с элементами на странице, например: загрузка, прокрутка
-
Событие фокуса, срабатывающее, когда элемент получает или теряет фокус, например: размытие, фокус
-
События мыши, срабатывающие, когда пользователь выполняет операцию на странице с помощью мыши, например: dblclick, mouseup
-
Событие колеса, срабатывающее при использовании колесика мыши или аналогичного устройства, например: колесико мыши
-
Текстовые события, инициируемые при вводе текста в документ, например: textInput
-
События клавиатуры, срабатывающие, когда пользователь выполняет операцию на странице с помощью клавиатуры, например: нажатие клавиши, нажатие клавиши
-
Событие композиции, инициируемое при вводе символов для IME (редактора метода ввода), например:
-
События изменения, запускаемые при изменении базовой структуры DOM, например: DOMsubtreeModified
-
В то же время события уровня DOM3 также позволяют пользователям настраивать некоторые события.
Суммировать:
-
Преимущество уровня DOM2 заключается в том, что можно добавить несколько обработчиков событий, DOM0 поддерживает только один обработчик для каждого события;
-
Анонимные функции, добавленные через DOM2, нельзя удалить,
addEventListener
а такжеremoveEventListener
изhandler
должно быть такое же имя -
Область применения: DOM0.
handler
будет работать в рамках элемента, которому он принадлежит, IEhandler
будет работать в глобальной области видимости,this === window
-
Порядок срабатывания: при добавлении нескольких событий DOM2 будет выполняться в порядке добавления, IE будет выполняться в обратном порядке, имейте в виду
Кроссбраузерные обработчики событий
совместимыйie9
следующие браузеры иDOM0
var EventUtil = {
// element是当前元素,可以通过getElementById(id)获取
// type 是事件类型,一般是click ,也有可能是鼠标、焦点、滚轮事件等等
// handle 事件处理函数
addHandler: (element, type, handler) => {
// 先检测是否存在DOM2级方法,再检测IE的方法,最后是DOM0级方法(一般不会到这)
if (element.addEventListener) {
// 第三个参数false表示冒泡阶段
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent(`on${type}`, handler)
} else {
element[`on${type}`] = handler;
}
},
removeHandler: (element, type, handler) => {
if (element.removeEventListener) {
// 第三个参数false表示冒泡阶段
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent(`on${type}`, handler)
} else {
element[`on${type}`] = null;
}
}
}
// 获取元素
var btn = document.getElementById('btn');
// 定义handler
var handler = function(e) {
console.log('我被点击了');
}
// 监听事件
EventUtil.addHandler(btn, 'click', handler);
// 移除事件监听
// EventUtil.removeHandler(button1, 'click', clickEvent);
агент событий
Поскольку событие будет распространяться до родительского узла на этапе всплытия, функция прослушивателя дочернего узла может быть определена на родительском узле, а функция прослушивателя родительского узла может единообразно обрабатывать события нескольких дочерних элементов. Этот метод называется делегированием событий (delegation), также известным как делегирование событий. Брокеры событий имеют следующие два преимущества:
- Сокращение потребления памяти и повышение производительности
Допустим есть список с большим количеством элементов списка, нам нужно отреагировать на событие при клике по каждому элементу списка
// 例4
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
<li>item n</li>
</ul>
Если вы привяжете функцию к каждому элементу списка, она будет потреблять много памяти и потреблять много производительности с точки зрения эффективности. С помощью прокси-сервера событий нам нужно только привязать методы к родительскому контейнеру ul, чтобы независимо от того, какой элемент-потомок был нажат, поведение щелчка контейнера будет запускаться в соответствии с механизмом передачи пузырькового распространения, а затем будет выполнен соответствующий метод.В зависимости от источника события мы можем знать, кто нажал, и делать разные вещи.
- Динамически связывать события
Во многих случаях нам нужно динамически добавлять или удалять элементы списка с помощью пользовательских операций.Если мы привязываем события к каждому дочернему элементу в начале, то при изменении списка нам нужно повторно привязывать события к вновь добавленным элементам, поэтому что элементы, которые собираются удалить, будут связаны с событиями.Элемент отвязывает событие, если вы используете прокси события, вы избавите себя от таких проблем.
Далее давайте реализуем делегирование события элемента li под родительским элементом #list в приведенном выше примере его родительскому элементу:
// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配目标元素
if (target.nodeName.toLocaleLowerCase === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
объект события
DOM0
а такжеDOM2
обработчики событий автоматически передаются вevent
объект, триггерDOM
Когда событие включено, будет сгенерирован объект события, который содержит всю информацию, связанную с событием.IE
серединаevent
Объект зависит от метода указанного обработчика событий
IE
handler
будет работать в глобальной области видимости,this === window
Итак, в IE будетwindow.event
,event
два случая
Также вIE
, свойства объекта события также отличаются, и соответствующая связь выглядит следующим образом:
srcElement
=> target
returnValue
=> preventDefault()
cancelBubble
=> stopPropagation()
IE
Захват событий не поддерживается, поэтому всплывающие сообщения о событиях можно только отменить, ноstopPropagation
Можно одновременно отменить захват событий и всплывающую подсказку
Только во время обработчиков событий,
event
Объект будет существовать только после того, как обработчик события завершит выполнение,event
объект будет уничтожен
1. event. preventDefault()
Если этот метод вызывается, поведение события по умолчанию больше не будет срабатывать.. Что такое событие по умолчанию? Например, когда форма нажимает кнопку отправки (отправить), чтобы перейти на страницу, страница тега a по умолчанию перескакивает или точка привязки позиционируется.
Много раз мы используем тег a просто для того, чтобы действовать как обычная кнопка, щелкнуть, чтобы выполнить функцию, не хотим переходить на страницу и не хотим позиционировать привязку.
//方法一:
<a href="javascript:;">链接</a>
Его также можно заблокировать с помощью метода JS и привязать метод к его событию щелчка.Когда мы нажимаем на тег A, сначала будет инициировано событие щелчка, а затем будет выполнено поведение по умолчанию.
//方法二:
<a id="test" href="http://www.cnblogs.com">链接</a>
<script>
test.onclick = function(e){
e = e || window.event;
return false;
}
</script>
//方法三:
<a id="test" href="http://www.cnblogs.com">链接</a>
<script>
test.onclick = function(e){
e = e || window.event;
e.preventDefault();
}
</script>
Далее рассмотрим пример: в поле ввода можно ввести только до шести символов, как этого добиться?
// 例5
<input type="text" id='tempInp'>
<script>
tempInp.onkeydown = function(ev) {
ev = ev || window.event;
let val = this.value.trim() //trim去除字符串首位空格(不兼容)
// this.value=this.value.replace(/^ +| +$/g,'') 兼容写法
let len = val.length
if (len >= 6) {
this.value = val.substr(0, 6);
//阻止默认行为去除特殊按键(DELETE\BACK-SPACE\方向键...)
let code = ev.which || ev.keyCode;
if (!/^(46|8|37|38|39|40)$/.test(code)) {
ev.preventDefault()
}
}
}
</script>
2. event.stopPropagation() & event.stopImmediatePropagation()
event.stopPropagation()
метод, чтобы предотвратить всплытие события к родительскому элементу, предотвращая выполнение любых обработчиков родительских событий (обычно мы думаемstopPropagation
Она используется для предотвращения всплытия событий, по сути, эта функция также может предотвращать захват событий). Упомянутая выше фаза всплытия события относится к событию снизу вверх целевого узла.window
Фаза распространения объекта. В нашем примере вышеinner
элементclick
мероприятие, добавитьevent.stopPropagation()
После этого предложения выполнение родительского события блокируется и, наконец, только печатается'inner'
.
inner.onclick = function(ev) {
console.log('inner');
ev.stopPropagation();
};
stopImmediatePropagation
Оба предотвращают всплытие события к родительскому элементу, а также предотвращают запуск других прослушивателей того же типа события для этого элемента. а такжеstopPropagation
Только прежний эффект может быть достигнут. Давайте посмотрим на пример:
<body>
<button id="btn">click me to stop propagation</button>
</body>
......
const btn = document.querySelector('#btn');
btn.addEventListener('click', event => {
console.log('btn click 1');
event.stopImmediatePropagation();
});
btn.addEventListener('click', event => {
console.log('btn click 2');
});
document.body.addEventListener('click', () => {
console.log('body click');
});
// btn click 1
Как показано выше, используйтеstopImmediatePropagation
После этого при нажатии кнопки не толькоbody
Событие привязки не срабатывает, и в то же время не срабатывает другое событие нажатия кнопки.
3. event.target & event.currentTarget
Честно говоря, разницу между ними нелегко описать словами.Давайте сначала рассмотрим пример:
<div id="a">
<div id="b">
<div id="c">
<div id="d"></div>
</div>
</div>
</div>
<script>
document.getElementById('a').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('b').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('c').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('d').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
</script>
Когда мы нажимаем на самый внутренний элемент d, он последовательно выводит:
target:d¤tTarget:d
target:d¤tTarget:c
target:d¤tTarget:b
target:d¤tTarget:a
Из вывода мы видим,event.target
указывает на элемент, вызвавший инициирующее событие, иevent.currentTarget
Это элемент, привязанный к событию, только целевой элемент, на который нажали.event.target
будет равноevent.currentTarget
.То есть Event.currentTarget всегда прослушивает событие, а event.target является истинным генератором события..
4. Кроссбраузерные объекты событий
var EventUtil = {
addHandler: function (el, type, handler) {
if (el.addEventListener) {
el.addEventListener(type, handler, false);
} else if (el.attachEvent) {
el.attachEvent('on' + type, handler);
} else {
el['on' + type] = handler;
}
},
removeHandler: function (el, type, handler) {
if (el.removeEventListener) {
el.removeEventListerner(type, handler, false);
} else if (el.detachEvent) {
el.detachEvent('on' + type, handler);
} else {
el['on' + type] = null;
}
},
getEvent: function (e) {
return e ? e : window.event;
},
getTarget: function (e) {
return e.target ? e.target : e.srcElement;
},
preventDefault: function (e) {
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
},
stopPropagation: function (e) {
if (e.stopPropagation) {
e.stopPropagation();
} else {
e.cancelBubble = true;
}
}
};
Порядок захвата и барботирования
При наличии нескольких вложенных взаимодействий порядок захвата и всплытия событий кажется неопределенным. Далее будет рассмотрен их порядок в 5 случаях и как избежать непредвиденных ситуаций.
1. Зарегистрируйте событие во внешнем div и щелкните внутренний div, чтобы вызвать событие, событие захвата всегда запускается до события всплытия (это не имеет ничего общего с порядком кода).
Допустим, есть такая html-структура:
<div id="test" class="test">
<div id="testInner" class="test-inner"></div>
</div>
Затем мы регистрируем два события щелчка на внешнем div, а именно событие захвата и событие всплытия Код выглядит следующим образом:
const btn = document.getElementById("test");
//捕获事件
btn.addEventListener("click", function(e){
alert("capture is ok");
}, true);
//冒泡事件
btn.addEventListener("click", function(e){
alert("bubble is ok");
}, false);
Нажмите на внутренний div, сначала захват всплывающего окна в порядке, затем всплывающее окно в порядке. Только когда элемент DOM, который фактически инициирует событие, является внутренним слоем, внешний элемент DOM имеет возможность имитировать событие захвата и событие всплытия.
2. При регистрации события в элементе DOM, который запускает событие, сначала будет выполнено то, что зарегистрировано первым.
Структура html такая же, как и выше, а код js выглядит следующим образом:
const btnInner = document.getElementById("testInner");
//冒泡事件
btnInner.addEventListener("click", function(e){
alert("bubble is ok");
}, false);
//捕获事件
btnInner.addEventListener("click", function(e){
alert("capture is ok");
}, true);
В этом примере всплывающее событие регистрируется первым, поэтому оно выполняется первым. Итак, нажмите на внутренний div и сначала всплывайтеbubble is ok
, затем всплывающее окноcapture is ok
.
3. Когда событие захвата зарегистрировано как для внешнего div, так и для внутреннего div, при нажатии внутреннего div событие внешнего div будет запущено первым.
const btn = document.getElementById("test");
const btnInner = document.getElementById("testInner");
btnInner.addEventListener("click", function(e){
alert("inner capture is ok");
}, true);
btn.addEventListener("click", function(e){
alert("outer capture is ok");
}, true);
Хотя событие внешнего div регистрируется позже, оно сработает первым. Итак, результат должен появиться первымouter capture is ok
, затем всплывающее окноinner capture is ok
.
4. Точно так же, когда и внешний div, и внутренний div регистрируют всплывающее событие одновременно, при щелчке внутреннего div событие внутреннего div должно запускаться первым.
const btn = document.getElementById("test");
const btnInner = document.getElementById("testInner");
btn.addEventListener("click", function(e){
alert("outer bubble is ok");
}, false);
btnInner.addEventListener("click", function(e){
alert("inner bubble is ok");
}, false);
всплывать первымinner bubble is ok
, затем всплывающее окноouter bubble is ok
.
5. Предотвратить отправку событий
Обычно мы все хотим нажать наdiv
, он запускает только свой собственный обратный вызов события. Например, четкое нажатие на внутренний слойdiv
, но внешнийdiv
Событие также срабатывает, а это не то, что нам нужно. В это время необходимо предотвратить отправку события.
Когда событие запускается, оно проходит по умолчаниюevent
объект, этоevent
На объекте есть метод:stopPropagation
. Объяснение на MDN:Предотвращение дальнейшего распространения текущего события на этапах захвата и всплытия.. Итак, по этому методу пусть внешний слойdiv
Если событие не получено, оно, естественно, не сработает.
btnInner.addEventListener("click", function(e){
//阻止冒泡
e.stopPropagation();
alert("inner bubble is ok");
}, false);
Справочная статья
- Расширенное программирование на JavaScript (третье издание)
- Разница между event.target и event.currentTarget
- JS-события: захват и всплытие, обработчики событий, объекты событий, кроссбраузерность, делегирование событий
- Поток событий JavaScript
- Механизм событий «Вопросы для интервью, серия 7» в JavaScript (от нативного к фреймворку)
- Механизм событий DOM