前言
近期手头的项目需要覆盖到移动终端,公司的移动终端版本未引入任何开源JS库。没了jQuery,开发效率低了很多,更要命的是很多基础知识遗忘的厉害,于是又一次翻出了那本《高级JavaScript程序设计》,整理以前的读书笔记形成以下内容。
一、DOM事件流
DOM2级事件规定的事件流包含三个阶段:捕获、到达目标、冒泡。
IE8及更早版本不支持DOM事件流(主要是不支持捕获)。所有浏览器都支持事件冒泡。
二、DOM0级事件处理程序
(1)添加事件处理程序
每个元素(包括window、document)都有自己的事件处理程序属性,这些属性通常全部小写,例如onclick。将属性的值设置为函数,就可指定事件处理程序。如:
var btn = document.getElementById("myBtn");
btn.onclick = function(){aelrt('clicked');};
(2)删除事件处理程序
btn.onclick = null;
(3)优点
兼容性好
(3)缺点
无法为同一个事件类型指定多个事件处理程序,后设置的事件处理程序将覆盖前一个。
(4)需要注意作用域问题
事件处理程序是在元素的作用域中运行,this指针指向的是当前元素。
事件处理程序是在元素的作用域中运行,this指针指向的是当前元素。
btn.onclick = function(){aelrt(this.id);}; // "myBtn"
三、DOM2级事件处理程序
(1)添加与移除事件处理程序
addEventListener、removeEventListener 所有DOM节点都包含这两个方法,接受3个参数:事件类型、处理函数、是否在捕获阶段触发函数。true表示在捕获阶段调用事件处理程序,false表示在冒泡阶段调用事件处理程序。
var btn = document.getElementById("myBtn");
btn.addEventListener('click', function(){alert(this.id)}, false);
btn.addEventListener('click', function(){alert('hello world!')}, false);
btn.removeEventListener('click', function(){alert('hello world!')}, false);// 无法移除
(2)同一个事件类型指定多个事件处理程序
执行顺序与调用addEventListener绑定事件处理程序的顺序相同,与第三个参数的值无关。如:
function bubbleHandle(){
console.log('bubbleHandle bubbleHandle bubbleHandle!!');
}
function fetchHandle(){
console.log('fetchHandle fetchHandle fetchHandle!!');
}
var eventflowEle = document.getElementById("eventflow");
eventflowEle.addEventListener('click', bubbleHandle, false);
eventflowEle.addEventListener('click', fetchHandle, true);
//eventflowEle.addEventListener('click', bubbleHandle, true);
//eventflowEle.addEventListener('click', fetchHandle, false);
以上代码总是会打印:
fetchHandle fetchHandle fetchHandle!!
'bubbleHandle bubbleHandle bubbleHandle!!
(3)作用域问题
与DOM0级事件一样,事件处理程序是在元素的作用域中运行,this指正执行的是当前元素
(4)通过匿名函数添加的事件处理程序将无法被移除
常规做法,为了兼容各种浏览器,最好将事件处理程序添加到事件流的冒泡阶段。
四、IE事件处理程序
(1)添加与移除事件处理程序
attachEvent、detachEvent
var btn = document.getElementById("myBtn");
btn.attachEvent('onclick', function(){alert(this.id)}); // undefined
btn.detachEvent('onclick', function(){alert(this.id)}); // 无法移除
IE8及早期版本不支持事件捕获,所以通过attachEvent添加的事件处理程序将在冒泡阶段被调用。
(2)同一个事件类型指定多个事件处理程序
执行顺序与调用attachEvent绑定事件处理程序的顺序相反
var btn = document.getElementById("myBtn");
btn.attachEvent('onclick', function(){alert('hello world!')});
btn.attachEvent('onclick', function(){alert('cliked!!')});
弹窗顺序:
cliked!!
hello world!
(3)作用域问题
与DOM0级事件不一样,事件处理程序是全局作用域中运行,this指针指向的是window对象
(4)通过匿名函数添加的事件处理程序将无法被移除
(5)事件类型要加"on",如onclick
写一段跨浏览器绑定事件处理程序的代码:
var EventUtil = {
addHandle : function(ele, type, handle){
if(ele.addEventListener){
ele.addEventListener(type, handle, false);// 默认在事件冒泡阶段触发事件处理程序,第三个参数默认为false
}else if(ele.attachEvent){
ele.attachEvent('on'+type, handle);
}else{
ele['on'+type] = handle;
}
},
removeHandle : function(ele, type, handle){
if(ele.removeEventListener){
ele.removeEventListener(type, handle, false);
}else if(ele.detachEvent){
ele.detachEvent('on'+type, handle);
}else{
ele['on'+type] = null;
}
}
};
function bubbleHandle(){
alert('bubbleHandle bubbleHandle bubbleHandle!!');
}
var ele = document.getElementById('eventflow');
EventUtil.addHandle(ele, 'click', bubbleHandle);
setTimeout(function(){
EventUtil.removeHandle(ele, 'click', bubbleHandle);
}, 3000);
五、事件对象
(1)DOM中的事件对象
兼容DOM的浏览器会将事件对象(event)传入到事件处理程序中。DOM0级,DOM2级都会传event对象
var btn = document.getElementById("myBtn");
btn.onclick = function(event){alert(event.type)}; // "click"
btn.addEventListener('click', function(event){alert(event.type)}, false); // "click"
currentTarget与target
currentTarget 绑定事件处理程序的DOM元素
target 事件的目标元素
事件处理程序中的this始终等于currentTarget
function bubbleHandle(event){
console.log(event.currentTarget === this); // true
console.log(event.target === document.getElementById('eventflow')); // true
}
var ele = document.body;
EventUtil.addHandle(ele, 'click', bubbleHandle);
应用场景:事件委托,将事件处理程序绑定在父级容器上,通过event.target判断用户单击的是哪一个DOM元素。
阻止事件冒泡
我们同时为body与body中的按钮绑定了单击事件,我们希望在单击按钮的时候不要触发body的事件处理程序,这时候就需要在按钮的事件处理程序中阻止事件冒泡(同时也取消了事件捕获):
event.stopPropagation()
阻止默认行为
event.preventDefault()
event对象将在事件处理程序结束之后被销毁
(2)IE中的事件对象
通过属性赋值的方式添加事件处理程序,可通过window.event获取事件对象:
var mybtn = document.getElementById("mybtn");
mybtn.onclick = fucntion(){alert(window.event.type);} // 'click'
通过attachEvent方式添加的事件处理程序,可通过参数传递的方式获取事件对象
var mybtn = document.getElementById("mybtn");
mybtn.attachEvent('onclick', function(event){alert(event.type)});// 'click'
srcElement
事件目标。与DOM中的target属性相同
var mybtn = document.getElementById("mybtn");
mybtn.onclick = fucntion(){alert(window.event.srcElement === this);} // true
mybtn.attachEvent('onclick', function(event){alert(event.srcElement === this)});// false 这里的this指向的是window对象
阻止事件冒泡
event.cancelBubble()
阻止默认行为
event.returnValue = false;
应用场景:给A标签绑定单击事件时需要阻止浏览器的默认跳转行为
写一段跨浏览器获取事件对象的代码:
var EventUtil = {
addHandle : function(ele, type, handle){
if(ele.addEventListener){
ele.addEventListener(type, handle, false);// 默认在事件冒泡阶段触发事件处理程序,第三个参数默认为false
}else if(ele.attachEvent){
ele.attachEvent('on'+type, handle);
}else{
ele['on'+type] = handle;
}
},
removeHandle : function(ele, type, handle){
if(ele.removeEventListener){
ele.removeEventListener(type, handle, false);
}else if(ele.detachEvent){
ele.detachEvent('on'+type, handle);
}else{
ele['on'+type] = null;
}
},
getEvent : function(event){
return event?event : window.event;
},
getTarget : function(event){
return event.target || event.srcElement;
},
stopPropagation : function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble();
}
},
preventDefault : function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
}
};
六、事件类型:
UI事件
焦点事件
鼠标事件
滚轮事件
文本事件
键盘事件
UI事件
不一定与用户操作有关的事件,如window.onload、window.onunload、window.onresize、window.onscroll
load事件
(1)JS代码为window对象绑定onload事件,也可在body上添加onload属性
(2)img标签也可添加onload事件。应用场景:还记我们的菊花效果么?
在DOM出现之前,通常会用Image对象预加载图片:
EventUtil.addHandle(window, 'load', function(event){
var image = new Image();
EventUtil.addHandle(image, 'load', function(event){
console.log('iamge loaded');
});
image.src = 'http://images.139cm.com/subscribe//upload/2014/06/20/820//images/1407016596180_242.jpg';
});
var image = new Image();
EventUtil.addHandle(image, 'load', function(event){
console.log('iamge loaded');
});
image.src = 'http://images.139cm.com/subscribe//upload/2014/06/20/820//images/1407016596180_242.jpg';
});
(3)script标签也可绑定load事件。应用场景:还记得脚本文件的按需加载么?
IE8及更早版本不支持script标签的load事件,可统一绑定onreadystatechange事件执行脚本加载后的回调。标准浏览器可绑定load事件执行回调。
unload事件
页面卸载后触发
应用场景:清除引用,避免内存泄露
resize事件
火狐浏览器会在用户停止调整窗口大小时触发resize事件,其他浏览器在调整窗口大小的过程中不断触发。
scroll事件
文档被滚动期间重复触发
焦点事件
blur 元素失去焦点时触发,所有浏览器都支持,并且不会冒泡。
focus 元素取得焦点时触发,所有浏览器都支持,并且不会冒泡。
鼠标与滚轮事件
click
dblclick
mousedown
mouseenter
mouseleave
mousemove
mouseover
mouseup
判断浏览器是否支持某一事件的方式:
document.implementation.hasFeature("MouseEvents", "3.0")
客户区坐标位置
客户区:用户可以看到的页面的区域,不包括浏览器工具栏,不包含页面滚动的距离。
event.clientX,event.clientY 可以获取事件发生时鼠标指针在视口中的水平和垂直坐标。
页面坐标位置
event.pageX,event.pageY
在页面没哟滚动的情况下,pageX与clientX值相等。
IE8及更早版本不支持pageX,pageY,需要这样获取pageX,pageY值:
var pageX = event.pageX;
var pageY = event.pageY;
if(pageX === undefined){
pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft);
}
if(pageY === undefined){
pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop);
}
屏幕坐标位置
事件发生时,鼠标位置相对于整个屏幕的坐标位置
event.screenX,event.screenY
修改键
shift、ctrl、alt、meta
DOM规定了四个属性表示这四个键的状态:shiftKey、ctrlKey、altKey、metaKey。为true时表示该键被按住。
应用场景:组合键执行事件处理程序
$(window.document).bind('keydown', function(event){
// Ctrl+Enter
if(event.ctrlKey && event.keyCode == M139.Event.KEYCODE.ENTER){
// TODO
}
});
鼠标滚轮事件
mousewheel
IE6首先实现了该事件,现在几乎所有浏览器都支持该事件。
EventUtil.addHandle(document, 'mousewheel', function(event){
alert(event.wheelDelta);
});
alert(event.wheelDelta);
});
向前滚wheelDelta的值是120的倍数
向后滚wheelDelta的值是-120的倍数
通常情况下我们只需要根据wheelDelta的正负号判断用户的滚动方向就行了。
139邮箱的应用场景:云邮局杂志阅读器,滚动滚轮放大缩小图片
火狐浏览器支持DOMMouseScroll事件,向前滚动时该值是-3的倍数,向后是3的倍数。
推荐使用JQuery插件:jquery.plugin.mouseweel.js 解决兼容性问题
键盘事件
keydown 按住不放重复触发
keypress 按住不放重复触发
keyup
139邮箱的应用场景:杂志阅读器,按住向下的方向键不松开,可连续往上移动图片。
HTML5事件
contextmenu事件 右键调出上下文菜单
contextmenu事件是冒泡的,我们可以在document上统一绑定该事件,利用事件委托,判断用户点击的元素,显示自定义菜单。然后使用onclick事件隐藏菜单,并可通过event.clientX、event.clientY定位菜单位置。
DOMContentLoaded事件
执行时间早于window.onload,DOM树形成之后就会触发。
IE8及更早的浏览器不支持该事件,可通过readystatechange事件实现相应的功能。
readystatechange事件
IE浏览提为DOM文档的某些部分提供了该事件。可判断document.readyState值执行相应的回调。
uninitialized、loading、loaded、interactive、complete
需要同时判断readyState值等于interactive或者等于complete,执行回调。
应用场景:jQuery的$(document).ready
<script>(IE和opera)和<link>(仅IE)也会触发readystatechange,需要同时判断readyState值等于loaded或者等于complete,执行回调