JavaScript与HTML交互系列--事件流(冒泡、捕获、应用)和事件处理程序

事件流

当浏览器发展到第四代时(IE4及Netscape Communicator4),浏览器开发团队遇到了一个颇有意思的问题:页面的哪一部分会拥有某个特定的事件?要明白这个问题问的是什么,能够想象画在一张纸上的一组同心圆。若是你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上的全部圆。两家公司的浏览器开发团队在看待浏览器事件方面仍是一致的。若是你单击了某个按钮,他们都认为单击事件不单单发生在按钮上。换句话说,在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。javascript

事件流 描述的是从页面中接收事件的顺序。但有意思的是,IE和Netscape开发团队竟然提出了差很少是彻底相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。html

思考:点击页面元素,什么样的元素能感应到这样一个事件?java

点击页面元素的同时,也点击了元素的容器元素甚至整个页面。浏览器

事件冒泡

IE的事件流叫作事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,而后逐级向上传播到较为不具体的节点(文档)。函数

事件捕获

Netscape Communicator 团队提出的另外一种事件流叫作事件捕获(event capturing)。其思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预订目标以前捕获它。ui

DOM 事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。而后是实际的目标接收到事件。最后一个阶段是冒泡阶段,(通常状况下,程序)能够在冒泡阶段对事件做出响应。this

事实上,DOM事件流在捕获阶段和冒泡阶段都可触发事件处理程序。捕获阶段先收到事件流spa

对上面元素添加事件绑定程序。

var outBox = document.getElementById("outBox");
var middleBox = document.getElementById("middleBox");
var innerBox = document.getElementById("innerBox");

outBox.addEventListener('click', function() {
    console.log('outBox捕获');
}, true);
middleBox.addEventListener('click', function() {
    console.log('middleBox捕获');
}, true);
innerBox.addEventListener('click', function() {
    console.log('innerBox捕获');
}, true);

outBox.addEventListener('click', function() {
    console.log('outBox冒泡');
}, false);
middleBox.addEventListener('click', function() {
    console.log('middleBox冒泡');
}, false);
innerBox.addEventListener('click', function() {
    console.log('innerBox冒泡');
}, false);

// 输出顺序
// 1 outBox捕获
// 2 middleBox捕获
// 3 innerBox捕获
// 4 innerBox冒泡
// 5 middleBox冒泡
// 6 outBox冒泡
复制代码

IE九、Opera、Firefox、Chrome和Safari(也就是说当下主流浏览器)都支持DOM事件流;IE8及更早版本不支持DOM事件流。代理

思考一下:事件冒泡和事件捕获在平常开发中有哪些应用场景?code

咱们下面先看一下事件处理程序。

事件处理程序

1. HTML 事件处理程序

为元素添加(事件)属性,值为事件处理程序。

注:事件属性所有为小写。

<div class="outBox" onclick="alert('outBox')">
    <div class="innerBox" onclick="showInnerBox()"></div>
</div>

<script> function showInnerBox() { alert("innerBox"); } </script>
复制代码

特色:传统方式,简单,跨浏览器

2. DOM0 级事件处理程序

使用JavaScript指定事件处理程序,首先必须取得一个要操做的对象的引用。

<div class="outBox" id="outBox">
    <div class="innerBox" id="innerBox"></div>
</div>

<script> var outBoxElement = document.getElementById('outBox'); outBoxElement.onclick = function() { alert(this.id) } // 删除事件处理程序 outBoxElement.onclick = null </script>
复制代码

特色:可经过this访问元素的任何属性和方法(HTML事件处理程序也能够),能够删除事件处理程序

3. DOM2 级事件处理程序

“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操做:addEventListener()removeEventLintener()。全部DOM结点中都包含这两个方法,而且他们都接受3个参数:要处理的事件名、做为事件处理程序的函数和一个布尔值。最后这个布尔值若是是true,表示在捕获阶段调用事件处理程序;若是是false,表示在冒泡阶段调用事件处理程序。

