事件
- 含义
JavaScript和HTML的交互是通过事件实现的。JavaScript采用异步事件驱动编程模型,当文档、浏览器、元素或与之相关对象发生特定事情时,浏览器会产生事件。 - 事件类型
- 当用户点击鼠标时
- 当网页已加载时
- 当图像已加载时
- 当鼠标移动到元素上时
- 当用户触发按键时
- .....
事件流
事件流描述的是从页面中接收事件的顺序。
-
事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素。
-
事件捕获:不太具体的节点更早接收事件,而最具体的元素最后接收事件,和事件冒泡相反。
-
DOM事件流:DOM2级事件规定事件流包括三个阶段,事件捕获阶段,处于目标阶段,事件冒泡阶段,首先发生的是事件捕获,为截取事件提供机会,然后是实际目标接收事件,最后是冒泡阶段。
图上:123处于事件捕获阶段,4处于目标阶段,4567处于事件冒泡阶段。
DOM2级事件规范明确要求捕获阶段不会涉及时间目标,但IE9、Safari、Firefox、Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。
事件处理程序(绑定事件)
- HTML内联方式
<input type = 'button" value = "Click me" onclick = "alert('clicked')">
事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。
- 缺点:
a: 存在时差问题,因为用户可能会在HTML元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能不具备执行条件;
b: 扩展事件处理程序的作用域链在不同的浏览器中会导致不同的结果;
c: HTML与JavaScript代码紧密耦合。
- JavaScript指定事件处理程序
- DOM0级事件
- 将一个函数值赋值给一个事件处理程序属性。使用DOM0级方法指定的事件处理程序被认为是元素的方法,所以处理程序是在元素的作用域中运行。
var btn = document.getElementById('btn')
btn.onclick = function(){
console.log(this)//指触发元素btn
}
- 删除DOM0级事件处理程序的方法:
btn.onclik = null;
对象属性重复赋值会覆盖原先的值,所以只能绑定一个事件。
- DOM2级事件
- DOM2级事件具有两个方法:
addEventListener()
和removeEventListener()
。这两个方法接收同样的三个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。其中,布尔值为true
表示在捕获阶段调用事件处理程序,布尔值为false
表示在冒泡阶段调用事件处理程序。默认值false
。因为函数可以重复执行,所以可以添加许多事件监听。
var btn = document.getElementById('btn')
btn.addEventListener('click',function(){
console.log(this)
},false)
btn.addEventListener('click',function(){
console.log('hello')
},false)
执行顺序与添加事件处理程序的顺序一致。
DOM2级事件的作用域是元素的作用域,所以this指代触发元素。
- 删除DOM2级事件处理程序:
removeEventListener()
与addEventListener()
参数要完全一致,所以事件处理程序函数为匿名函数的时候,则无法删除。
var btn = document.getElementById('btn')
btn.addEventListener('click',function(){
console.log(this)
},false)
//下面的删除函数无法删除
var btn = document.getElementById('btn')
btn.removeEventListener('click',function(){
console.log(this)
},false)
//解决办法
var handler = function(){console.log(this)}
var btn = document.getElementById('btn')
btn.addEventListener('click',handler,false)
btn.removeEventListener('click',handler,false)
- 关于this和事件
<body>
<div class = "box">
<button>click me</button>
</div>
<script>
var box = document.querySelector('.box')
var button = document.querySelector('button')
//事件可以作为参数传递进来
button.addEventListener('click',function(e){
console.log(this)//监听事件的元素
console.log(this.innerText)
console.log(e)//事件其实是一个对象
console.log(e.target)//事件的触发元素btn(真正触发事件的元素)
})
box.addEventListener('click',function(e){
console.log(this)//代表监听事件的元素
console.log(this.innerText)
console.log(e)//事件其实是一个对象
console.log(e.target)//事件的触发元素btn(真正触发事件的元素)
</script>
</body>
- IE兼容事件
- IE事件处理程序具有两个方法:
attachEvent()
和detachEvent()
。具有两个完全相同的参数:事件处理程序名称和事件处理程序函数。此事件处理程序是在全局作用域下运行的,所以this指代window
。可以添加多个事件处理程序。
var btn = document.getElementById('btn')
btn.attachEvent('onclick',function(){
console.log(this) // window
})
btn.attachEvent('onclick',function(){
console.log('hell')
})
事件处理程序的执行顺序是事件处理程序添加顺序的反顺序。
- 删除事件处理程序
btn.attachEvent('onclick',handler)
btn.detachEvent('onclick',handler)
addEventListener()和attachEvent()的区别:
- 参数个数不同:
addEventListene
r有三个参数,attachEvent
只有两个,attachEvent
添加的事件处理程序只能发生在冒泡阶段,addEventListener
第三个参数可以决定添加的事件处理程序是在捕获阶段还是冒泡阶段处理(我们一般为了浏览器兼容性都设置为冒泡阶段)。
2.第一个参数含义不同:addEventListener
第一个参数是事件类型(比如click,load
),而attachEvent
第一个参数指明的是事件处理函数名称(onclick,onload
)。 - 事件处理程序的作用域不同:
addEventListener
的作用域是元素本身,this
是指的触发元素,而attachEvent
事件处理程序会在全局变量内运行,this
是window
。· - 为一个事件添加多个事件处理程序时,执行顺序不同:
addEventListener
添加会按照添加顺序执行,而attachEvent
添加多个事件处理程序时顺序无规律(添加的方法少的时候大多是按添加顺序的反顺序执行的,但是添加的多了就无规律了),所以添加多个的时候,不依赖执行顺序的还好,若是依赖于函数执行顺序,最好自己处理,不要指望浏览器。
this、target、currentTarget的区别:
DOM0级事件和DOM2级事件,作用是元素作用域,this指代的是触发事件的元素。IE事件处理程序,作用域是全局作用域,this
指代的是window
。currentTarget
代表其事件处理程序当前正在处理的事件的那个元素。对象this
始终等于currentTarget
。而target
则只包含事件的实际目标。
<div id = "ct">
<button id = "btn">click me</button>
</div>
<script>
var btn = document.getElementById('btn')
btn.addEventListener('click',function(){
console.log(this)
},false)
//this = target = currentTarget = btn
var div = document.getElementById('ct')
div.addEventListener('click',function(){
console.log(this)
},false)
// this = current = <div>...</div>
// target = btn
</script>
事件可以作为参数传递进来
<button>click me</button>
<script>
var button = document.querySelector('button')
button.addEventListener('click',function(e){
console.log(e)//e事件其实是一个对象
})
</script>
此截图为部分截图
事件的封装(兼容所有的浏览器)
- 封装方法一:(不好,不建议采纳)
//函数的封装,使其在IE浏览器下也能运行(浏览器的兼容)
function addEvent(node,type,handler){
//元素不存在,直接退出
if(!node) return false;
//非IE下运行
if(node.addEventListener){
node.addEventListener(type,handler,false);
return true;
}
//IE下运行
if(node.attachEvent){
node.attachEvent('on' + type,handler,);
return true
}
return true
}
var node = document.getElementsByClassName('.box')[0]
addEvent(node,'click',function(e){
console.log(e)
console.log(this)//在IE下,this代表的是window,所以采用此种封装不好
})
- 更好的封装方法
<script>
function addEvent(node,type,handler) {
if(!node) return false;
if(node.addEventListener) {
node.addEventListener(type,handler,false)
return true
}
else if(node.attachEvent) {
node['e' + type + handler] = handler
node[type + handler] = function(){
node['e' + type + handler](window.event)
}
// 此处的作用是在全局作用域中,this也可以指触发元素而不是window
//此种写法也可以把事件作为参数e传递进来
node.attachEvent('on' + type,node[type + handler])
return true
}
else {
node['on' + type] = handler
}
}
var node = document.getElementsByClassName('.box')[0]
addEvent(node,'click',function(e){
console.log(e)
console.log(e.target)
console.log(this)
})
</script>
- 代码解析
node['eclickfunction(){xxx}'] = function(e){
console.log(e)
console.log(this)
}
node['clickfunction(){xxx}'] = function(){
node['eclickfunction(){xxx}'](window.event)
}