DOM 事件模型

DOM 的事件操作(监听和触发),都定义在EventTarget接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如,XMLHttpRequest、AudioNode、AudioContext)也部署了这个接口。

该接口主要提供三个实例方法。

  • addEventListener:绑定事件的监听函数
  • removeEventListener:移除事件的监听函数
  • dispatchEvent:触发事件

事件模型

一个事件发生后,会在子元素及父元素之间进行传播(propagation),这种传播分为三个阶段。

(这种三阶段的传播模型,使得同一个事件会在多个节点上触发。)

  1. 由外向内找监听函数就是事件捕获
  2. 在目标节点触发事件
  3. 由内而外找监听函数就是事件冒泡

通俗一点来说就是一个事件被触发时,浏览器会自动从用户操作标签外的最上级标签逐渐向里检查是否有相同事件,如果有则触发,如果没有则继续向下检查知道用户操作的标签,这过程称为捕获,此时浏览器会继续由用户操作标签继续向是上级标签检查,如果有相同事件则触发,如果没有则继续向上检查直到最上级元素为止,此过程称为冒泡。(有监听函数就执行,并提供事件信息,没有就跳过)

事件传播的最上层对象是window,上例的事件传播顺序,在捕获阶段依次为window、document、html、body、父节点、目标节点,在冒泡阶段依次为目标节点、父节点、body、html、document、window。

DOM事件传播的三个阶段:捕获阶段,目标阶段,冒泡阶段

点击事件

代码:

<div class="grandfather"><div class="father"><div class="son"></div>word</div>
</div>

即.grandfather>.father>.son

给三个div分别添加事件的监听fnYe/fnBa/fnEr

提问1:点击了谁?

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

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

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

答案:都算

提问2:调用循序

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

答案:都行

IE5认为先调用fnEr,网景认为先调用fnYe,最后遇到了W3C

2002年,w3c发布标准
文档名为DOM Level 2 Events Specification
规定浏览器应该同时支持两种调用顺序
首先按照grandfather->father->son
然后按照son->father->grandfather

术语:

从外向内找监听函数,叫做事件捕捉
从内向外找监听函数,叫做事件冒泡
那岂不是fnYe/fnBa/fnEr都调用两次,非也!
开发者可以自己决定把fnYe放在捕捉阶段还是放在冒泡阶段

addEventListener事件绑定API

IE5*: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,并且提供事件信息。

代码演示:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>JS Bin</title>
</head>
<body>
<div class="level1 x"><div class="level2 x"><div class="level3 x"><div class="level4 x"><div class="level5 x"><div class="level6 x"><div class="level7 x"></div></div></div></div></div></div>
</div></body>
</html>

CSS:

* {box-sizing: border-box;
}
div[class^=level] {border: 1px solid;border-radius: 50%;display: inline-flex;
}
.level1 {padding: 10px;background: purple;
}
.level2 {padding: 10px;background: blue;
}
.level3 {padding: 10px;background: cyan;
}
.level4 {padding: 10px;background: green;
}
.level5 {padding: 10px;background: yellow;
}
.level6 {padding: 10px;background: orange;
}
.level7 {width: 50px;height: 50px;border: 1px solid;background: red;border-radius: 50%;
}
.x{background: transparent;//把元素的变为透明
}

Javascript代码:

const level1 = document.querySelector('.level1')
const level2 = document.querySelector('.level2')
const level3 = document.querySelector('.level3')
const level4 = document.querySelector('.level4')
const level5 = document.querySelector('.level5')
const level6 = document.querySelector('.level6')
const level7 = document.querySelector('.level7')let n = 1level1.addEventListener('click', (e)=>{const t = e.currentTarget//e只有在点击得一瞬间才会出现,所以要用t来记录一下。setTimeout(()=>{  t.classList.remove('x')},n*1000)n+=1//因为如果每个时间都为1000那么就相当于在8点同时设置很多的闹钟,。知识
})
level2.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{  t.classList.remove('x')},n*1000)n+=1
})
level3.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{  t.classList.remove('x')},n*1000)n+=1
})
level4.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{  t.classList.remove('x')},n*1000)n+=1
})
level5.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{  t.classList.remove('x')},n*1000)n+=1
})
level6.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{  t.classList.remove('x')},n*1000)n+=1
})
level7.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{  t.classList.remove('x')},n*1000)n+=1
})

简化:

