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>    
  • 相关阅读:
    BZOJ 3744 Gty的妹子序列
    BZOJ 3872 Ant colony
    BZOJ 1087 互不侵犯
    BZOJ 1070 修车
    BZOJ 2654 tree
    BZOJ 3243 向量内积
    1003 NOIP 模拟赛Day2 城市建设
    CF865D Buy Low Sell High
    CF444A DZY Loves Physics
    Luogu 4310 绝世好题
  • 原文地址:https://www.cnblogs.com/adhehe/p/9794168.html
Copyright © 2011-2022 走看看