В предыдущей статье "JS Knowledge Point Hodgepodge" мы говорили о потоке событий, но не вдавали его в подробности. В этой статье мы познакомимся с потоком событий.
Поток событий состоит из трех этапов:
1.捕获阶段
2.目标阶段
3.冒泡阶段
Как мы все знаем о привязке событий, есть уровень DOM0 (on+type) и уровень DOM2 (addEventListener). Я думаю, что сложно понять столько концепций. Просто посмотрите на код. Для удобства я буду напрямую использовать id для получить элементы.
- Уровень DOM0
<div id="box1"></div>
box1.onclick = function(){
console.log('box1');
}
выходbox1Мы все это знаем, давайте посмотрим.
<div id="box1"></div>
box1.onclick = function(){
console.log('box1');
}
box1.onclick = function(){
console.log('box1 two');
}
выходbox1 two, так как уровень DOM0 перезапишет предыдущую привязку того же элемента, давайте посмотрим.
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
box1.onclick = function(){
console.log('box1');
}
box2.onclick = function(){
console.log('box2');
}
box3.onclick = function(){
console.log('box3');
}
Когда мы нажимаем на box1, мы знаем вывод box1, но что появляется, когда мы нажимаем на box3?
Вам может быть интересно, почему поле 3, на которое я нажимаю, срабатывает на другом? потому чтовсплывающее окно события. Так что же такое пузырьковое событие? Пожалуйста, обратитесь к Baidu за концепцией, прямо на карте.
Это называется всплытием событий, и оно всплывает уровень за уровнем, пока окно здесь я не нарисовал. Уровень DOM0 поддерживает только фазу всплытия.
- ДОМА уровень 2
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
box1.addEventListener('click', function(){
console.log('box1');
},false);
box2.addEventListener('click', function(){
console.log('box2');
},false);
box3.addEventListener('click', function(){
console.log('box3');
},false);
Результат такой же, как и выше, потому что мы связаны фазой всплытия. (истинный захват, ложный пузырь). Давайте посмотрим, как выглядит этап захвата.
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
box1.addEventListener('click', function(){
console.log('box1');
},true);
box2.addEventListener('click', function(){
console.log('box2');
},true);
box3.addEventListener('click', function(){
console.log('box3');
},true);
Мы нажимаем на box3, чтобы увидетьВы можете обнаружить, что порядок обратный, так почему же это так? потому чтозахват событий, что такое захват времени? Пожалуйста, обратитесь к Baidu за концепцией, прямо на карте.
Это фаза захвата, полная противоположность фазе пузыря.
Каков порядок выполнения всплытия и захвата? Я навязал одно и то же событие для обеих фаз на каждый элемент отдельно, посмотрим порядок срабатывания.
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
box1.addEventListener('click', function(){
console.log('box1 捕获阶段');
},true);
box2.addEventListener('click', function(){
console.log('box2 捕获阶段');
},true);
box3.addEventListener('click', function(){
console.log('box3 捕获阶段');
},true);
box1.addEventListener('click', function(){
console.log('box1 冒泡阶段');
},false);
box2.addEventListener('click', function(){
console.log('box2 冒泡阶段');
},false);
box3.addEventListener('click', function(){
console.log('box3 冒泡阶段');
},false);
Мы нажимаем на поле 3, чтобы увидеть, что оно сначала захвачено, а затем всплывает.Это все равно? Давайте немного изменим его.
box1.addEventListener('click', function(){
console.log('box1 捕获阶段');
},true);
box2.addEventListener('click', function(){
console.log('box2 捕获阶段');
},true);
box1.addEventListener('click', function(){
console.log('box1 冒泡阶段');
},false);
box2.addEventListener('click', function(){
console.log('box2 冒泡阶段');
},false);
box3.addEventListener('click', function(){
console.log('box3 冒泡阶段');
},false);
box3.addEventListener('click', function(){
console.log('box3 捕获阶段');
},true); // 将box3的捕获阶段放到box3的冒泡阶段后面
Посмотрите, осталась ли последовательность запуска прежней?
Оказывается, все наоборот, на самом деле это называетсяцелевой этапБар. Захват пузыря неразличим на целевом элементе, где вы запускаете событие, в том порядке, в котором оно было привязано.
Давайте посмотрим на это с картинкой.
Это не слишком просто, давайте немного усложним.
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
box1.addEventListener('click', function(){
console.log('box1 捕获阶段');
},true);
box2.addEventListener('click', function(){
console.log('box2 捕获阶段');
},true);
box3.addEventListener('click', function(){
console.log('box3 捕获阶段');
},true);
box1.addEventListener('click', function(){
console.log('box1 冒泡阶段');
},false);
box2.addEventListener('click', function(){
console.log('box2 冒泡阶段');
},false);
box3.addEventListener('click', function(){
console.log('box3 冒泡阶段');
},false);
box1.onclick = function () {
console.log('box1 51561');
}
box2.onclick = function () {
console.log('box2');
}
box3.onclick = function () {
console.log('box3');
}
box1.onclick = function () {
console.log('box1');
}
Как выглядит триггерная последовательность? (Я думаю, вам лучше сначала написать ответ самому)
посмотри, правильно ли ты понял
Это не будет слишком просто, изменить заказ
box1.onclick = function () {
console.log('box1 51561');
}
box2.onclick = function () {
console.log('box2');
}
box3.onclick = function () {
console.log('box3');
}
box1.onclick = function () {
console.log('box1');
}
box1.addEventListener('click', function(){
console.log('box1 捕获阶段');
},true);
box2.addEventListener('click', function(){
console.log('box2 捕获阶段');
},true);
box1.addEventListener('click', function(){
console.log('box1 冒泡阶段');
},false);
box2.addEventListener('click', function(){
console.log('box2 冒泡阶段');
},false);
box3.addEventListener('click', function(){
console.log('box3 冒泡阶段');
},false);
box3.addEventListener('click', function(){
console.log('box3 捕获阶段');
},true);
Это правильно?
- Поговорим о пузырении и ловле
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
box1.onclick = function () {
console.log('box1 51561');
}
box2.onclick = function () {
console.log('box2');
}
box3.onclick = function () {
console.log('box3');
}
box1.onclick = function () {
console.log('box1');
}
box1.addEventListener('click', function(){
console.log('box1 捕获阶段');
},true);
box2.addEventListener('click', function(){
console.log('box2 捕获阶段');
},true);
box1.addEventListener('click', function(){
console.log('box1 冒泡阶段');
},false);
box2.addEventListener('click', function(){
console.log('box2 冒泡阶段');
},false);
box3.addEventListener('click', function(){
console.log('box3 冒泡阶段');
},false);
box3.addEventListener('click', function(){
console.log('box3 捕获阶段');
},true);
Как вы думаете, что выдаст этот щелчок? Вы колеблетесь? Это значит, что вы до сих пор не понимаете бульканье и захват.
Не позволяйте тому, что вы видите, одурачить вас, пузырьковое отображение — это отношение родитель-потомок структуры DOM, а не отношение, которое выглядит как пакет. (тот же ответ, что и выше).
- Забудьте об IE, давайте поговорим о механизме событий IE.
IE не поддерживает addEventListener, но имеет attachEvent
box1.onclick = function () {
console.log('box1 51561');
}
box2.onclick = function () {
console.log('box2');
}
box3.onclick = function () {
console.log('box3');
}
box1.onclick = function () {
console.log('box1');
}
box1.attachEvent('onclick', function (){
console.log('box1 attachEvent')
})
box2.attachEvent('onclick', function (){
console.log('box2 attachEvent')
})
box3.attachEvent('onclick', function (){
console.log('box3 attachEvent')
})
box1.attachEvent('onclick', function (){
console.log('box1')
})
box1.attachEvent('onclick', function (){
console.log('box2')
})
box1.attachEvent('onclick', function (){
console.log('box3')
})
Что это выведет? (подсказка: не перезапишется) Ответ: коробка1 коробка2 коробка3 Ха-ха, шучу.
Не странно ли, что событие в IE сначала связывается, а потом выводится. IE6, 7, 8 не поддерживают захват событий, поддерживают только всплывающие сообщения.
- Совместимость с предотвращением всплытия событий
Сначала поговорим о событиях, не поддерживающих всплытие: blur, focus, mouseenter, mouseleave. (Я знаю это) Снова на этом примере давайте посмотрим на предотвращение всплытия.
box1.onclick = function (){
console.log('box1')
}
box2.onclick = function (){
console.log('box2')
}
box3.onclick = function (e){
e.stopPropagation();
console.log('box3')
}
Выводится только box3. Хотя он предотвращает всплытие, его непросто использовать в IE 8 и ниже.Давайте рассмотрим совместимый метод записи.
function stopPropagate(e){
var event = e || window.event;
if(event.stopPropagation){
event.stopPropagation();
}else if(event.cancelBubble){ //IE
event.cancelBubble = true;
}
}
Предотвратить совместимость событий по умолчанию
function preventDef(e){
var g = e || window.event;
if(g.preventDefault){
g.preventDefault();
}else if(g.returnValue){
g.returnValue = false;
}
return false;
}
Давайте посмотрим на указатель this привязки события.
box1.onclick = function (){
console.log('onclick', this);
}
box1.addEventListener('click',function () {
console.log('addEventListener', this);
}, false)
IE6, 7, 8, привязка события this указывает на
box1.attachEvent('onclick',function () {
console.log('attachEvent', this);
})
attachEvent [object Window]
Мы обнаружили, что в IE6, 7, 8 this указывает на window
- Расширенное делегирование мероприятия
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Если бы нам нужно было следить за поведением каждого ли, вы бы сделали это?
let li = document.getElementsByTagName("li");
for (var i = 0; i < li.length; i++) {
li.onclick = ()=>{
console.log(i);
}
}
Большой парень в области комментариев указал на мою ошибку, потому что это формирует закрытие и не может получить результат вывода. Я хотел бы принести свои искренние извинения читателям, потому что я хотел только продемонстрировать делегирование мероприятия и не заметил этой ситуации. Следующее после изменений.Причина, по которой я не исправляю ошибки, состоит в том, чтобы предупредить себя и других, которые сделали те же ошибки, что и я.
let li = document.getElementsByTagName("li");
//ES6
for (let i = 0; i < li.length; i++) {
li[i].onclick = ()=>{
console.log(i);
}
}
//IIFE(1)
for (var i = 0; i < li.length; i++) {
(function(j){
li[j].onclick = ()=>{
console.log(j);
}})(i)
}
//IIFE(2)
for (var i = 0; i < li.length; i++) {
li[i].onclick = (function(j){
return ()=>{
console.log(j);
}
})(i);
}
Делать это правильно недостаточно.Если вы добавите еще несколько ли или у вас будет много 100|1000 ли, вам будет плохо, если вы все еще будете это делать. Нам нужно зарегистрировать событие для каждого li, не говоря уже о том, что регистрировать много событий нехорошо. Итак, что мы делаем, используяделегация мероприятия, состоит в том, чтобы делегировать ваши события другим (родителям), использовать всплывающую подсказку событий и указывать только один обработчик событий для управления всеми событиями определенного типа.
Давайте взглянем.
ul.onclick = function (e){
console.log(e.target);
}
Это делегирование событий.
- преимущество:
- Это может сэкономить много памяти и уменьшить количество регистраций событий.
- Можно реализовать, что при добавлении дочернего объекта для него не требуется привязка событий, что особенно подходит для динамического контента.
- недостаток:
- Обычное применение прокси событий должно быть ограничено вышеуказанными требованиями.Если все события используются в качестве прокси событий, может произойти ошибочная оценка событий. То есть события, которые не должны запускаться, привязываются к событиям.
Исправление:
существуетChrome 89.0.4363.0и в более поздних версияхПорядок срабатывания событий для целевых элементов больше не срабатывает в том порядке, в котором они были зарегистрированы! Вместо этого он выполняется последовательно в виде сначала захвата, а затем всплытия!