1、DOM 事件

DOM体系相关知识点包括如下:

  • DOM 事件流
  • 事件对象
  • 事件代理
  • 自定义事件

2、事件的传播过程

W3C 标准约定了一个事件的传播过程要经过以下三个阶段:

  1. 事件捕获阶段
  2. 目标阶段
  3. 事件冒泡阶段

image.png

3、事件对象梳理

3.1 currentTarget

它记录了事件当下正在被哪个元素接收,即”正在经过哪个元素“。这个元素是一直在改变的,因为事件的传播毕竟是个层层穿梭的过程。

3.2 target

指触发事件的具体目标,也就是最具体的那个元素,是事件的真正来源。

3.3 preventDefault 阻止默认行为

这个方法用于阻止特定事件的默认行为,如 a 标签的跳转等。

  1. e.preventDefault();

3.4 stopPropagation 不再派发事件

这个方法用于终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。

3.5 事件对象,是可以手动创建的

事件对象不一定需要你通过触发某个具体的事件来让它“自然发生”,它也可以手动创建的:

我们可以借助 Event() 构造函数, 来创建一个新的事件对象 Event。

  1. event = new Event(typeArg, eventInit);

4.事件冒泡、事件代理

4.1 事件冒泡例子

  1. <body>
  2. <div id="div1">
  3. <p id="p1">激活</p>
  4. <p id="p2">取消</p>
  5. <p id="p3">取消</p>
  6. <p id="p4">取消</p>
  7. </div>
  8. </body>
  1. //通用事件绑定例子
  2. function bindEvent(elem,type,fn){
  3. elem.addEventListener(type,fn);
  4. }
  5. //事件冒泡
  6. const p1 = document.getElementById("p1")
  7. bindEvent(p1,'click',function(event){
  8. event.stopPropagation(); //阻止冒泡
  9. console.log('激活')
  10. })
  11. const body = document.body
  12. //事件冒泡 将事件绑定到body上实现取消
  13. bindEvent(body,'click',event=>{
  14. console.log('取消')
  15. })

4.2 使用事件代理场景

所谓事件代理,因为数量太多不好去每个绑定事件,运用事件冒泡的特征把它绑定到父元素。

  1. 要你安装监听某一个事件的监听函数(事件相同)
  2. 监听函数是被安装在一系列具有相同特征的元素上(元素特征相同,一般来说就是具备同样的父元素)
  3. 这一系列相同特征元素上的监听函数还干的都是一样的事儿(监听逻辑相同/雷同)

4.3 事件代理例子

  1. <div id="div3">
  2. <a href="#">a1</a>
  3. <a href="#">a2</a>
  4. <a href="#">a3</a>
  5. <a href="#">a4</a>
  6. </div>
  7. <button>
  8. 点击增加一个a标签
  9. </button>
  1. //事件代理
  2. const div3 = document.getElementById("div3")
  3. bindEvent(div3,'click','a',function(event){
  4. event.preventDefault();
  5. alert(this.innerHTML)
  6. })
  7. function bindEvent(elem,type,selector,fn){
  8. if(fn == null){
  9. fn = selector
  10. selector = null
  11. }
  12. elem.addEventListener(type,event=>{
  13. const target = event.target
  14. if(selector){
  15. //代理绑定
  16. if(target.matches(selector)){
  17. fn.call(target,event)
  18. }
  19. }else{
  20. fn.call(target,event)
  21. }
  22. })
  23. }

4.4 事件代理特点

  • 事件代理代码简洁
  • 减少浏览器内存占用
  • 但是不要滥用

5、自定义事件

有这样一个场景:在点击A之后,B 和 C 都能感知到 A 被点击了,并且做出相应的行为——就像这个点击事件是点在 B 和 C 上一样。在这样的场景下就诞生了自定义事件。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  7. <title>Document</title>
  8. <style>
  9. #divA,
  10. #divB,
  11. #divC {
  12. width: 200px;
  13. height: 200px;
  14. color: #fff;
  15. }
  16. #divA {
  17. background-color: red;
  18. }
  19. #divB {
  20. background-color: black;
  21. }
  22. #divC {
  23. background-color: blue;
  24. }
  25. </style>
  26. </head>
  27. <body>
  28. <div id="divA">我是A</div>
  29. <div id="divB">我是B</div>
  30. <div id="divC">我是C</div>
  31. <script>
  32. var clickAEvent = new Event("clickA");
  33. // 获取 divB 元素
  34. var divB = document.getElementById("divB");
  35. // divB 监听 clickA 事件
  36. divB.addEventListener("clickA", function (e) {
  37. console.log("我是B,我感觉到了A");
  38. console.log(e.target);
  39. });
  40. // 获取 divC 元素
  41. var divC = document.getElementById("divC");
  42. // divC 监听 clickA 事件
  43. divC.addEventListener("clickA", function (e) {
  44. console.log("我是C,我感觉到了A");
  45. console.log(e.target);
  46. });
  47. divA.addEventListener("click", function () {
  48. console.log("我是A");
  49. // 注意这里 dispatch 这个动作,就是我们自己派发事件了
  50. divB.dispatchEvent(clickAEvent);
  51. divC.dispatchEvent(clickAEvent);
  52. });
  53. </script>
  54. </body>
  55. </html>

image.png

6、典型题

6.1 编写一个通用的事件监听函数

  1. function bindEvent(elem,type,selector,fn){
  2. if(fn == null){
  3. fn = selector
  4. selector = null
  5. }
  6. elem.addEventListener(type,event=>{
  7. const target = event.target
  8. if(selector){
  9. //代理绑定
  10. if(target.matches(selector)){
  11. fn.call(target,event)
  12. }
  13. }else{
  14. fn.call(target,event)
  15. }
  16. })
  17. }

6.2 事件冒泡的流程

  • 基于DOM树形结构
  • 事件会顺着触发元素向上冒泡
  • 应用场景:代理

6.3 无限下拉的图片列表,如何监听每个图片的点击

  • 事件代理
  • 用e.target获取触发元素
  • 用matches来判断是否是触发元素