JavaScript 与 HTML 之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器来预订事件,以便事件发生时执行相应的代码。这也叫观察者模式。
事件流
“DOM2级事件” 规定事件包括三个阶段:事件捕获阶段,处于目标阶段,事件冒泡阶段
事件冒泡 即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上到较为不具体的节点(文档)  
事件捕获 是由最不具体的节点到最具体的节点

事件处理程序
事件就是用户或浏览器自身执行的某种动作。例如click,load,mouseover等,都是事件的名字,而响应某个事件的函数叫事件处理程序(或侦听器)
DOM0级事件处理程序
DOM0级事件处理程序 通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
比如:
var btn = document.getElementById("myBtn")
btn.onclick = function(){
    alert(this.id)          // myBtn
}
使用DOM0级方法指定的事件处理程序被认为是元素的方法,这时候事件的处理程序实在元素的作用域中运行的;换句话说,程序的this引用当前元素。
也可以删除通过 DOM0级方法 指定的事件处理程序,只要将对应的值设为null即可
btn.onclick = null      // 删除事件处理程序
DOM2级事件处理程序
DOM2级事件处理程序 定义两个方法,用于处理指定和删除事件处理程序的操作:
addEventListener() 和 removeEventListener()。
他们都接受3个参数 : 要处理的事件名、作为事件处理程序的函数、一个布尔值。
最后一个布尔值为true,表示在捕获阶段调用事件处理程序
如果为false,表示再冒泡阶段调用事件处理程序
要在按钮为click事件添加事件处理程序:
var btn = document.getElementById("myBtn")
btn.addEventListener("click", function(){
    alert(this.id)
}, false)
通过 addEventListener() 添加的事件处理程序只能通过 removeEventListener() 来移除,移除时出入的参数与添加处理程序时的参数相同,这意味着通过 addEventListener() 添加的匿名函数无法被移除,比如:
var btn = document.getElementById("myBtn")
btn.addEventListener("click", function(){
    alert(this.id)
}, false)
//  .....
btn.removeEventListener("click", function(){    // 没有用
    alert(this.id)
}, false)
上面例子中,addEventListener() 添加了一个事件处理程序,removeEventListener() 看似传递了相同的函数,实际是不一样的。必须传入相同的参数才有用。比如:
var btn = document.getElementById("myBtn")
var handler = function(){
    alert(this.id)
}
btn.addEventListener("click", handler, false)
// ...
btn.removeEventListener("click", handler, false)
大多数情况下,都是将事件处理函数添加到事件流的冒泡阶段,这样可以最大限度的兼容各种浏览器。
IE 事件处理程序
IE事件处理程序 IE实现了与DOM中类似的方法:
attachEvent() 和 detachEvent()
这两个方法接受两个参数:事件处理程序名称、事件处理函数。由于 IE8 及更早的版本只支持事件冒泡,所以用 attachEvent() 添加的事件处理程序都会被添加到冒泡阶段
var btn = document.getElementById("myBtn")
// 注意 第一个参数为 "onclick"
btn.attachEvent("onclick", function(){
    alert("Clicked")
})
在 IE 中使用 attachEvent() 与 使用 DOM0级 方法主要区别在作用域。
DOM0级中,事件处理程序会在其所属元素的作用域内运行
 attachEvent() 中,事件处理程序在全局中运行,this 相当于window
