一、 学习Javascript或者说是前端、那么学习和理解事件是非常重要的。因为我们写的js代码和html网页要进行交互、而交互的方式就是事件。
事件代表文档或浏览器窗口中某个有意义的时刻。可以使用仅在事件发生时执行的监听器(也叫处理程序)订阅事件。在传统软件工程领域,这个模型叫“观察者模式”,其能够做到页面行为(在 JavaScript 中定义)与页面展示(在 HTML 和 CSS 中定义)的分离。
浏览器的事情系统是比较复杂的、不同的浏览器对于不同的事件api有着不同的兼容。
二、事件流
在第四代 Web 浏览器(IE4 和 Netscape Communicator 4)开始开发时,两家开发团队碰到了一个有意思的问题:页面哪个部分拥有特定的事件呢?要理解这个问题,可以在一张纸上画几个同心圆。把手指放到圆心上,则手指不仅是在一个圆圈里,而且是在所有的圆圈里。两家浏览器的开发团队都是以同样的方式看待浏览器事件的。当你点击一个按钮时,实际上不光点击了这个按钮,还点击了它的容器以及整个页面。
事件流代表的是页面接收事件的顺序、IE 和 Netscape 开发团队提出了几乎完全相
反的事件流方案。IE 将支持事件冒泡流,而 Netscape Communicator 将支持事件捕获流。
1.事件冒泡:IE 事件流被称为事件冒泡,这是因为事件被定义为从最具体的元素(文档树中最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)。
2.事件捕获:。事件捕获的意思是最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。但事件捕获得到了所有现代浏览器的支持。实际上,所有浏览器都是从 window 对象开始捕获事件,而 DOM2 Events规范规定的是从 document 开始。由于旧版本浏览器不支持,因此实际当中几乎不会使用事件捕获。通常建议使用事件冒泡,特殊情况下可以使用事件捕获。
三 、事件流的阶段
DOM2 Events 规范规定事件流分为 3 个阶段:事件捕获、到达目标和事件冒泡。事件捕获最先发生,
为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡,最迟要在这个
阶段响应事件。所有现代浏览器都支持 DOM 事件流,只有 IE8 及更早版本不支持。
四 、事件处理程序
事情处理程序代表的是我们于网页交互的一个动作、我们为了响应这个动作必须调用一个函数、而这个 函数就是事件处理程序函数、事件处理程序函数以on开头、比如点击事件的事件 onclick。
绑定事件处理程序的方式主要有三种:
- html事件处理程序
<button onclick="fn()">点击</button>
<script>
function fn(){
console.log("html事件处理程序模式");
}
- Dom0事件处理程序
在 JavaScript 中指定事件处理程序的传统方式是把一个函数赋值给(DOM 元素的)一个事件处理程
序属性。这也是在第四代 Web 浏览器中开始支持的事件处理程序赋值方法,直到现在所有现代浏览器
仍然都支持此方法,主要原因是简单。要使用 JavaScript 指定事件处理程序,必须先取得要操作对象的
引用。
let btn = document.getElementsByTagName("button");
console.log(btn);
btn[0].onclick = function() {
console.log("Dom0事件处理程序模式");
};
3 .Dom2级事件处理程序
DOM2 Events 为事件处理程序的赋值和移除定义了两个方法:addEventListener()和 removeEventListener()。这两个方法暴露在所有 DOM 节点上,它们接收 3 个参数:事件名、事件处理函数和一个布尔值,true 表示在捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事
件处理程序
let btn = document.getElementsByTagName("button");
console.log(btn);
btn[0].addEventListener("click", () => {
console.log('dom2级事件处理程序');
}, true);
btn[0].addEventListener("click", () => {
console.log('dom2级事件处理程序再次被触发');
}, true);
移除dom事件监听器
let btn = document.getElementsByTagName("button");
console.log(btn);
function fn() {
console.log("dom2事件");
}
btn[0].addEventListener("click", fn, false);
btn[0].removeEventListener("click", fn, false);
五、事件对象
在 DOM 中发生事件时,所有相关信息都会被收集并存储在一个名为 event 的对象中。这个对象包
含了一些基本信息,比如导致事件的元素、发生的事件类型,以及可能与特定事件相关的任何其他数据。例如,鼠标操作导致的事件会生成鼠标位置信息,而键盘操作导致的事件会生成与被按下的键有关的信息。所有浏览器都支持这个 event 对象,尽管支持方式不同。
<button>点击</button>
<script>
let btn = document.getElementsByTagName("button");
console.log(btn);
btn[0].onclick=function(e){
console.log(`打印事件对象:${e}`); //如下图
}
</script>
在事件处理程序内部,this 对象始终等于 currentTarget 的值,而 target 只包含事件的实际目标。如果事件处理程序直接添加在了意图的目标,则 this、currentTarget 和 target 的值是一样的。下面的例子展示了这两个属性都等于 this 的情形:
<button id="btn">点击</button>
<script>
let btn = document.getElementById("btn");
btn.onclick = function (e) {
console.log(e);
console.log(this); //当前目标元素
console.log(e.currentTarget); //当前目标元素
console.log(e.target); //目标元素
};
</script>
let btn = document.getElementById("btn");
document.body.onclick = function (e) {
console.log(e);
console.log(this); //当前目标元素 body
console.log(e.currentTarget); //当前目标元素body
console.log(e.target); //目标元素target //btn
};
这段代码实际this指的是body因为它是当前的目标元素、而btn也能响应事件就是因为事件冒泡。
<div id="div">
<button id="btn">点击</button>
</div>
<script>
let div = document.getElementById("div");
//事件绑定在了div而button元素基于事件冒泡也能响应事件
div.onclick = function (e) {
console.log(e);
console.log(this); //当前目标元素div
console.log(e.currentTarget); //当前目标元素div
console.log(e.target); //目标元素target btn
};
</script>
下面代码阻止了事件流的传递导致后面的打印不会输出
<div id="div">
<button id="btn">点击</button>
</div>
<script>
let btn = document.getElementById("btn");
btn.onclick = function (event) {
event.stopPropagation(); //阻止了事件流的传递、body里面的打印不会输出
console.log("Clicked");
};
document.body.onclick = function (event) {
console.log("Body clicked");
};
事件的冒泡和捕获:
我们可以利用e.stopPagation()来阻止事件流的传递。
let btn = document.getElementById("btn");
let div=document.getElementById('div')
document.body.addEventListener('click',(e)=>{
console.log('222222');
console.log(e.eventPhase);
},false)
div.addEventListener('click',(e)=>{
e.stopPropagation()
console.log('22222dwad2');
console.log(e.eventPhase);
},true)
六、事件委托
<ul id="ul">
<li id="a">1</li>
<li id="b">2</li>
<li id="c">3</li>
</ul>
</div>
<script>
let ul=document.getElementById('ul')
ul.onclick=function(e){
switch(e.target.id){
case "a" :
console.log("aaaaaaa");
return;
case "b" :
console.log("bbbbbbb");
return;
case "c" :
console.log("Cccc");
return;
default :55555555
}
}
</script>
上面代码其实就是用了事件委托的方式、我们直接给UL绑定事件、去点击每一个li都会触发冒泡事件。这样我们就不必给每一个li去绑定事件处理函数。
七、删除事件处理程序
把事件处理程序指定给元素后,在浏览器代码和负责页面交互的JavaScript 代码之间就建立了联系。这种联系建立得越多,页面性能就越差。除了通过事件委托来限制这种连接之外,还应该及时删除不用的事件处理程序。很多 Web 应用性能不佳都是由于无用的事件处理程序长驻内存导致的。