事件冒泡,事件捕获,事件委托

6/29/2020 Javascript

# 事件机制说起

事件触发三阶段 捕获阶段,目标阶段,冒泡阶段

  1. 捕获阶段:

事件从根节点流向目标节点,途中流经各个 DOM 节点,在各个节点上触发捕获事件,直到达到目标节点。

捕获阶段的主要任务是建立传播路经,在冒泡阶段根据这个路经回溯到文档根节点

  1. 目标阶段 事件到达目标节点时,就到了目标阶段,事件在目标节点上被触发

  2. 冒泡阶段(从哪里来,回哪里去) 事件在目标节点上触发后,不会终止,一层层向上冒,回溯到根节点。

小 demo 演示下:

(这是一张图片,demo 的代码在下面)

思考:

  • 点击grandson范围,会触发到什么?
  • 点击children范围,会触发到什么?
  • 点击parent范围,会触发到什么?
查看 demo 代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <style>
    #grandson {
      width: 100px;
      height: 100px;
      background-color: #00f;
    }
    #children {
      width: 150px;
      height: 150px;
      background-color: #8ac28a;
    }
    #parent {
      width: 200px;
      height: 200px;
      background-color: green;
    }
    div {
      color: #fff;
      text-align: center;
      margin: 0 auto;
    }
  </style>
  <body>
    <div id="parent">
      parent
      <div id="children">
        children
        <div id="grandson">
          grandson
        </div>
      </div>
    </div>
  </body>

  <script>
    let parent = document.getElementById('parent')
    let children = document.getElementById('children')
    let grandson = document.getElementById('grandson')

    parent.addEventListener('click', function(e) {
      console.log('click - parent', e)
    })
    children.addEventListener('click', function(e) {
      console.log('click - children', e)
    })
    grandson.addEventListener('click', function(e) {
      console.log('click - grandson', e)
    })
  </script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  • 点击 grandson 。首先打印的是 grandson,其次children,最后 parent 其余同理。这也证实了事件触发的 3 个阶段分别是捕获 -> 目标 ->冒泡

---可以在看下 e 打印的内容,3 个e都是一样的内容。只看比较有价值的部分。

我这里点击的的是grandson,打印如下:

  • path 代表冒泡的路径,一直从 grandson 重新回到 documentwindow
  • target 就是我们点击的目标节点了

那会不会是因为 3 个元素重叠了(图层重叠),所以才会触发到这样的?

那 div 结构改变一下:

刚才的 html 结构不变,只是多加了一层定位。然后实验结果是一样的,依旧是 grandson,其次children,最后 parent。只要节点是父子节点关系,就都会触发到对应的事件

# 事件委托

  • 原理:

    利用事件冒泡机制实现的

  • 优点:

    1. 可以为动态添加的 dom 绑定事件(动态 dom 就是通过 JS,后期添加的 dom 元素)
    2. 只需要将同类元素的事件委托给父级或者更外级的元素,不需要给所有元素都绑定事件,减少内存空间占用,提升性能 动态新增的元素无需重新绑定事件
  • 注意事项:

    事件委托的实现依靠事件冒泡,因此不支持事件冒泡的事件就不适合用事件委托。

用到刚才修改过定位的 demo、现在为 position 注册点击事件

<body>
  <div id="position">
    <div id="parent">
      parent
      <div id="children">
        children
        <div id="grandson">
          grandson
        </div>
      </div>
    </div>
  </div>
</body>

<script>
  var position = document.getElementById('position')

  position.onclick = function(e) {
    var e = event || window.event
    var target = e.target || e.srcElement
    console.log(e.target.nodeName, e.target.id)
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

如果采用了事件委托的方式。触发的节点就只有一个!就是点击的节点

不过在 path 中还是可以看到父节点,因为这是在捕获事件就已经记录下了

# 阻止事件冒泡

w3c 的方法是 e.stopPropagation(),IE 则是使用 e.cancelBubble = true

//阻止冒泡行为
function stopBubble(e) {
  //如果提供了事件对象,则这是一个非IE浏览器
  if (e && e.stopPropagation) {
    //因此它支持W3C的stopPropagation()方法
    e.stopPropagation()
  } else {
    //否则,我们需要使用IE的方式来取消事件冒泡
    window.event.cancelBubble = true
  }
}

document.getElementById('childred').onclick = function(e) {
  stopBubble(e) // 这样就可以阻止事件冒泡了
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 取消默认事件

什么是默认事件?和事件冒泡什么区别?

既然是说默认行为,当然是元素必须有默认行为才能被取消,如果元素本身就没有默认行为,调用当然就无效了

哪些元素有默认行为呢?

  1. a 标签
  2. <input type="submit">
  3. ... 不用写 JS 就会有交互的,我们都称为:默认行为

阻止默认行为:

w3c 的方法是 e.preventDefault(),IE 则是使用 e.returnValue = false;

//阻止冒泡行为
function stopDefault(e) {
  if (e && e.preventDefault) {
    e.preventDefault()
  } else {
    window.event.returnValue = true
  }
}

document.getElementByTagName('a').onclick = function(e) {
  stopDefault(e)
}
1
2
3
4
5
6
7
8
9
10
11
12

如何判断元素是否已经使用了 e.preventDefault()

我们可以在事件对象中使用 event.defaultPrevented 属性。它返回一个布尔值用来表明是否在特定元素中调用了 event.preventDefault()。

# JQ 阻止事件冒泡和默认行为

$('#对应节点').on('click', function() {
  return false
})
1
2
3
Last Updated: 5/9/2021, 10:45:03 PM