JavaScript基础概念之----事件冒泡/捕获/委托

事件流javascript

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

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

<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事件流原理图 :app

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

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

<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元素,控制台打印以下图:spa

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

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

<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元素,以下图控制台输出:事件

一、全部在目标元素上绑定的事件,都会发生在目标阶段

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

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>    
相关文章
相关标签/搜索