zoukankan      html  css  js  c++  java
  • BOM基础(四)

      最近写的文章感觉内容不像之前那么充实,内容可能也有点杂。对于DOM,和BOM来说,要理解是不难的,难的是做的时候。要自己想的到,而且,对于目前阶段来说,BOM还存在着很大的兼容性问题,最主要就是要兼容ie8。不过说实在的,用不了多久,ie8也差不多被淘汰了,新版本的ie浏览器对标准属性兼容性还是很好的。不过接下来要说的主题还是BOM中的一些内容。前一篇文章中主要讲了两种注册事件的方式和事件参数。本文主要讲如何移除事件,事件的冒泡。

      首先讲讲移除事件,移除事件用的是removeEventListener();移除事件一般都会配合addEventListener()一起使用,比如下述例子:

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <input type="button" value="click" id="btn"/>
    <script>
        var btn = document.getElementById("btn");
        btn.addEventListener("click", play, false);
        function play() {
            alert("lalala");
            btn.removeEventListener("click", play, false);
        }
    </script>
    </body>
    </html>

      我们给btn这个按钮添加了鼠标单击事件,当我们单击这个按钮时,执行play这个函数。在play函数中,我们先输出了一句话,之后,我们再给鼠标移除了执行函数为play的鼠标单击事件。这样,我们这段代码就实现了鼠标单击一次按钮之后会跳出一句话,但之后鼠标再次点击的时候就不会有这样的效果了。不过,这段代码还有兼容性的问题,就是在ie8及以下版本的浏览器中,是没有这个方法的,在ie8中,我们注册事件用的是attachEvent(),他有两个参数,前一个参数是事件的名称,第二个参数则是事件处理函数。不过于addEventListener不同的是,它的事件名称是带on的,也就是说,我们要用这种方式给元素添加一个鼠标单击事件的时候,参数名称要写成onclick,具体如下:

    btn.attachEvent("onclick", play);
    function play() {
        alert("lalala");
        btn.detachEvent("onclick", play);
    }

      同样的,我们给btn这个按钮添加了一个鼠标单击事件,当我们单击这个按钮时,会输出一句话,之后移除btn的执行函数为play的鼠标单击事件。这样,我们在ie8中测试,也会发现在单击鼠标的时候,会显示一句话,但之后再点击鼠标则没有效果了。这样我们就实现了在ie8中事件的添加和移除。不过,在实际的使用中,我们可能在代码中把这两段代码都添加进去,因为这两种方法可以说是互不兼容的,google中并不支持attachEvent()这种方法。而ie8及以下版本的浏览器则不支持addEventListener这种方法。所以,这时候我们就只能封装兼容性代码了。

    function addEvent(element, type, listener) {
        if (element.addEventListener) {
            element.addEventListener(type, listener, false);
        } else if (element.attachEvent) {
            element("on" + type, listener);
            listener.call(element);
        } else {
            element["on" + type];
        }
    }
    function removeEvent(element, type, listener) {
        if (element.removeEventListener) {
            element.removeEventListener(type, listener, false);
        } else if (element.detachEvent) {
            element.detachEvent(type, listener);
        } else {
            element["on" + type] = null;
        }
    }

      以上两个函数分别封装了添加事件和移除事件的兼容性代码,在使用的过程中,只要调用这两个函数就可以了。比如下述代码

    addEvent(btn, "click", play);
    function play() {
        alert("lalala");
        removeEvent(btn, "click", play);
    }

      这段代码跟最前面两段代码执行的结果是一样的,而且他们在googleie8中都能很好的兼容。这样,我们就封装了添加和移除事件的兼容性代码。

      说完了添加和移除事件,就来说说事件冒泡了。事件冒泡可能比较难理解,先看代码了解一下什么是事件冒泡。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            #box1 {
                width: 300px;
                height: 300px;
                
            }
            #box2 {
                 width: 200px;
                 height: 200px;
                 background-color: #00ff00;
             }
            #box3 {
                width: 100px;
                height: 100px;
                background-color: #0000ff;
            }
        </style>
    </head>
    <body>
        <div id="box1">
            <div id="box2">
                <div id="box3">
                </div>
            </div>
        </div>
        <script>
            var box1 = document.getElementById("box1");
            var box2 = document.getElementById("box2");
            var box3 = document.getElementById("box3");
            var elements = [box1, box2, box3, document.body, document];
            for(var i = 0, length = elements.length; i < length; i++) {
                var element = elements[i];
                element.addEventListener("click",test , false);
            function test() {
                console.log(this);
            }
        </script>
    </body>
    </html>

      上述代码中,我们定义了三个盒子,并且给box1,box2,box3,body,document都注册了鼠标单击事件。当我们单击box3的时候,控制台中输出的结果是

      

      我们发现,我们在点击box3的时候,不但输出了box3,还输出了box2,box1,bodydocument。这是为什么呢?这时候,就涉及了事件冒泡。在说这个之前,我们先讲一讲事件执行的阶段:

     

      如上图,事件在被触发的时候分三个阶段,首先是事件捕获,就是从document起一层层遍历下来找到事件触发的元素,然后目标阶段,也就是正在执行的当前对象的事件处理程序。当执行玩之后,就会发生事件冒泡。拿上述代码来说,我们点击了box3,在经过了事件捕获阶段之后,就开始执行box3中的执行函数,只时候,就输出了box3。然后,事件进入到冒泡阶段,发现box2也有鼠标单击事件,这时候,执行box2中的执行函数,所以执行了一次box2中的执行函数,之后事件再冒泡到box1,发现他也有单击事件,这时候,执行box1中的执行函数,就这样一级级往上知道document为止。所以一共输出了5个内容。这就是事件冒泡。事件冒泡有他的好处,也有他的坏处,先来说说它的好处。既然事件会冒泡,那么我们就可以利用事件冒泡来做一些事情,比如事件委托。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <ul id="names">
            <li id="ll">11111</li>
            <li id="lw">22222</li>
            <li id="lt">33333</li>
            <li id="xc">44444</li>
        </ul>
        <input type="button" value="插入" id="btn">
        <script>
            var ul = document.getElementById("names");
            ul.addEventListener("click", function (e) {
                alert(e.target.innerText);
            }, false);
            var btn = document.getElementById("btn");
            btn.onclick = function () {
                var li = document.createElement("li");
                li.innerText = "55555";
                ul.appendChild(li);
            }
        </script>
    </body>
    </html>

      在上述代码中,我们希望给每个li注册一个鼠标点击事件。按照正常的思路,我们就会遍历每个li,然后给每个li注册一个鼠标单击事件。这样的方法有很多坏处,首先,如果li很多的话,我们就要注册很多个事件,如果我们使用匿名函数注册的话,每多一个li就要多一个匿名函数,虽然 我们可以在外面先定义一个函数,然后在赋给鼠标单击事件。这样可以增加他的效率。不过,我们还会遇到另一个问题,就是我们如果想动态的创建一个函数的时候,我们要重新给他们添加事件,这样会很麻烦。这个时候,如果我们把这个鼠标单击事件委托给他们的父元素ul,在点击ul时,获取点击的那个li,这样,我们就只要给一个ul注册事件就好了,不用循环给每个li来添加事件。这里用到了一个知识点就是e.target,他获取的始终是当前触发事件的元素。这样,我们就实现了事件的委托。不过这个方法也是有兼容性问题。在ie8中,我们只能使用srcElement  ==  target这样的方式来获取当前事件触发的元素。所以,我们又要封装兼容性代码了,不过这个还是比较简单的。注意,这边我就不重复事件参数e的兼容性代码,这个在我BOM基础(三)的文章中提到过。

    getTarget: function (e) {
        return e.target ? event.target : e.srcElement;
    },

      既然说了事件冒泡的好处,就该来说说事件冒泡的坏处了。

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style>
            #box1 {
                width: 200px;
                height: 200px;
                
            }
    
            #box2 {
                width: 200px;
                height: 200px;
                background-color: red;
                display: none;
            }
        </style>
    </head>
    <body>
    <div id="box1"></div>
    <div id="box2"></div>
    <script>
        var box1 = document.getElementById("box1");
        var box2 = document.getElementById("box2");
        box1.onclick = function () {
            box2.style.display = "block";
        }
        document.onclick = function () {
            box2.style.display = "none";
        }
    </script>
    </body>
    </html>

      在上述代码中,我们希望点击box1box2显示,点击文档让box2隐藏。按正常的思路来说,我们会想到上述代码,不过,在尝试中我们发现,不管我们怎么点box1box2都不会显示,这就是因为时间冒泡了,在我们点击box1的时候,执行了它的函数,然box2显示,当他完成了之后,事件开始冒泡,发现document也有一个鼠标单击事件,这时候,又执行他里面的代码,让box2隐藏。这个过程非常快,所以我们根本看不出来box2显示过。不过这不是我们想要的效果,所以,我们就要对其中的过程进行改进,就是在单击box1的时候,不让他冒泡。这时候,我们就又要用到事件参数的一个方法了就是e.stopPropagation();

    box1.onclick = function (e) {
        box2.style.display = "block";
        e.stopPropagation();
    }

      这样,我们就阻止了事件冒泡和捕获,实现了我们想要的效果,不过,这个方法在ie8中还是有兼容性问题,他用的是event.cancelBubble=true来阻止事件的冒泡,在ie8中,没有事件的捕获这个过程。这时候,我们又要封装兼容性代码了。

     function stopPropagation(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }

       到这里,这件冒泡就完成了。

  • 相关阅读:
    Codeforces 845E Fire in the City 线段树
    Codeforces 542D Superhero's Job dp (看题解)
    Codeforces 797F Mice and Holes dp
    Codeforces 408D Parcels dp (看题解)
    Codeforces 464D World of Darkraft
    Codeforces 215E Periodical Numbers 容斥原理
    Codeforces 285E Positions in Permutations dp + 容斥原理
    Codeforces 875E Delivery Club dp
    Codeforces 888F Connecting Vertices 区间dp (看题解)
    Codeforces 946F Fibonacci String Subsequences dp (看题解)
  • 原文地址:https://www.cnblogs.com/wqc5730/p/5782215.html
Copyright © 2011-2022 走看看