事件
事件是 JS 和 HTML 交互的桥梁。采用“观察者模式”,使用仅在事件发生时执行的监听器(也叫处理程序)订阅事件
事件流
事件流描述的是页面接收事件的顺序。分为 3 各阶段:
- 事件捕获:最先触发,可以做事件拦截。
- 到达事件目标:触发目标事件
- 事件冒泡:可以拦截事件响应
如下代码的事件流为
<body>
<div id="test" onclick="console.log(event)"></div>
</body>
事件处理程序
事件处理程序(或事件监听器):为响应事件而调用的函数,以"on"开头。
函数体在 HTML 中
如果事件处理程序很简单,那么可以直接写在 HTML 中,但是要注意不能在未经转义的情况下使用 HTML 语法字符,例如和号(&)、双引号(")、小于号(<)和大于号(>)。
<div onclick="console.log('Clicked')"></div>
函数有一个特殊的局部变量 event (事件对象),它保存有事件的一些关键信息,后边会重点介绍。以这种方式定义的事件处理程序中的 this 指向事件的目标元素,并且可以更方便地访问自己的属性。
<div onclick="console.log(event)"></div>
<div onclick="console.log(this)"></div>
<div id="test" onclick="console.log(id)"></div>
以这种方式添加事件处理程序会导致 HTML 和 JS 的强耦合,不利于后期维护。
DOM 绑定事件
如下这样的方式为事件处理程序赋值时,事件处理程序会在元素的作用域中运行,所以 this 指向目标元素本身。这种方式添加事件处理程序是注册在事件流的冒泡阶段触发。
直接赋值
<div id="test"></div>
<script>
let oDiv = document.getElementById('test');
oDiv.onclick = function (event) {
console.log(event, this)
}
</script>
移除事件处理程序只需要将处理程序属性值设为 null
<div id="test"></div>
<script>
let oDiv = document.getElementById('test');
oDiv.onclick = function (event) {
console.log(event, this)
}
// 移除事件处理程序
oDiv.onclick = null;
</script>
addEventListener & removeEventListener
addEventListener 接收 3 个参数:
- 事件名
- 事件处理函数
- 一个布尔值,true 表示在捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事件处理程序。
使用 addEventListener 可以为同一个事件添加多个事件处理程序,以添加顺序来触发。
<div id="test"></div>
<script>
let oDiv = document.getElementById('test');
oDiv.addEventListener('click', function (event) { console.log(event, this) }, true)
oDiv.addEventListener('click', function () { console.log(this) }, true)
</script>
通过 addEventListener 添加的事件处理程序只能使用 removeEventListener 来移除。接收三个参数:
- 事件名
- 添加事件时的事件处理程序
- 同添加事件时一样
<div id="test"></div>
<script>
let oDiv = document.getElementById('test');
function click(event) {
console.log(event)
}
oDiv.addEventListener('click', click, true);
oDiv.removeEventListener('click', click, true);
</script>
注意:不要使用 addEventListener 方法添加匿名的事件处理程序,因为这样无法移除。
事件对象
DOM 事件触发时,所有相关信息都会被收集并存储在 event 的对象中。事件对象包含事件相关的属性和方法。不同的事件生成的事件对象也会不同。
注意 event 对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁。
每个事件对象都包含 currentTarget 和 target 属性。currentTarget 属性是事件绑定的元素,target 是事件的真正目标。这两个属性比较重要。
下面的例子,事件绑定在 body 身上,但是我点击的是 div,所以 currentTarget 为 body 元素对象,target 为 div 元素对象。
<div id="test"></div>
<script>
function click(event) {
console.log(event.currentTarget, event.target);
}
document.body.addEventListener('click', click);
</script>
preventDefault 方法用于阻止特定事件的默认行为。比如 a 标签的默认行为是点击打开 href 链接,但是如果添加如下方法就能阻止打开链接。
let oA = document.getElementById("myLink");
oA.onclick = function(event) {
event.preventDefault();
};
stopPropagation 方法用于取消后续的事件捕获或冒泡。如下例子,在 div 绑定的事件中调用了stopPropagation ,那么就不会再继续冒泡了,body 的点击事件自然不会触发。
<div id="test"></div>
<script>
let oDiv = document.getElementById('test');
oDiv.onclick = function (event) {
console.log("div Clicked");
event.stopPropagation();
}
document.body.onclick = function (event) {
console.log("Body clicked");
};
</script>
事件委托
设想一种情况,当有一个 ul 标签,其内有许多 li 标签,我需要在点击 li 标签是能够做一些处理,最简单的方法就是给每一个 li 标签都绑定一个点击事件,但是这就带来一个问题,由于每个函数都是一个对象,那么对象越多占用内存就越多,性能就越差。为了解决这种情况就提出了事件委托。
事件委托就是把子元素的事件处理程序绑定在父元素上,利用事件冒泡来触发事件。如下例:
<div id="test">
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
</div>
<script>
let oDiv = document.getElementById('test');
oDiv.onclick = function (event) {
let target = event.target;
switch (target.id) {
case "1":
console.log(target.id + 1);
break;
case "2":
console.log(target.id + 2);
break;
case "3":
console.log(target.id + 3);
break;
}
}
</script>
我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,欢迎你的关注。