zoukankan      html  css  js  c++  java
  • JavaScript基础概念之----事件冒泡/捕获/委托

    事件流

    如果单击了页面中的某个按钮,同时也单击了按钮的容器元素,甚至单击了整个页面。

    IE提出的是冒泡流,网景提出的是捕获流。

    <div id="content">content
            <div id="btn">button</div>
        </div>
    
        <script type="text/javascript">
            var content = document.getElementById("content");
            var btn = document.getElementById('btn');
            btn.onclick = function(){
                alert("btn");
            };
            content.onclick = function(){
                alert("content");
            };
            document.onclick = function(){
                alert("document");
            }
        </script>
    
    //点击容器 #btn ,则弹出的顺序是:btn > content > document
    //点击容器 #content,则弹出的顺序是:content > document
    //点击容器 document,则弹出的是 document

    JS事件流原理图 :

    1、一个完整的事件流是从 window 开始,最后回到 window 的一个过程

    2、事件流被分为三个阶段:(1-5)捕获过程、(5-6)目标过程、(6-10)冒泡过程

    <div id="wrapDiv">wrapDiv
            <p id="innerP">innerP
                <span id="textSpan">textSpan</span>
            </p>
        </div>
        <script>
        var wrapDiv = document.getElementById("wrapDiv");
        var innerP = document.getElementById("innerP");
        var textSpan = document.getElementById("textSpan");
    
        // 捕获阶段绑定事件
        window.addEventListener("click", function(e){
            console.log("window 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        document.addEventListener("click", function(e){
            console.log("document 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        document.documentElement.addEventListener("click", function(e){
            console.log("documentElement 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        document.body.addEventListener("click", function(e){
            console.log("body 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        wrapDiv.addEventListener("click", function(e){
            console.log("wrapDiv 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        innerP.addEventListener("click", function(e){
            console.log("innerP 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        textSpan.addEventListener("click", function(e){
            console.log("textSpan 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        // 冒泡阶段绑定的事件
        window.addEventListener("click", function(e){
            console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        document.addEventListener("click", function(e){
            console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        document.documentElement.addEventListener("click", function(e){
            console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        document.body.addEventListener("click", function(e){
            console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        wrapDiv.addEventListener("click", function(e){
            console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        innerP.addEventListener("click", function(e){
            console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        textSpan.addEventListener("click", function(e){
            console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    </script>

    如果点击textSpan元素,控制台打印如下图:

    如上图所示,事件传播的过程是先捕获,再冒泡。

     那么,如果不使用addEventListener方法绑定的事件(如onclick),会发生在哪个阶段?

    <div id="wrapDiv">wrapDiv
            <p id="innerP">innerP
                <span id="textSpan">textSpan</span>
            </p>
        </div>
        <script>
        var wrapDiv = document.getElementById("wrapDiv");
        var innerP = document.getElementById("innerP");
        var textSpan = document.getElementById("textSpan");
    
    // 测试直接绑定的事件到底发生在哪个阶段
        wrapDiv.onclick = function(){
            console.log("wrapDiv onclick 测试直接绑定的事件到底发生在哪个阶段")
        };
    
        // 捕获阶段绑定事件
        window.addEventListener("click", function(e){
            console.log("window 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        document.addEventListener("click", function(e){
            console.log("document 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        document.documentElement.addEventListener("click", function(e){
            console.log("documentElement 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        document.body.addEventListener("click", function(e){
            console.log("body 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        wrapDiv.addEventListener("click", function(e){
            console.log("wrapDiv 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        innerP.addEventListener("click", function(e){
            console.log("innerP 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        textSpan.addEventListener("click", function(e){
            console.log("textSpan 捕获", e.target.nodeName, e.currentTarget.nodeName);
        }, true);
    
        // 冒泡阶段绑定的事件
        window.addEventListener("click", function(e){
            console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        document.addEventListener("click", function(e){
            console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        document.documentElement.addEventListener("click", function(e){
            console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        document.body.addEventListener("click", function(e){
            console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        wrapDiv.addEventListener("click", function(e){
            console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        innerP.addEventListener("click", function(e){
            console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    
        textSpan.addEventListener("click", function(e){
            console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName);
        }, false);
    </script>

    点击textSpan元素,如下图控制台输出:

    1、所有在目标元素上绑定的事件,都会发生在目标阶段

    2、在绑定捕获代码之前写了绑定的冒泡阶段的代码,所以在目标元素上就不会遵守先捕获后冒泡这一规则,而是先绑定的事件先发生。

    3、由于wrapDiv不是目标元素,所以它上面绑定的事件会遵守先捕获后冒泡的规则。所以用onclick直接绑定的事件发生在了冒泡阶段。

     事件委托

     对“事件处理程序过多”问题的解决方案就是事件委托

     事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

     例如,click事件会一直冒泡到document层次。我们可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。

    //点击li元素,输出li当中的颜色
    <ul id="box">
        <li>red</li>
        <li>yellow</li>
        <li>blue</li>
        <li>green</li>
        <li>black</li>
        <li>white</li>
    </ul>
    
    //一般会这样写
    <script>
        var colorBox = document.getElementById('box')
        var colors = colorBox.getElementsByTagName('li')
        for(var i=0;i<colors.length;i++){
            colors[i].addEventListener('click',function(e){
                var li = e.target;
                console.log(li.innerHTML)
            },false)
        }
    </script>
    
    
    //使用事件委托去写
    <script>
        var colorBox = document.getElementById('box')
        colorBox.addEventListener('click',function(e){
            var li = e.target;
            if(li.nodeName.toLowerCase() === 'li'){
                console.log(li.innerHTML)
            }
        },false)
    </script>    

    事件委托还有一个好处就是添加进来的元素也能绑定事件

    //点击li元素,输出li当中的颜色
    <ul id="box">
        <li>red</li>
        <li>yellow</li>
        <li>blue</li>
        <li>green</li>
        <li>black</li>
        <li>white</li>
    </ul>
    <button onclick="add()">add</button>
    
    <script>
        var colorBox = document.getElementById('box')
        colorBox.addEventListener('click',function(e){
            var li = e.target || e.srcElement; //兼容处理
            if(li.nodeName.toLowerCase() === 'li'){
                console.log(li.innerHTML)
            }
        },false)
    
        function add(){
            var liNode = document.createElement('li')
            var textNode = document.createTextNode('this is add text.')
            liNode.appendChild(textNode);
            document.getElementById('box').appendChild(liNode)
        }
    </script>    
  • 相关阅读:
    C++ 编译时字符串加密
    c#自动修复缺损和不规范的html
    C#下载网络资源(网页或文件)
    yum install 命令下载安装离线包
    C# Sql Server 数据库 传递 表值参数
    cximage 裁剪图片并背景透明
    centos 7.5 编译并运行 opencv 4.5.1 c++
    c++ freeimage 指定颜色透明
    c++ string 大小写转换
    opencv 裁剪图像
  • 原文地址:https://www.cnblogs.com/adhehe/p/9794168.html
Copyright © 2011-2022 走看看