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 οnclick="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>    

 

转载于:https://www.cnblogs.com/adhehe/p/9794168.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值