事件流、事件委托/事件代理

事件流

JS 与 html 页面的交互是通过 DOM 事件实现的,那么什么是事件流?其实就是指页面接收事件的顺序;、
DOM 事件流包括三个阶段:事件捕获阶段,处于目标阶段,事件冒泡阶段;
用意下代码为简单示例说明:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Event</title>
</head>
<body>
    <button>我是一个按钮</button>
</body>
</html>

事件捕获

事件触发后,不具体的节点应该更早接收到事件,最具体的节点应该最后接收到事件,其用意在于在事件到达预定目标之前捕获它;
在示例页面中,当点击 <button> 元素,click 事件传播顺序:
Document -> html -> body -> button 由外到内
image

事件冒泡

与事件捕获正好相反,事件开始时,由最具体的元素(事件发生所在的节点)接收,然后逐级向上传播到较为不具体的节点(文档);
在示例页面中,当点击 <button> 元素,click 事件传播顺序:
<button> -> body -> html -> Document 由内到外
image

结论:触发一个事件,首先事件捕获,为截获事件提供机会,然后实际的目标接收到事件,最后事件冒泡阶段,可在这个阶段对事件做出响应;
image

阻止事件触发

默认情况下,同时存在多个事件处理函数时,会按照 DOM 事件流模型中的顺序执行;当子元素上发生某个事件,不需要执行父元素上注册的事件处理函数,可以停止事件捕获和冒泡,避免过多没有意义的函数调用;
常见的3中阻止事件触发的方式:

  1. event.preventDefault()
    用于阻止特定事件的默认动作;
    只有 cancelabletrue 的事件才可以用,若对象的 cancelable 为 false, 那就是没有默认动作,或不能阻止默认动作;
    cancelable:是事件对象(event)上的属性,event.cancelable 返回一个布尔值, 如用 preventDefault() 方法可以取消与事件关联的默认动作,则为 true,否则为 fasle;
    示例:点击 type="submit"input 标签提交表单,在 onclick 事件函数中用 event.preventDefault()阻止默认动作, 点击 submit 后就不会自动提交表单,但是不能阻止事件冒泡;
  2. event.stopPropagation()
    立即停止事件在 DOM 中的传播,不阻止默认动作;
    示例:点击一个按钮,只想触发绑定在按钮上的 click 事件函数,不想触发传播链上的其他函数,可以使用这种方式 event.stopPropagation();
    注意:若是在捕获流就执行到的事件,比如在 document 上绑定一个点击事件,addEventListen 的第三个参数设置为 true,那么 event.stopPropagation 无法阻止触发;
  3. return false
    后续所有相关的触发事件和动作都不会被执行,阻止事件继续传播,事件冒泡和默认行为都被阻止;
    jQuery 提供,实际做了三件事:
    1. event.preventDefault()
    2. event.stopPropagation()
    3. 停止回调函数执行并立即返回

事件委托/事件代理

事件委托:又名事件代理,利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件;
优点:
1. 事件管理函数减少,不需要为每个元素都添加监听函数,对于同意父节点下相似的子元素,可以通过委托给父元素的监听函数来处理事件;
2. 可以更方便的动态添加和修改元素,不需要因元素的改动而修改事件绑定;
3. 极大程度上减少了与 DOM 的交互次数,从而提高整体运行性能,事件委托是 "事件处理程序过多" 问题的直接解决方案;
一个简单的事件委托示例:

点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件委托</title>
</head>
<body>
    <div id="root">
        <button id="add">添加</button>
        <button id="modify">修改</button>
        <button id="move">移动</button>
        <button id="delete">删除</button>
    </div>
    <script>
        window.onload = function() {
            // 普通方式
            const addDom = document.getElementById("add");
            const modifyDom = document.getElementById("modify");
            const moveDom = document.getElementById("move");
            const deleteDom = document.getElementById("delete");
            addDom.onclick = function(e) {
                console.log("click add");
            };

            modifyDom.onclick = function(e) {
                console.log("click modify");
            };

            moveDom.onclick = function(e) {
                console.log("click move");
            };

            deleteDom.onclick = function(e) {
                console.log("click delete");
            };

            // 事件委托方式
            const root = document.getElementById("root");
            root.onclick = function(e) {
                const event = e || window.event;
                const target = event.target || window.srcElement;
                if (target.nodeName.toLowerCase() === "button") {
                    switch (target.id) {
                        case "add":
                            console.log("add");
                            break;
                        case "modify":
                            console.log("modify");
                            break;
                        case "move":
                            console.log("move");
                            break;
                        case "delete":
                            console.log("delete");
                            break;
                        default:
                            console.log("无效的点击类型");
                            break;
                    }
                }
            }
        };
    </script>
</body>
</html>

使用普通方式点击每一个做不同的操作,至少需要4次DOM操作,用事件委托就可以只用一次DOM操作就能实现所有效果;
适合使用事件委托的事件: clickmousedownmouseupkeydownkeyupkeypress
注意: mouseovermouseout 事件虽然也冒泡,但是处理它们的时候需要计算元素位置,非常不好把控,focusblur 之类本身就没用冒泡的特性,所以不能使用事件委托;

总结

事件流是页面接收事件的顺序:捕获阶段 -> 目标阶段 -> 冒泡阶段;
事件委托又名事件代理,利用事件冒泡,只指定一个事件处理程序,就可以管理某种类型的所有事件;极大程度上减少与 DOM 的交互次数,提高整体运行性能;

posted @ 2022-04-12 09:55  太轻描淡写了  阅读(194)  评论(0编辑  收藏  举报