const level1 = document.querySelector('.level1')
const level2 = document.querySelector('.level2')
const level3 = document.querySelector('.level3')
const level4 = document.querySelector('.level4')
const level5 = document.querySelector('.level5')
const level6 = document.querySelector('.level6')
const level7 = document.querySelector('.level7')let n = 1const fm = (e)=>{const t = e.currentTargetsetTimeout(()=>{  t.classList.remove('x')},n*1000)n+=1
}const fa = (e)=>{const t =e.currentTargetsetTimeout(()=>{t.classList.add('x')},n*1000)n+=1}level1.addEventListener('click',fm,true)
level1.addEventListener('click',fa)
level2.addEventListener('click',fm,true)
level2.addEventListener('click',fa)
level3.addEventListener('click',fm,true)
level3.addEventListener('click',fa)
level4.addEventListener('click',fm,true)
level4.addEventListener('click',fa)
level5.addEventListener('click',fm,true)
level5.addEventListener('click',fa)
level6.addEventListener('click',fm,true)
level6.addEventListener('click',fa)
level7.addEventListener('click',fm,true)
level7.addEventListener('click',fa)

知识复习:

classList:

定义和用法

classList 属性返回元素的类名,作为 DOMTokenList 对象。

该属性用于在元素中添加,移除及切换 CSS 类。

classList 属性是只读的,但你可以使用 add() 和 remove() 方法修改它。

HTML DOM classList 属性​www.runoob.com

currentTarget 事件属性

定义和用法

currentTarget 事件属性返回其监听器触发事件的节点,即当前处理该事件的元素、文档或窗口。
在捕获和起泡阶段,该属性是非常有用的,因为在这两个节点,它不同于 target 属性。

currentTarget ʼþÊôÐÔ​www.w3school.com.cn

总结:

两个疑问:

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

那么先调用老子得函数还是先调用儿子的函数?

捕获冒泡

捕获说先调用爸爸的监听函数

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

W3C时间模型

先捕获(先爸爸=>儿子)再冒泡(再儿子=>爸爸)

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

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

target v.s. currentTarget的区别

区别:

e.target - 用户操作的元素
e.currentTarget-程序员监听的元素
this是e.currentTarget,我个人不推荐使用它

举例:

div>span{文字},用户点击文字
e.target就是span
e.currentTarget就是div

一个特例

背景:

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

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

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

代码:

div.addEventListenter('click',f1)

div.addEventListenter('click',f2,true)

请问,f1先执行还是f2先执行?

如果把两个调换位置?

总结:谁先监听谁先执行。

level7.addEventListener('click',()=>{console.log(2)
},true)//捕获
level7.addEventListener('click',()=>{console.log(1)
})//冒泡

e.stopPropagation():取消冒泡

e.stopPropagation()可打断冒泡,浏览器不再向上走

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

注意:捕获不可以取消但是冒泡可以

不可以取消冒泡

有些事件不可以取消冒泡

可以查阅MDN英文版冒泡

比如scroll:

Bubbles:冒泡

Cancelable:是否取消冒泡

如何禁用滚动

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

JS Bin​js.jirengu.com

浏览器自带事件

来自MDN:

事件参考​developer.mozilla.org

自定义事件:代码

JS Bin​js.jirengu.com

事件委托:

我委托一个元素帮我监听我本该监听的东西,比如onclick

场景1:

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

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

代码:

JS Bin​js.jirengu.com

场景2:

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

答:监听祖先,等点击的时候看看是不是监听的元素即可。

优点:省监听数(内存),可以动态监听元素

代码:

JS Bin​js.jirengu.com

封装一个事件委托

只要实行一个函数就可以实现事件委托

要求:

写出这样一个函数on('click','#testDiv','li',fn)

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

要求用到事件委托

答案1:判断target是否匹配'li'

答案2:target/target的爸爸/target的爷爷

代码:

JS Bin​js.jirengu.com

错的但是面试可以用:

答:给一个元素加一个监听,看当前的target是否满足监听函数(函数2)中函数2的条件如果满足调用,不满足放过。但是是错的!

代码:

setTimeout(()=>{const button = document.createElement('button')const span = document.createElement('span')span.textContent='click 1'button.appendChild(span)div1.appendChild(button)
},1000)on('click','#div1','button',()=>{//'#div'是选择器不是元素console.log('button 被点击啦')
})
function on(eventType,element,selector,fn){if(!(element instanceof Element)){element = document.querySelector(element)}element.addEventListener(eventType,(e)=>{const t= e.target//被点击的元素是span不是button啦if(t.matches(selector)){//matches用来判断一个元素是否匹配一个选择器,selector是不是一个选择器
span不匹配buttonfn(e)}
})
}

