BOM基础(四)

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

  首先讲讲移除事件,移除事件用的是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,具体以下:app

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中都能很好的兼容。这样,咱们就封装了添加和移除事件的兼容性代码。this

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

<!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的时候,控制台中输出的结果是spa

  

 

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

 

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

<!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;
    }
}

 

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

相关文章
相关标签/搜索