var btn = document.getElementById("myBtn")
btn.attachEvent("onclick", function(){
    alert(this === window)      // true
})
为同一个按钮添加不同的事件处理程序,与 DOM方法不同的是,这些事件处理程序不是按照添加他们的顺序执行,而是以相反的顺序执行
var btn = document.getElementById("myBtn")
btn.attachEvent("onclick", function(){
    alert("Clicked")
})
btn.attachEvent("onclick", function(){
    alert("Hello world !")
})
// 这里首先会看到 Hello world,然后才是 Clicked
使用 attachEvent() 添加的事件处理程序可以通过 detachEvent() 来移除,必须提供相同的参数。
var btn = document.getElementById("myBtn")
var handler = function(){
    alert("Clicked")
}
btn.attachEvent("onclick", handler)
// ...
btn.detachEvent("onclick", handler)
跨区域的事件处理程序
第一个要创建的方法为 addHandler() 它的职责是视情况分别使用 DOM0级、DOM2级方法或者 IE 方法来添加事件。接受3个参数:要操作的对象、事件名称、事件处理程序函数
与之对应的是 removeHandler() 也接受相同的方法
var EventUtil = {
    addHandler : function(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
        }
    },
    removeHandler : function(element, type, handler){
        if(element.removeEventListener){
            element.removeEventListener(type, handler, false)
        } else if (element.detachEvent){
            element.detachEvent("on" + type, handler)
        } else {
            element["on" + type] = null
        }
    }
}
可以像下面这样使用 EventUtil 对象
var btn = document.getElementById("myBtn")
var handler = function(){
    alert("Clicked")
}
EventUtil.addEventHandler(btn, "click", handler)
// ...
EventUtil.removeEventHandler(btn, "click", handler)
事件对象
在触发 DOM 上的某个事件,会产生一个事件对象event,这个对象包含所有与事件有关的信息。
DOM中的事件对象
兼容DOM的浏览器会将一个 event 对象传入到事件处理程序中。
var btn = document.getElementById("myBtn")
btn.onclick = function(event){
    alert(event.type)       // "click"
}
btn.addEventListener("click", function(){
    alert(event.type)       // "click"
}, false)
event 包含与创建它的特定事件有关的属性和方法
bubbles
表明事件是否冒泡
cancelable
表明是否取消事件的默认行为
currentTarget
某事件处理程序当前正在处理的那个元素
defaultPrevented
为 true 表明已经调用了preventDefault(DOM3新增)
eventPhase
调用事件处理程序的阶段:1 捕获;2 处于阶段;3 冒泡阶段
这个属性的变化需要在断点中查看,不然你看到的总是<0br></0br>
target
事件目标
trusted
true表明是系统的,false为开发人员自定义的(DOM3新增)
type
事件类型
view
与事件关联的抽象视图,发生事件的window对象
preventDefault
取消事件默认行为,cancelable是true时可以使用
stopPropagation
取消事件捕获/冒泡,bubbles为true才能使用
stopImmediatePropagation
取消事件进一步冒泡,并且组织任何事件处理程序被调用(DOM3新增)
在我们的事件处理内部,this与currentTarget相同
createEvent
可以在document对象上使用createEvent创建一个event对象
在事件处理程序内部,对象 this 始终等于 currentTarget 的值,而 target 只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则 this、currentTarget
和 target 包含相同的值。
var btn = document.getElementById("myBtn")
btn.onclick = function(event){
    alert(event.currentTarget === this)         // true
    alert(event.target === this)        // true
}
preventDefault() 方法用于组织特定事件的默认行为
stopPropagation() 方法用于立即停止事件在 DOM 层次中的传播,取消进一步的事件捕获/冒泡
如果事件处理程序存在于按钮的父节点中(例如document.body),那么那些值不相同。
document.body.onclick = function(event){  
    alert(event.currentTarget === document.body)        // true
    alert(this === document.body)       // true
    alert(event.target === document.getElementById("myBtn"))        // true
}
IE 中的事件对象
与访问 DOM中的 event 对象不同,要访问IE中的 event 对象有几种不同的方式,取决于指定事件处理程序的方法。
在 DOM0级 方法添加事件对象处理程序时,event 对象作为 window 对象的一个属性存在。
var btn = document.getElementById("myBtn")
btn.onclick = function(){
    var event = window.event
    alert(event.type)       // "click"
}
使用 attachEvent() 方法添加的事件处理程序,会有一个 event 对象作为参数被传入事件处理程序
var btn = document.getElementById("myBtn")
btn.attachEvent("onclick", function(event){
    alert(event.type)       // "click"
})
IE 中的 event 对象同样也包括与创建它的事件相关的属性和方法。
cancelBubble
默认值为 false,将其设置为 true 可以取消冒泡
returnValue
默认值为 true,将其设置为 false 就可以取消事件的默认行为
srcElement
事件的目标
type
被触发事件的类型
returnValue 属性相当于 DOM中的 preventDefault() 方法,取消给定事件的默认行为。只要将 returnValue 设置为 false,就可以阻止默认行为
var link = document.getElementById("myLink")
link.onclick = function(){
    window.event.returnValue = false
}
cancelBubble 属性与 DOM中的 stopPropagation() 方法相似,都是用来停止冒泡的。IE 不支持事件捕获,因而只能取消事件冒泡
var btn = document.getElementById("myBtn")
btn.onclick = function(){
    alert("clicked")
    window.event.cancelBubble = true
}
跨浏览器的事件对象
可以对之前介绍的 EventUtil 对象加强,添加如下方法以求同存异
var EventUtil = {
    addHandler : function(element, type, handler){
        // 省略的代码
    },
    getEvent : function(event){
        return event ? event : window.event
    },
    getTarget : function(event){
        return event.target || event.srcElement
    },
    preventDefault : function(event){
        if(event.preventDefault){
            event.preventDefault()
        } else {
            event.returnValue = false
        }
    },
    removeHandler : function(){
        // 省略的代码
    },
    stopPropagation : function(event){
        if(event.stopPropagation){
            event.stopPropagation()
        } else {
            event.cancelBubble = true 
        }
    }
}
以上代码添加了4个新方法,
第一个 getEvent(),获取事件对象
btn.onclick = function(event){
    event = EventUtil.getEvent(event)
}
第二个 getTarget(),返回事件目标
btn.onclick = function(event){
    event = EventUtil.getEvent(event)
    var target = EventUtil.getTarget(event)
}
第三个 preventDefault(),用于取消事件的默认行为
var link = document.getElementById("myLink")
link.onclick = function(event){
    event = EventUtil.getEvent(event)
    EventUtil.preventDefault(event)
}
第四个 stopPropagation(),组织事件流
btn.onclick = function(event){
    alert("Clicked")
    event = EventUtil.getEvent(event)
    EventUtil.stopPropagation(event)
}
