DOM事件与事件委托


你可以看到

请简述 DOM 事件模型或 DOM 事件机制(请描述什么是捕获什么是冒泡,并说说是先捕获还是先冒泡,再搜搜看网上的文章怎么说)

简述事件委托(请想办法把「监听祖先元素」说得高大上一点)

要说的话

带你了解捕获与冒泡,W3C事件模型和事件委托。

了解捕获与冒泡

往下代码都以点击事件作为例

先一步一步了解。

上代码

  • 代码-HTML
<div class=爷爷>
  <div class=爸爸>
    <div class=儿子>
    文字
    </div>
  </div>
</div>

即.爷爷>.爸爸>.儿子

给三个div分别添加事件监听 fnYe / fnBa / fnEr。点击谁监听谁

  • 针对上面代码 提问1:点击了谁

点击文字,算不算点击儿子?

点击文字,算不算点击爸爸?

点击文字,算不算点击爷爷?

-答案:都算

  • 提问2:调用顺序呢

点击文字,最先调用fnYe /fnBa / fnEr中的哪一个函数?

答案:都行。看浏览器怎么实现的。

IE5认为先调 fnEr,网景认为先调fnYe,然后掐上了,最后闹到了W3C

和事佬W3C
  • 2002年,W3C发布标准

文档名为DOM Level 2 Events Specification

规定浏览器应该同时支持两种调用顺序

首先按爷爷=>爸爸=>儿子顺序看有没有函数监听

然后按儿子=>爸爸=>爷爷顺序看有没有函数监听

有监听函数就调用,并提供事件信息,没有就跳过

相必你已经大致了解了捕获与冒泡

捕获与冒泡
  • 术语

从外向内找监听函数,叫事件捕获

从内向外找监听函数,叫事件冒泡

疑问:从外到内再从内到外,那岂不是fnYe / fnBa / fnEr都调用两次?

非也!开发者自己选择把fnYe放在捕获阶段还是放在冒泡阶段

  • DOM事件机制图示

捕获与冒泡

解释:

蓝色td被点击了,要一个个问,先问window:你有没有函数要调用的,因为你的后代的后代…被点击了。window说:我没有函数可以管。然后再像这样依次调用下来,直到问到td。这个过程走完了后会有个目标阶段,就是在td这里判断一下是否需要冒泡。td说:我需要冒。那就冒回去吧,然后从td到window再走一遍,再把所有函数调用一遍(有就调,没有就不调)

addEventListener
  • 事件绑定API

IE 5*: baba.attachEvent(‘onclick’, fn)//冒泡

网景: baba.addEventListener( ‘click’, fn)//捕获

W3C: baba.addEventListener(‘click’, fn, bool)

  • 如果bool不传或为falsy

就让fn走冒泡,即当浏览器在冒泡阶段发现baba有fn 监听函数,就会调用fn,并提供事件信息

  • 如果bool为true
    就让fn走捕获,即当浏览器在捕获阶段发现 baba有fn 监听函数,就会调用fn ,并提供事件信息

你可以选择把fn放在哪边
fn放在哪边

代码示例

代码图解

小结
  • 两个疑问

儿子被点击了,算不算点击老子?算

那么先调用老子的函数还是先调用儿子的函数?不确定,IE先调用儿子,firefox就先调用老子,最终W3C说我两个都支持,我先从老子到儿子,在从儿子到老子,这下你们满意了吧。

  • 捕获与冒泡

捕获说先调用爸爸的监听函数,再调用儿子的监听函数

冒泡说先调用儿子的监听函数,再调用爸爸的监听函数

W3C事件模型

先捕获(先爸爸=>儿子) 再冒泡?(再儿子=>爸爸)(可以阻止冒泡)

注意e对象被传给所有监听函数

事件结束后,e对象就不存在了

说不存在是不想纠结于e。在事件结束后,不推荐大家继续用 e,因为 e 会被浏览器悄悄改变,要用的话你应该提前把属性复制到自己的变量里,比如示例代码里的 const t = e.currentTarget 就是把 currentTarget 的引用复制到 t。

