Javascript事件总结(一)

2014-04-27 · 21 min read

最近在项目中使用原生的 javascript,不依赖任何类库开发通用组件,发现真的是“成也 jQuery,败也 jQuery”。离开 jQuery 都不会写 js 代码了,一个组件花费的大部分时间,在使用 jQuery 的情况下都可以分分钟搞定。这其中问题比较大的一块就是 javascript 事件,下面认真总结一下。

事件流#

第一个要提的概念就是事件流。在很久很久以前,浏览器的开发团队遇到一个问题:当用户点击一个a 标签的时候,他是否也点击了p 标签?是否也点击了div 呢…

<body>
    <div>
        <p><a href='1095.html'>Javascript事件总结<a></p>.html'
    </div>
</body>

IE 和 Netscape 的团队在这个问题上的认识是一致的:用户的点击事件不止发生在a 标签上,也发生在p div …上。那么总要有个顺序吧?事件流就是描述这个顺序的。但是 IE 和 Netscape 提出了相反的事件流概念,IE 的事件冒泡和 Netscape 的事件捕获

还拿上面的代码说,事件冒泡是由具体元素逐级传播到不具体的节点。就是a ->p ->div ->body ->html ->Document,所有现代浏览器都支持事件冒泡,IE9、chrome、firefox 还会传播到 window 对象(点击在线测试)。而事件捕获刚好相反,不具体的节点先接受事件,具体的事件后接受事件,它的用意是在事件到达预定目标之前捕获它,同样的,IE9、chrome、firefox 也是从 window 对象开始捕获的。

这两家浏览器厂商对着干,这时候我们的“规范”就要跳出来统一他们了,“DOM2 级事件”规范规定的事件流包含 3 个阶段:事件捕获阶段,处于目标元素的阶段和事件冒泡阶段,他们依次发生。但是规范只是规范,实际情况是,IE8 以及更早的版本不支持 DOM2 级事件,IE9、chrome、FireFox 会在捕获和冒泡两个阶段涉及目标元素,所以目标元素的事件有两次被触发的机会。

事件处理程序#

click,load 之类的是事件,那么响应事件的函数就是事件处理程序。为事件指定处理程序的方式有:HTML 事件处理程序,DOM0 级事件处理程序,DOM2 级事件处理程序,IE 事件处理程序。

HTML 事件处理程序#

代码如下:

<input type="button" value="Click Me" onclick="alert('click')" />

onclick 执行的函数封装了event 变量,可以直接访问事件对象。而this 就是事件的目标元素。例如:

<input type="button" value="Click Me" onclick="alert(event.type)" />
<input type="button" value="Click Me" onclick="alert(this.value)" />

onclick 后面这个动态创建的函数,可以直接访问 document 以及该元素本身的成员,如果当前元素是表单的一部分,还可以直接访问表单元素,因为它使用with 扩展了作用域。

function (){
    with(document){
        with(this.form){
            with(this){
                //...
            }
        }
    }
}

使用示例:

<form>
  <input type="text" name="username" />
  <input type="button" value="click" onclick="alert(username.value)" />
</form>

直接使用username.value 就可以访问好 name 为 username 的文本框的值,在线测试

DOM0 级事件处理程序#

代码如下:

var btn = document.getELementById('myBtn')
btn.onclick = function () {
  alert('click')
}

DOM0 级事件处理程序被当做是元素的方法,所以可以this 使用访问到元素的属性,例如用this.id 访问 ID。DOM0 级事件处理程序会在事件流的冒泡阶段被处理(第一个例子中就使用了 DOM0 级事件处理程序)。另外,也可以通过设置为null 来删除。

DOM2 级事件处理程序#

DOM2 级事件定义了两个方法,用来添加和删除处理程序:addEventListener()removeEventListener() 。接受三个参数:要处理的事件名,作为事件处理程序的函数和一个表示事件阶段的布尔值(true-事件捕获阶段,false-事件冒泡阶段)。这个很经常用就不多说了,只要注意 IE9+才支持就好了,有问题欢迎留言交流。

IE 事件处理程序#

IE 实现了与 DOM 类似的两个方法,:attachEvent()detachEvent() ,因为 IE8 只支持事件冒泡,所以这两个方法只有两个参数。例如:

var btn = document.getElementById('btn')
btn.attachEvent('onclick', function () {
  alert(1)
})

注意它的第一个参数是“onclick”,不是“click”。还有一点就是 this 不是当前元素,而是 window。

跨浏览器的事件处理程序#

最后给出跨浏览器的解决方案:

function addHandler(element, type, handler) {
  if (element.addEventListener) {
    element.addEventListener(type, handler, false)
  } else if (element.attachEvent) {
    element.attachEvent('on' + type, handler)
  } else {
    element['on' + type] = handler
  }
}

移除事件的兼容方案同理,我比较懒就不写了。