移除元素所有事件监听_DOM 事件模型或 DOM 事件机制相关推荐

  1. html5鼠标事件监听,HTML5 Canvas鼠标与键盘事件

    演示HTML5 Canvas鼠标事件,获取Canvas对象上的鼠标坐标,演示键盘事件 通过键盘控制Canvas上对象移动. Canvas对象支持所有的JavaScript的鼠标事件,包括鼠标点击(Mo ...

  2. 宽度发生变化事件监听_PyQt5(3) :实现登录 事件监听处理 程序逻辑功能和界面分离...

    学习于: 学点编程吧:PyQt5图形界面编程(目录) 最终界面如下: 然后是代码: import sys from PyQt5.QtWidgets import QApplication, QWidg ...

  3. java中事件监听是什么意思_Java的事件监听器学习心得

    在Java的swing编程中,Java中的事件机制非常常用 一. 事件监听器的参与者: 1.事件对象: 一般继承自java.util.EventObject对象,由开发者自行定义. 2.事件源: 就是 ...

  4. html5中页面关闭事件监听,JS针对浏览器窗口关闭事件的监听方法集锦

    本文实例总结了JS针对浏览器窗口关闭事件的监听方法.分享给大家供大家参考,具体如下: 方式一:(适用于IE浏览器,而且刷新不提示,只在点击浏览器关闭按钮的时候提示) window.οnbefοreun ...

  5. JavaScript 基础--- (正则表达式 / 事件监听与绑定)

    正则表达式 创建正则表达式: 方法一: var reg = /pattern/; 方法二:var reg = new RegExp('pattern'); RegExp 对象的常用方法: 示例: &l ...

  6. html学习 - jquery事件监听详解

    html学习 - jquery事件监听详解 html学习 - jquery事件监听详解 监听方法 监听方法参数解释 click参数 事件自动执行问题解决 bind方法 live方法 监听方法 在jqu ...

  7. Ext JS 5的声明式事件监听

    原文:Declarative Listeners in Ext JS 5 在前文<在Ext JS 5使用ViewControllers>中,简单的介绍了Ext JS 5的一项重要改进--声 ...

  8. 由c#事件监听、回调函数引发观察者模式

    由c#事件监听.回调函数引发观察者模式 事件监听: C#中的事件,可以简单的理解为类或者对象发生了一件事,并且把这件事通知给了其他的类或者对象,其他的类或者对象可以根据事件的消息有所反应. 这非常类似 ...

  9. SpringBoot重点详解--事件监听

    目录 自定义事件监听 Springboot 启动事件监听 Springboot 事件监听为 Bean 与 Bean 之间的消息通信提供支持:当一个 Bean 做完一件事以后,通知另一个 Bean 知晓 ...

最新文章

  1. 聊聊工业界做机器学习的里程碑
  2. 惠普m1005连接电脑步骤_电脑连接电视机详细步骤方法图文
  3. ThihkPHP开发聚合支付系统源码 兼容所有易支付程序
  4. ibatis动态的传入表名、字段名
  5. paip.提升性能---string split
  6. mpu6050惯性导航学习记录
  7. TM1620中显存地址是偶数
  8. 计算机辅助设计基础试题,CAD基础试题「附答案」
  9. rrweb从数据库读取数据回放问题 (Error: Replayer need at least 2 events.)
  10. 8、实战项目-性能优化实战
  11. Latex学习(一)更改页边距
  12. 最小函数值(minval)
  13. 操作系统1(OS,operating system)
  14. 小兵围大炮||大兵小将【C++】
  15. mysql_row是什么类型的_【原创】8. MYSQL++中的Row类型
  16. Tomcat 解决Several ports (8005, 8080, 8009) required的方法:
  17. useSSL=false和true的区别
  18. MFC中worksheets.add 将新建sheet加在某sheet后的方法
  19. b站江科大自化协51单片机入门教程笔记(2)
  20. 不容错过 产品发布ppt模板素材推荐

热门文章

  1. 科普 | 单精度、双精度、多精度和混合精度计算的区别是什么?
  2. centos6.5 MySQL 服务器_启用CentOS6.5 64位安装时自带的MySQL数据库服务器
  3. python编译成dll文件_用vc生成可被python调用的dll文件
  4. apache启动失败_请检查相关配置.√mysql5.1已启动._1、Apache启动失败,请检查相关配置-百度经验...
  5. rocketmq 消息指定_SpringBoot 整合 RocketMQ 如何实现消息生产消费?
  6. python isinstance()
  7. TYVJ P1012 火柴棒等式 Label:枚举
  8. THEOS的第一个TWeak的成功创建
  9. 统计在从1到n的正整数中1出现的次数
  10. nutch,hbase,zookeeper兼容性问题