前端面试官最爱:事件冒泡与事件捕获

621 阅读3分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

问答(分析在最后)

这个问题主要考察基础知识,我一般喜欢这样问:

  1. 对DOM事件流你是怎么理解的?
  2. 事件冒泡和事件捕获具体的理解?
  3. 举个例子?
  4. 那怎么解决事件冒泡所引发的问题呢?
  5. 那在解释下事件委托?

Q:对DOM事件流你是怎么理解的?

A:事件流也叫事件传播,DOM2级事件规定事件流的三个阶段,分别是事件捕获阶段,目标阶段,事件冒泡阶段。 触发顺序先是事件捕获,在这个阶段可以截获事件做一些操作。 然后到目标阶段,在此阶段一般不做处理。 最后到冒泡阶段,可以在这个阶段对事件做出响应。

回答完后,引出了事件捕获和冒泡,问具体的理解?

Q:事件冒泡和事件捕获具体的理解?

事件冒泡是当某个元素的某类型事件触发时,它的父元素同类型的事件也会被触发,一直触发到根元素上。从确定的元素到不确定的元素。 事件捕获的思想是从根元素一直到事件源,用意是在事件到达预定目标之前就捕获它。

Q:追问:举个例子?

比如,现在有一个简单的H5页面,里面有3个嵌套元素,外层from,内层div,最内层p,每一层都有onclick事件,在点击最内层的p标签时,会依次运行onclick,直到文档对象。这个过程就是冒泡。

当我点击p标签时,首先会通过事件流的捕获阶段找到p元素,这个过程叫事件捕获,找到后触发目标阶段,然后上升到冒泡阶段,开始事件调用。

Q:追问:ok,那怎么解决事件冒泡所引发的问题呢?

A:可以使用stopPropagation()阻止冒泡或者也可以通过事件委托处理。

Q:那在解释下事件委托?

A:利用事件冒泡,只指定一个事件处理程序,管理某一类型的所有事件。 假如最内层有多个P标签,都有onclick事件,给它的父元素div绑定事件,代理子元素的点击事件,然后通过event对象提供的target属性,取到具体的节点进行操作。

事件委托减少了事件注册,节省内存占用,适合动态添加元素。

分析

事件流的三个阶段,捕获、目标、冒泡

1642422987(1).jpg

事件冒泡演示代码

<style> 
    body * { margin: 10px; border: 1px solid blue; 
    } 
</style> 
<form onclick="alert('form')">
    FORM 
    <div onclick="alert('div')">
    DIV 
    <p onclick="alert('p')">P</p> 
    </div>
</form>

单击内部<p> 运行onclick事件,将看到 alertp→ div→ form

这个过程被称为“冒泡”,因为事件像水中的气泡一样从内部元素向上通过父元素“冒泡”。

注意:几乎所有的事件都会冒泡,也有例外,例如focus。

停止冒泡演示代码

<body onclick="alert(`body点击`)"> 
    <button onclick="event.stopPropagation();alert('button点击')">点我</button> 
</body>

此时点击button,只有alert按钮点击,不会出现body点击。

事件委托演示代码

<ul id="parent">
    <li class="child">one</li>
    <li class="child">two</li>
    <li class="child">three</li>
</ul>

<script type="text/javascript">
    //父元素
    var dom= document.getElementById('parent');
    //父元素绑定事件,代理子元素的点击事件 
    dom.onclick= function(event) {
        var event= event || window.event;
        var curTarget= event.target || event.srcElement;
        if (curTarget.tagName.toLowerCase() == 'li') {
            //事件处理 
        } 
    }
</script>

优点:

  1. 节省内存占用,减少事件注册。
  2. 新增子对象无需再次绑定事件,适合动态添加

局限性:

  1. focus、blur 之类的事件本身没有事件冒泡机制,所以无法委托
  2. mousemove、mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,不适合事件委托