target v.s. currentTarget

-区别

e.target- 用户操作的元素

e.currentTarget-程序员监听的元素

this是e.currentTarget,我个人不推荐使用它

-举例

div > span{文字},用户点击文字后

e.target就是span

e.currentTarget就是div

一个特例
  • 背景

只有一个div被监听(不考虑父子同时被监听)

fn 分别在捕获阶段和冒泡阶段监听click事件

用户点击的元素就是开发者监听的

代码

div.addEventLisenter('click', f1)
div.addEventLisenter('click', f2, true)
  • 请问
    f1先执行还是f2先执行?

如果把两行调换位置后,请问哪个先执行?

错误答案:f2先执行

正确答案:谁先监听谁先执行,没有父子

总结:这是一个特例

取消冒泡
  • 捕获不可取消,但冒泡可以

e.stopPropagation()可中断冒泡,浏览器不再向上走。通俗来说:有人打我,我自己解决,别告诉我老子

一般用于封装某些独立的组件

不可阻止默认动作
  • 有些事件不能阻止默认动作

MDN搜索scroll event,看到Bubbles和 Cancelable

Bubbles的意思是该事件是否冒泡,所有冒泡都可取消

Cancelable的意思是开发者是否可以阻止默认事件

Cancelable与冒泡无关

推荐看MDN英文版,中文版内容不全

如何阻止滚动

  • scroll 事件不可阻止默认动作
    阻止scroll默认动作没用,因先有滚动才有滚动事件

要阻止滚动,可阻止 wheel和 touchstart的默认动作

注意你需要找准滚动条所在的元素,示例

但是滚动条还能用,可用CSS让滚动条width: 0 (::-webkit-scrollbar { width: 0 !important })

  • CSS也行
    使用overflow: hidden可以直接取消滚动条

但此时JS依然可以修改scrollTop

小结
  • target和currentTarget

一个是用户点击的,一个是开发者监听的

  • 取消冒泡

e.stopPropagation()

  • 事件的特性

Bubbles表示是否冒泡

Cancelable表示是否支持开发者取消冒

如scroll不支持取消冒泡

  • 如何禁用滚动

取消特定元素的wheel和touchstart的默认动作

自定义事件
  • 浏览器自带事件

一共100多种事件,列表在MDN 上

  • 提问

开发者能不能在自带事件之外,自定义一个事件

答案:可以,见示例

事件委托

委托一个元素监听我本该监听的东西

针对特定场景的事件委托
  • 场景一

你要给100个按钮添加点击事件,咋办?

答:监听这100个按钮的祖先,等冒泡的时候判断target是不是这100个按钮中的一个,代码示例

  • 场景二

你要监听目前不存在的元素的点击事件,咋办?

答:监听祖先,等点击的时候看看是不是我想要监听的元素即可,代码示例

  • 优点

省监听数(内存)

可以监听动态元素

封装事件委托
  • 要求
    写出这样一个函数on(‘click’, ‘#testDiv’, ‘li’, fn)

注意:先搞清楚输入(封装的四个参数,事件绑定、元素选择器 Element.matches 、事件 event 对象,某些函数)在搞清楚输出

当用户点击#testDiv里的li元素时,调用fn函数

要求用到事件委托

  • 答案一
    判断target是否匹配’li’等选择器,代码示例
  • 答案二
    递归判断target / target的爸爸/target的爷爷。(当前元素不匹配button就看他的爸爸匹配不,只要我的祖先中某一项是button,说明点击的就是button),代码示例
  • 整合进jQuery
    有兴趣可以自己实现$(’#xxx’).on(‘click’, ‘li’, fn)
JS支持事件吗

  • 不支持。本次讲的DOM事件不属于JS的功能,是浏览器提供的 DOM的功能

JS只是调用了DOM提供的addEventListener而已

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值