Js事件、事件流+事件传播三个阶段,事件模型

一、事件

事件: 在HTML文档或浏览器发送的一种交互操作,使得网页具有互动性,常见的有加载、鼠标事件
事件处理函数: 当事件发生时,我们希望自动调用执行的函数

二、事件流

因为交互会影响到DOM,它是一个树状结构,触发子节点的事件时,常常涉及到调用的顺序问题,这就引出了事件流

2.1 三个阶段

事件传播的整个过程分为3个阶段:

  1. 事件捕获阶段
    先由文档的根节点document往事件触发对象,从外向内捕获事件对象
  2. 目标阶段(目标对象本身的事件程序)
    到达目标事件位置,触发事件本身的程序内容;
  3. 事件冒泡阶段
    再从目标事件位置往文档的根节点方向回溯,从内向外冒泡事件对象。

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中以赋值方式绑定

  1. 查找触发事件的元素对象
    var button= document.getElementById('.button');
  2. 绑定事件
    button.onclick = fun;

缺点: 无法同时绑定多个事件处理函数,后赋值的事件处理函数会把原事件处理函数覆盖。

c. 添加事件监听对象的方式

将会经历事件流的三个过程(详见“二”)

  1. 查找触发事件的元素
  2. 添加事件监听
    元素对象.addEventListener(eventType, handler, useCapture)
  3. 移除事件监听
    元素对象.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 特性

  1. 浏览器中有一个巨大的事件监听对象队列

  2. addEventListener做了2件事:
         i. 新建一个事件监听对象,其中保存:元素对象、事件名、事件处理函数
         ii. 自动将事件监听对象添加到事件监听对象队列中

  3. 重复执行几次addEventListener()就会反复添加几个重复的事件监听对象

  4. 调用顺序:
        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. 利用冒泡/事件委托

浏览器触发事件:遍历事件监听对象队列形式查找事件监听对象。
如果添加的事件监听对象越多,遍历时间越长,事件响应的速度越慢

优化:应在保持功能的情况下尽量减少事件绑定的次数

步骤:

  1. 当多个子元素都要绑定相同的事件处理函数时,只要给父元素绑定一个事件处理函数即可
    所有子元素都可通过冒泡的方式触发父元素上统一的事件处理函数——共用!
    缺点:
    事件处理函数绑定在父元素上的,触发也是在父元素上触发的,this->父元素,不再指子元素
    再使用this,就无法获得实际点击的子元素了。

  2. 只要使用事件委托,都要e.target代替this
    e.target: 是事件对象中专门保存最初点击的那个目标元素的特殊属性。
    优点: 不随冒泡而改变

  3. 验证当前点击的目标元素的特征,只有目标元素的特征符合要求,才继续执行后续操作,否则不执行操作
    一切目标元素与其他元素不同的特征,都可作为判断依据
    .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元素有效

    1. href中的javascript意思是不让a跳转到其它页面,而是在当前页面的内容中执行一条js语句。
    2. javascript:; 表示执行一条空语句,相当于什么也不做。
  • 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

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你脸上有BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值