一、事件
事件: 在HTML文档或浏览器发送的一种交互操作,使得网页具有互动性,常见的有加载、鼠标事件
事件处理函数: 当事件发生时,我们希望自动调用执行的函数
二、事件流
因为交互会影响到DOM
,它是一个树状结构,触发子节点的事件时,常常涉及到调用的顺序问题,这就引出了事件流。
2.1 三个阶段
事件传播的整个过程分为3个阶段:
- 事件捕获阶段
先由文档的根节点document往事件触发对象,从外向内捕获事件对象 - 目标阶段(目标对象本身的事件程序)
到达目标事件位置,触发事件本身的程序内容; - 事件冒泡阶段
再从目标事件位置往文档的根节点方向回溯,从内向外冒泡事件对象。
2.2 过程
特点:从document对象开始,最后返回document对象结束。
本质上,事件首先向下遍历父元素,直到到达目标元素(捕获阶段)为止。
当事件到达目标时,它将在那儿触发(目标阶段)。
然后返回链上(冒泡阶段),并一路调用处理程序
例如,如果您单击<div>
中的<button>
,则相应的<button>
标记将成为目标。 该元素可以作为event.target
进行访问,并且在事件传播的整个阶段都不会更改。
蓝色箭头代表捕获过程,紫色箭头代表冒泡过程:
2.3 示例
示例: 点内层元素,也触发父元素上的事件处理函数:
<div class="div-1">div-1
<div class="div-2">div-2
<div class="div-3">div-3
<div class="div-4">div-4
<div class="div-5">div-5</div>
</div>
</div>
</div>
</div>
<p id="show"></p>
<script>
function bubble() {
var div = document.getElementsByTagName('div');
var show = document.getElementById("show");
for (var i = 0; i < div.length; ++i) { //遍历div元素
div[i].onclick = (function(i) { //为每个div元素注册鼠标单击事件处理函数
return function() { //返回闭包函数
div[i].style.border = '1px dashed red'; //定义当前元素的边框线为红色虚线
show.innerHTML += div[i].className + " > "; //标识每个div元素的响应顺序:div5>div4>div3>div2>div1
}
})(i);
}
}
window.onload = bubble;
</script>
三、事件绑定
3.1 概念
a. 每个元素上都有一批on开头的事件属性
b. 事件绑定就是提前将事件处理函数保存到元素的事件属性上。
c. 结果: 当事件发生时
1). 浏览器先找到元素身上的对应事件属性
2). 再自动调用事件属性中保存的事件处理函数
3.2 事件模型
a. 在HTML中手动绑定
<button onclick="fun()">
b. 在js中以赋值方式绑定
- 查找触发事件的元素对象
var button= document.getElementById('.button');
- 绑定事件
button.onclick = fun;
缺点: 无法同时绑定多个事件处理函数,后赋值的事件处理函数会把原事件处理函数覆盖。
c. 添加事件监听对象的方式
将会经历事件流的三个过程(详见“二”)
- 查找触发事件的元素
- 添加事件监听
元素对象.addEventListener(eventType, handler, useCapture)
- 移除事件监听
元素对象.removeEventListener(eventType, handler, useCapture)
tip:
1.添加事件监听时,事件名称不要加on前缀
其实,DOM标准中事件名本来是没有on的!
比如: click, blur, change
元素对象中为了将事件属性和其它普通属性区分开,便于识别,才在事件属性名前加on。2.添加事件监听对象时,handler事件处理结尾不要加()!
因为事件处理函数都不是立刻执行!而是将来某个时间,触发事件时,才自动调用执行。
addEventListener可以在一个DOM添加多个事件处理,各自不冲突
示例:
<button id="bonbon">bonbon</button>
<button id="btnGrandBonbon">升级大棒棒糖</button>
<script>
var btnBonbon = document.getElementById("btnBonbon");
var btnGrandBonbon = document.getElementById("btnGrandBonbon");
var grandBonbon = () => console.log('大棒棒糖*1')
btnBonbon.onclick = () => console.log('棒棒糖*1')
//点btnGrandBonbon插件后,之后点击btnBonbon获得大棒棒糖
btnGrandBonbon.onclick = function () {
btnBonbon.addEventListener("click",grandBonbon)
}
</script>
3.3 特性
-
浏览器中有一个巨大的事件监听对象队列
-
addEventListener
做了2件事:
i. 新建一个事件监听对象,其中保存:元素对象、事件名、事件处理函数
ii. 自动将事件监听对象添加到事件监听对象队列中。 -
重复执行几次
addEventListener()
就会反复添加几个重复的事件监听对象 -
调用顺序:
i. 先查找元素对象自己身上的事件属性中保存的事件处理函数,优先自动执行.
ii. 再去事件监听对象队列中遍历查找所有符合要求(元素相同,事件名也相同)的事件监听对象,找到几个,就触发几个事件处理函数。
四、事件对象
4.1 概念
事件对象:当事件发生时,浏览器自动创建保存事件相关信息的对象。
使用场景:
a. 想获得事件相关的信息时
b. 想改变事件默认的行为
对象作为事件处理函数的第一个实参值,自动传入
元素对象.on事件名=function( e ){ ... }
或
元素对象.addEventListener("事件名",function(e){ ... })
4.2 方法
a. 停止冒泡: e.stopPropagation()
如果我们在使用的时候不想使用冒泡事件模型,
在回调函数中传入事件对象e,并使用e.stopPropagation();
方法即可
<script>
function bubble() {
var div = document.getElementsByTagName('div');
var show = document.getElementById("show");
for (var i = 0; i < div.length; ++i) { //遍历div元素
div[i].onclick = (function(i) { //为每个div元素注册鼠标单击事件处理函数
return function(e) {//返回闭包函数,并传入事件对象e
div[i].style.border = '1px dashed red'; //定义当前元素的边框线为红色
show.innerHTML += div[i].className + " > \n" ;
e.stopPropagation();
}
})(i);
}
}
window.onload = bubble;
</script>
b. 利用冒泡/事件委托
浏览器触发事件:遍历事件监听对象队列形式查找事件监听对象。
如果添加的事件监听对象越多,遍历时间越长,事件响应的速度越慢
优化:应在保持功能的情况下尽量减少事件绑定的次数
步骤:
-
当多个子元素都要绑定相同的事件处理函数时,只要给父元素绑定一个事件处理函数即可
所有子元素都可通过冒泡的方式触发父元素上统一的事件处理函数——共用!
缺点:
事件处理函数绑定在父元素上的,触发也是在父元素上触发的,this->父元素,不再指子元素。
再使用this,就无法获得实际点击的子元素了。 -
只要使用事件委托,都要用
e.target
代替this
e.target
: 是事件对象中专门保存最初点击的那个目标元素的特殊属性。
优点: 不随冒泡而改变 -
先验证当前点击的目标元素的特征,只有目标元素的特征符合要求,才继续执行后续操作,否则不执行操作
一切目标元素与其他元素不同的特征,都可作为判断依据
.className,.nodeName,.name, ... ...
示例:
<ul id="list">
<li>列表项目1</li>
<li>列表项目2</li>
<li>列表项目3</li>
</ul>
<script>
var ul = document.getElementById("list");
ul.addEventListener('click',function(e) {
// 兼容处理
var e = e || window.event;
var target = e.target || e.srcElement;
/* 判断目标事件是否为li*/
if (target.nodeName.toUpperCase() === "LI") {
alert(target.innerHTML);
}
});
</script>
c. 阻止默认行为
有些HTML元素身上带有我们不想要的默认行为。
例:a会自动在地址栏url结尾添加#,不是我们想要的
<a href="#">仅当做按钮用的a</a>
解决:
-
<a href="javascript:;">文本</a>
只对a元素有效- href中的
javascript
意思是不让a跳转到其它页面,而是在当前页面的内容中执行一条js语句。 javascript:;
表示执行一条空语句,相当于什么也不做。
- href中的
-
e.preventDefault()
对 任意元素 都有效
示例: 点a可返回顶部,但不在地址栏结尾加#top
<a id="a1" href="#top">返回顶部</a>
<script>
var a1=document.getElementById("a1");
a1.onclick=function(e) {
// 滚动 到 0位置
window.scrollTo(0,0);
e.preventDefault();//阻止默认行为,适用于所有元素
}
</script>
d. 取鼠标位置: 3组坐标
1). 鼠标相对于屏幕左侧、上方的距离
e.screenX, e.screenY
2). 鼠标相对于浏览器内部文档显示区左侧、上方的距离
e.clientX, e.clientY
3). 鼠标相对于当前点击的HTML元素左侧、上方的距离
e.offsetX, e.offsetY