什么是事件?
事件是可以被js侦测到的行为,当用户与web页面进行交互时,解释器就会创建响应的event对象来描述事件。
常见的事件有哪些?
- 点击页面上某个元素
- 鼠标经过特定的元素
- 按下键盘上某个按键
- 滚动窗口或改变窗口大小
- 页面元素加载完成或失败
一、主流浏览器 dom2级事件定义(包括IE9+)
1.1 事件定义三种方式
这里重点讲dom2级事件
- 在html元素中定义事件
<button onclick="alert('hello')">按钮</button>
缺点:html中写js代码。强耦合,不利于服用代码,违反了内容与行为相分离的原则。
- dom0级事件
事件对象的属性添加绑定事件。
document.getElementById('btn').onclick = function(){}
document.body.onload = init;
function init(){ ....}
优点:兼容所有浏览器包括IE6~IE8,可以进行事件模拟.
缺点:同一个事件只能绑定一个监听函数
- dom2级事件:addEventListener
btn.addEventListener('click',function(){},false); 主流浏览器
btn.attachEvent('onclick',function(){}) IE
优点:高级事件处理方法,一个事件可以绑定多个函数。
缺点:浏览器的兼容性。
1.2 dom2级事件
dom2级事件的优点:
- 允许事件对象绑定多个同种事件
- 事件捕获
- 事件冒泡
1.2.1 绑定事件addEventListener()
功能:添加的事件函数
语法:ele.addEventListener(event, functiion, useCapture)
参数说明:
- event 必选。字符串,要移除的事件名称。
- function 必选,指定要移除的函数。
- useCapture 可选,布尔值,默认为false(事件冒泡),true(事件捕获)
addEventListener()可以绑定多个同类型事件。
var btn = document.getElementById('btn');
btn.addEventListener('click',function(){alert(1)},false);
btn.addEventListener('click',function(){alert(2)},false);
1.2.2 移除事件removeEventListener()
功能:移除addEventListener()
方法添加的事件函数
语法:ele.removeEventListener(event, functiion, useCapture)
参数说明:
- event 必选。字符串,要移除的事件名称。
- function 必选,指定要移除的函数。
- useCapture 可选,布尔值,默认为false,指定移除事件句柄的阶段。
移除事件的坑
如果想要用removeEventListener()
进行事件解绑,那就在绑定事件的时候不能使用匿名函数。
var btn = document.getElementById('btn');
btn.addEventListener('click',function(){alert(1)},false);
btn.removeEventListener('click',function(){alert(1)},false);
上面的解绑事件看似解绑的是同一个除了函数,但是这两个匿名函数不是同一个,只有使用function
和var
声明的函数,才能拿到函数体的地址,指向同一个函数。
var fn = function(){alert(1)};
btn.addEventListener('click',fn,false);
btn.removeEventListener('click',fn,false);
二、IE6~IE8 dom2级事件定义
这里的方法只有 IE6~IE8支持。
只有事件冒泡,没有事件捕获。
2.1 添加事件attachEvent()
属于dom2级事件,也是可以绑定多个事件处理函数。
语法
ele.attachEvent(event,function);
参数说明:
- event 必须,指定事件名,必须添加
on
前缀。 - function 必须,指定事件触发时的执行函数。
- 没有第三个参数,因为ie6~ie8不支持事件捕获。
2.2 移除事件detachEvent()
移除attachEvent()
方法添加的监听函数。
语法
ele.detachEvent(event, function);
参数说明:
- event 必须,指定事件名,必须添加
on
前缀。 - function 必须,指定事件触发时的执行函数。
- 没有第三个参数,因为ie6~ie8不支持事件捕获。
2.3 IE8中this
的指向问题
在IE8及更早版本浏览器 中dom2级事件中的this
指向的不是当前对象,而是window
var btn = document.getElementById('btn');
var test = function(){
alert(this === window); true
}
EventUtil.addHandler(btn,'click',test);
三、跨浏览器事件处理
封装一套兼容低版本浏览器和主流浏览器的事件添加和移除的代码。
EventUtil这个对象可以直接写在单独的js中引用即可
var EventUtil = {
addHandler: function(element, type, handler){
if(element.addEventListener){ //主流浏览器
element.addEventListener(type, handler, false);
}else if(element.attachEvent){ //ie8以下
element.attachEvent('on'+type, handler);
}else{
element['on'+type] = null;
}
},
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;
}
}
};
var btn = document.getElementById('btn');
var test = function(){
alert(1);
}
EventUtil.addHandler(btn,'click',test);
EventUtil.removeHandler(btn,'click',test);
四、事件冒泡与事件捕获
- 事件冒泡(大多数情况都是使用事件冒泡)
事件对象沿dom树向上传播,目标触发运行事件监听函数。
主流浏览器:addEventListener(type, function, false) 或者 addEventListener(type, function);
IE6~IE8:attachEvent(type, function);
- 事件捕获(几乎不用)
事件对象沿dom树向下传播,目标触发运行事件监听函数。
addEventListener(type, function, true);
4.1 事件冒泡
直系亲属树结构中,点击某个元素,由于冒泡作用,亲属树上的元素凡是添加了事件的,都会被触发。
<div id="parent">
<div id="child">点击儿子</div>
</div>
<script type="text/javascript">
document.getElementById('parent').addEventListener('click',function(){
alert('parent'+this);
},false);
document.getElementById('child').addEventListener('click',function(){
alert('child'+this);
},false);
</script>
点击儿子,会先弹出child,再弹出parent。
4.2 事件捕获
<div id="parent">
<div id="child">点击儿子</div>
</div>
<script type="text/javascript">
document.getElementById('parent').addEventListener('click',function(){
alert('parent'+this);
},true);
document.getElementById('child').addEventListener('click',function(){
alert('child'+this);
},true);
</script>
会先弹出parent,再弹出child
五、事件委托
dom2级事件和dom0级事件都是可以利用冒泡机制进行事件委托的。
事件委托就是利用冒泡的原理,把事件加到父级上,触发执行。
使用事件委托的可以提高性能。
<ul id="ul">
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item4</li>
</ul>
给<li>
一个个绑定点击事件非常消耗性能和内存,并且大多数下<li>
的个数是不确定的。
所有我们可以将事件处理函数绑定在其父亲<ul>
上,利用event
对象就可以得到事件触发元素对象。
document.getElementById('ul').addEventListener('click',function(event){
console.log(event.target);
},false);
5.1 event对象
addEventListener()中event对象常用属性和方法:
- type 事件类型
- target 事件源,当前触发事件的元素
- currentTarget 绑定事件的元素
- preventDefault() 阻止事件默认行为
- stopPropagation() 阻止事件冒泡或捕获
- clientY 浏览器可视区域顶部底边到鼠标的位置(clientX同理)
- pageY 浏览器可视区域顶部底边到鼠标的位置,计算滚动轴的距离。(pageX同理)
- screenY 屏幕的顶端到鼠标的位置(screenX同理)
IE8及以下attachEvent()中event对象常用属性和方法:
- type 事件类型
- srcElement 同target
- returnValue = false 阻止默认行为同preventDefault()
- cancelBubble = true 取消事件冒泡同stopPropagation()
5.1.1 type 事件类型
可以用来统一事件处理的方法
var eventFun = function(event){
if(event.type == 'click'){
...
}else if(event.type == 'mouseout'){
...
}
}
btn.addEventListener('click',eventFun );
btn.addEventListener('mouseout',eventFun );
5.1.2 target 事件源对象
点击谁谁就是target,事件源。
5.1.3 currentTarget 处理事件的绑定对象
事件绑定在谁身上,就指向谁。
5.1.4 preventDefault() 阻止默认行为
阻止跳转
<a href="https://www.baidu.com" id="a">点击跳转</a>
<script>
var a = document.getElementById("a");
a.addEventListener('click',function(){
event.preventDefault();
},false);
</script>
5.1.5 stopPropagation() 阻止事件的冒泡或捕获
<div id="parent">
<div id="child">点击儿子</div>
</div>
<script type="text/javascript">
document.getElementById('parent').addEventListener('click',function(){
alert('parent'+this);
},false);
document.getElementById('child').addEventListener('click',function(event){
alert('child'+this);
event.stopPropagation();
},false);
</script>
点击儿子,只会弹出child。
5.2 Event对象跨浏览器写法
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] = null;
}
},
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;
}
},
//event跨浏览器
getTarget: function(event){
return event.target || event.srcElement;
},
preventDefault: function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
stopPropagation: function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
};
使用案例
var btn = document.getElementById('btn');
var test = function(event){
EventUtil.stopPropagation(event);
}
EventUtil.addHandler(btn,'click',test);
六、事件类型
比较实用的事件类型
- ul事件
- load 页面或资源(img、js、css等)加载完毕以后触发
- unload 用户从一个页面切换到另一个页面触发
- resize 窗口大小发生变化时触发(重复执行,损耗性能)
- scroll 页面上下滚动的时候触发
- 焦点事件
- blur 元素失去焦点时触发
- focus 元素获得焦点时触发
- focusin 同focus一样,但是支持冒泡
- focusout 同blur一样,失去焦点时触发(支持低版本IE)
- 鼠标事件
- click 点击时触发 (click = mousedown + mouseup)
- dblclick 双击时触发 (PS:jquery不支持双击事件)
- mousedown 鼠标按下
- mouseup 鼠标松开
- mousemove 鼠标在目标元素上移动时触发(重复执行,损耗性能)
- mouseover 鼠标进入目标元素(或者目标元素的子元素)
- mouseout 鼠标离开目标元素(或者目标元素的子元素)
- mouseenter 鼠标进入目标元素 (只能进入目标元素才触发,进入目标元素的子元素不会触发)
- mouseleave 鼠标离开目标元素(只能离开目标元素才触发,离开目标元素的子元素不会触)
- 键盘事件
- keydown 键盘上任意键按下时触发(支持keyCode)
event.keyCode 可以获得键码
- keyup 释放任意键时触发(支持keyCode)
- keypress 按下字符键触发(回车,f1,f2这些不支持,keyCode不稳定)
event.charCode 获得ASCII码
- textInput 在文本框中输入每一个字符时触发
event.data 获得输入的每一个字符
- dom结构事件
- DOMNodeRemoved 删除 目标元素中 的任意元素时触发
- DOMNodeIInserted 目标元素中 被添加任意元素触发
- DOMSubtreeModified 目标元素中 的dom结构发生任何变化(添加、删除等等)触发
- DOMNodeRemovedFromDocument 目标元素被移除前触发
- DOMNodeInsertedIntoDocument 被添加之前触发
- HTML5新增事件
-
DOMContentLoaded dom树加载完成之后就触发,比load快很多。
-
hashchange 只能给
window
添加,网页url#后面的值变化时触发 -
移动端事件
- touchstart:手指触摸时触发
- touchend:手指从屏幕上移开时触发
- touchmove:手指在屏幕上滑动时触发
- touchcancel 当系统停止跟踪触摸时触发
6.1 onload
当页面完全加载完毕触发
EventUtil.addHandler(window, 'load', function(event){...});
当图片加载完后触发
在图片非常大的时候
var image = document.createElement('img');
image.src = '../1.png';
EventUtil.addHandler(image, 'load', function(event){
....
});
图片预加载
var image = new Image(); 将图片缓存到内存中
image.src = '../1.png';
EventUtil.addHandler(image, 'load', function(event){
....
});
js动态加载完毕
EventUtil.addHandler(window, 'load', function(event){
var script = document.createElement('script');
script.src = 'XXX.js';
Event.addHandler(script, 'load', function(event){
....
});
document.body.appendChild(script);
});
css动态加载完毕
EventUtil.addHandler(window, 'load', function(event){
var link = document.createElement('link');
link.type = 'text/css';
link.rel = ''stylesheet;
link.href = 'xxxx.css';
EventUtill.addHandler(link, 'load', function(event){
...
});
document.getElementsByTagName('head')[0].appendChild(link);
});
6.21 DOMNodeRemoved
document中任意元素删除时触发
EventUtil.addHandler(document, 'DOMNodeRemoved', function(event){
...
});
指定元素中的元素被删除时触发
var ul = document.getElementsTagName('ul')[0];
EventUtil.addHandler(ul, 'DOMNodeRemoved', function(event){
...
});
6.27 hashchange
EventUtil.addHandler(window, 'hashchange', function(event){
console.log(event.oldURL);
console.log(event.newURL);
});
6.28 移动端事件
移动端常用事件
-
touchstart:手指触摸时触发
-
touchend:手指从屏幕上移开时触发
-
touchmove:手指在屏幕上滑动时触发
-
touchcancel 当系统停止跟踪触摸时触发
移动端常用事件对象的属性
- event.touches 获取当前触摸屏幕的触摸点数组(不管触摸点是不是在目标元素内)
- event.changedTouches 数组中只包含引起事件的触摸点信息
- event.targetTouches 只包含放在元素上的触摸信息
在移动端的时候,手机屏幕上可能有多个触摸点。