// 添加事件绑定
outBoxElement.addEventListener('click', showOutBox, false);
// 移除事件绑定
outBoxElement.removeEventListener('click', showOutBox);
// 下面移除事件绑定方式无效,没法移除绑定的匿名函数
outBoxElement.removeEventListener('click', function() {
    alert(this.id);
});
复制代码

4. IE 事件处理程序

IE实现了与DOM中相似的两个方法:attachEvent() 和 detachEvent()。这两个方法接受相同的个参数:事件名称和事件处理函数。因为IE8及更早版本只支持事件冒泡,因此经过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。

var btn = document.getElementById('MyBtn');
btn.attachEvent('onclick', function() {
    console.log('clicked');
});

var handler = function() {
    console.log('clicked');
}
// 第二个参数必须是具名函数,不能是匿名函数
btn.detachEvent('onclick', handler);
复制代码

注:attachEvent() 的第一个参数是'onclick',而非DOM的addEventListener()方法中的'click'。

在IE中使用attachEvent()与使用DOM0级方法的主要区别在于时间处理程序的做用域。在使用DOM0级方法的状况下,事件处理程序会在其所属元素的做用域内运行;在使用attachEvent()方法的状况下事件处理程序会在全局做用域中运行,所以this等于window

btn.attachEvent('onclick', function() {
    console.log(this === window); // true
})
复制代码

此外,attachEvent() 与addEventListener() 相似都可觉得一个元素添加多个事件处理程序。

// 为同一个元素添加多个事件处理程序
btn.addEventListener('click', function() {
    console.log('clicked');
}, false);
btn.addEventListener('click', function() {
    console.log('hello world');
}, false);

btn.attachEvent('onclick', function() {
    console.log('clicked');
});
btn.attachEvent('onclick', function() {
    console.log('hello world');
});
复制代码

5. 跨浏览器事件处理程序

跨浏览器事件处理程序即一套能够同时运行在IE以及非IE浏览器的程序,重点是浏览器能力检测也就是浏览器兼容性检查。

var EventUtil = {
    // 添加事件绑定(元素,事件类型,事件处理函数)
    addHandler: function(element, type, handler) {
        if(element.addEventListenter) {
            // DOM2级绑定事件
            element.addEventListenter(type, handler, false);
        }else if(element.attachEvent) {
            // IE绑定事件
            element.attachEvent('on' + type, handler)
        }else {
            // DOM0级绑定事件,兼容IE8及更早版本
            element['on' + type] = handler;
        }
    },
    // 删除事件事件绑定(元素,事件类型,事件处理函数)
    removeHandler: function() {
        if(element.addEventListenter) {
            // DOM2级删除绑定事件
            element.removeEventListenter(type, handler, false);
        }else if(element.attachEvent) {
            // IE删除绑定事件
            element.detachEvent('on' + type, handler)
        }else {
            // DOM0级删除绑定事件,兼容IE8及更早版本
            element['on' + type] = null;
        }
    }
}

var btn = document.getElementById('MyBtn');
var handler = function() {
    console.log('clicked');
}
// 添加绑定事件
EventUtil.addHandler(btn, 'click', handler);
复制代码

事件冒泡应用-事件委托/代理

对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就能够管理一类型的全部事件。例如,click事件会一直冒泡到document层次。也就是说咱们能够为整过页面(通常是某几个元素的父元素)指定一个onclick事件处理程序,而没必要给每一个可单击的元素分别添加事件处理程序。

<ul id="items">
    <li id="item1">item1</li>
    <li id="item2">item2</li>
    <li id="item3">item3</li>
</ul>
复制代码
var itemList = document.getElementById('items');
EventUtil.addHandler(itemList, 'click', function(e) {
    var id = e.target.id;
    switch(id) {
        case "item1":
            alert('item1 is clicked!');
            break;
        case "item2":
            alert('item2 is clicked!');
            break;
        case "item3":
            alert('item3 is clicked!');
            break;
        case "items":
            alert('items is clicked!');
            break;
    }
});
复制代码

总结

这是一篇关于JavaScript中事件相关介绍的基础文章,方便咱们快速温习。

参考文章

javaScript事件(一)事件流

相关文章
相关标签/搜索