JavaScript与HTML之间是通过事件交互的。
事件流:描述的是从页面中接收事件的顺从。事件流分为两种,一个是事件冒泡,一个是事件捕获。
事件冒泡:有最具体的元素接收,逐级向上传播到较为不具体的节点(文档);(div->body->html->document)
事件捕获:由不太具体的节点最早接收到事件,逐级向下传播到最具体的节点;(document->html->body->div)
DOM事件流:DOM2级事件,规定事件流包括3个阶段,事件捕获阶段、处于目标阶段、事件冒泡阶段。事件捕获阶段不会接收到事件,处于目标事件阶段时,元素刚好接收到事件,
事件冒泡事件,就可以对事件作出响应了。只有支持DOM2级事件流的浏览器才有DOM事件流。
事件处理程序:响应某个事件的函数,叫作事件处理程序。如click事件的事件处理程序是onclick。
添加HTML事件处理程序
1、直接写行内js代码(不推荐)
<input type="text" value="click" onclick="alert('clicked')"> //用单引号哦,双引号有可能编译出错
2、引入一个函数
<input type="text" value="click" onclick="funName()"> function funName(){ console.log("cliced"); }
第二种指定事件处理程序具有一些读到之处。首先这样会创建一个封装这元素属性值的函数,这个函数中有一个局部变量event(第一种方法也有),也就是事件对象。(event不用定义就有,一般显示的定义一下)
在这个函数里面,this值等于事件的目标元素。
var light=document.getElementsByClassName("light")[0];
light.onclick=function(event){
console.log(this);//事件的目标元素
console.log(event);//事件对象
console.log(this.className)//light
}
DOM0级事件处理程序:javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
上面代码就是典型的DOM0级事件处理程序。
删除通过DOM0级指定的事件处理程序:
light.onclick=null;
DOM2级事件处理程序:
添加:addEventListener(“事件名,函数,布尔值);(事件名:clik,而不是onclick);
删除:removeEventListener("事件名”,函数名,布尔值);(同过匿名函数添加的DOM2级事件是无法删除的);
DOM2级事件,可以给一个元素添加多次同中事件,而不会覆盖,而且还可以控制事件流是冒泡(false),还是捕获(true);不加布尔值参数,默认为false(冒泡);
IE事件处理程序:
添加:attachEvent("事件处理程序名称",事件处理程序函数);(事件处理程序名称是onclick而不是click)
删除:detachEvent(“事件处理程序名称”,事件处理程序函数名);(同样无法删除通过匿名函数添加的事件处理程序);
IE事件处理程序也可以添加多个同样的事件处理程序且不会覆盖,但执行顺序是从下到上,也就是后加的先执行,还有函数中的this指向window而不是当前元素。
跨浏览器事件处理程序:
恰当的使用能力检测就可以写出来一个跨浏览器事件处理程序如:
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;
}
}
}
//使用
var ele=document.getElementsByClassName("light")[0];
var handler=function(){
console.log("handler");
}
EventUtil.addHandler(ele,"click",handler);//添加
EventUtil.removeHandler(ele,"click",handler);//删除
事件对象(event):
事件对象主要分为DOM中的事件对象和IE中的事件对象;
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着所有与事件有关的信息,包括导致事件 的元素(target)、事件的类型(type)、以及其它与事件相关的信息,例如,鼠标操作导致的事件中,会包含鼠标位置的信息,而键盘操作导致的事件对象中,会包含与按下键有关的信息,所有浏览器都支持event对象,但支持方式不同。
在事件处理程序内部,对象this始终等于currentTarget的值(事件绑定的对象),而target则包含事件的实际目标。如果直接接将事件处理程序指定给了目标元素,则this、currentTarget、target三个值相等,都是目标对象。(这点可以理解事件委托);
跨浏览器事件对象
因为DOM事件对象、IE事件对象有一定的差异,所以引出了跨浏览器事件对象,我们主要来介绍,跨浏览器事件对象(event)、事件目标对象(target)、阻止事件默认行为、阻止冒泡,将这几个封装出一个兼容的方法:
<a href="http://www.baidu.com" class="link">百度</a> var EventUtil={ addHandler:function(element,type,handler){//绑定事件 if(element.addEventListener){ element.addEventListener(type,handler,false); }else if(element.attachEvent){ element.attachEvent("on"+type,handler);//IE }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);//IE }else{ element["on"+type]=null; } }, 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;//IE } }, stopPropagation:function(event){//阻止事件冒泡 if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble=true;//IE } } } //使用 var ele=document.getElementsByClassName("link")[0]; function handler(event){ event=EventUtil.getEvent(event);//获取事件对象 var target=EventUtil.getTarget(event);//获取事件目标对象 console.log(target); EventUtil.preventDefault(event);//阻止事件默认行为 EventUtil.stopPropagation(event);//阻止冒泡 return false; } EventUtil.addHandler(ele,"click",handler);//绑定事件
事件类型:
UI事件:当用户与页面上的元素上交互时触发;
焦点事件:当元素获得或失去焦点时触发;
鼠标事件:当用户通过鼠标在页面上执行操作时触发:
文本事件:当在文档中输入文本时触发;
键盘事件:当用户通过键盘在页面上执行操作时触发;
合成事件:当为IME输入字符时触发;
变动事件:当底层DOM结构发生变化时触发;
变动名称事件:当元素或属性名变动时触发;(已废弃)
UI事件:
load事件:当页面完全加载后在window上触发,当所有框架加载完毕时,在框架集上触发,当img元素加载完后,在img元素上触发,或者当嵌入内容加载完时,在<object>元素上触发;
unload事件:当页面完全卸载后在window元素上触发,img,嵌入元素也会触发;
abort事件:用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上触发;
error:当javascript发生错误时在window上触发,img,嵌入内容也会触发。。。
select:当用户选择文本框input、texterea中的一个或多个字符时触发;
resize:当窗口或框架大小发生变化时触发;
scroll:当滚动有滚动条的元素时,在该元素上触发,body也会触发;
// 判断浏览器是否支持DOM2级事件规定的HTML事件;
var isSupported=document.implementation.hasFeature("HTMLEvents","2.0");
console.log(isSupported);//true
//判断浏览器是否支持DOM3级事件
var isSupportedDOM3=document.implementation.hasFeature("UIEvent","3.0");
console.log(isSupportedDOM3);//true
load事件,可以用window.onload=function(){}添加,也可以用<body onload="alert('load')>添加
也可以给img和script元素添加;img只要设置src属性就会加载,不必等到添加到文档;(load事件要在设置src属性前设置)
script元素,当设置src属性后,还必须等到加载到文档后才会加载;
unload事件是当用户从一个页面切换到另一个事件就会触发;
resize事件,当窗口或框架大小发生变化就会触发,尽量不要忘这个事件里写太多计算代码,否则窗口变化会变得卡顿;
scrioll事件:在混杂模式下通过body元素的scrollLeft、scrollTop监控滚动事件,在标准模式下,通过HTML元素反应这一变化;
window.onscroll=function(){
if(document.compatMode=="CSS1Compat"){//标准模式
console.log(document.documentElement.scrollTop);
}else{//混杂模式
console.log(document.body.scrollTop);
}
}
焦点事件主要就是focus和blur事件,分别是input框获得或失去焦点时触发;可以与document.hasFocus()方法与document.activeElement属性结合,可以知晓用户在页面上的行踪;(focus、blur事件都不会冒泡);
鼠标与滚轮事件:
click事件:在用户单击鼠标按钮或者按下回车键触发;
dblclick事件:在用户双击鼠标按钮时被触发;
mousedown事件:在用户按下了任意鼠标按钮时被触发,不能通过键盘触发这个事件。
mouseenter事件:在鼠标光标从元素外部移动到元素范围之内被触发;这个事件不冒泡;(移动到后代元素时不会再次出发)
mouseleave事件:在位于元素上方的鼠标光标移动到元素范围之外时触发,这个事件不冒泡;(移动到后代元素上不会触发)
mousemove事件:当鼠标指针在元素内部移动时重复地触发。
mouseout事件:用户将其移入另一个元素内被触发。(移动到后代元素上会再次触发)
mouseover事件:鼠标指针在元素外部,用户将移入另一个元素的边界时触发,(移动到后代元素上会再次触发);
mouseup事件:用户释放鼠标按钮时触发;
1.客户区坐标位置:event.clientX、event.clientY(可视区域的坐标位置,不包括滚动条)
2.页面坐标位置:event.pageX、event.pageY(页面区域包括滚动到视口之外的区域及可视区域,也就是整个文档区域)
3.屏幕坐标位置:event.screenX、event.screenY(相对于电脑屏幕的区域的坐标位置,所有浏览器都支持)
event.pageX=event.clientX+event.scrollLeft
event.pageY=event.clientY+event.scrollTop
当页面没有滚动的情况下,pageX、pageY与clientX、clientY相等;
用跨浏览器的方法写一个获取页面坐标位置的方法
EventUtil.addHandler(btn,'click',function(event){
event = EventUtil.getEvent(event);
var pageX = event.pageX,
pageY = event.pageY;
if(!pageX) {//IE下
pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft);
}
if(!pageY) {//IE下
pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop);
}
console.log("页面X轴坐标为:"+pageX + " "+ "页面Y轴坐标为:"+pageY);
});
4.修改键:
event.shiftKey、event.ctrlKey、event.altKey、event.metaKey,当点击鼠标时,按下相应的键,响应的值就会为true,否则为false
5.相关元素:
mouseout、mouseover,都有相关元素,DOM通过event.relatedTarget属性提供相关元素信息,返回相关元素;IE中mouseout的相关元素在event.toElement中保存,
mouseover的相关元素由event.fromElement中保存
mouseout中离开该元素,进入的那个元素就是相关元素、mouseover从哪个元素进入的这个元素,就是进入元素的相关元素
在EventUtil中封装一下(跨浏览器)获得这两个事件的相关元素的方法
var EventUtil={
getRelatedTarget:function(event){
if(event.relatedTarget){
return event.relatedTarget;
}else if(event.toElement){//IE中
return event.toElement;
}else if(event.fromElement){//IE中
return event.fromElement;
}else{
return null;
}
}
}
6.鼠标按钮:
对mouseDown和mouseUp事件来说,在其event对象中存在一个button属性,表示按下或释放的按钮;DOM的button属性有3个可能值:0(主键,一般是左键);1(中间键,华滑轮键);2(次键,右键);但是在IE中,虽然也有button属性,但是它有7个可能值,不多说,封装一个跨浏览器的getButton()的方法
var EventUtil={
getButton:function(event){
if(document.implementation.hasFeature("MouseEvents","2.0")){//判断支持DOM鼠标事件
return event.button;
}else{//IE中
switch(event.button){
case 0:
case 1:
case 3:
case 5:
case 7:
return 0;
case 2:
case 6:
return 2;
case 4:
return 1;
}
}
}
}
//使用
var div=document.getElementById("myDiv");
EventUtil.addHandler(div,"mousedown",function(event){
event = EventUtil.getEvent(event);
alert(EventUtil.getButton(event));
});
8.鼠标滚轮事件:mousewheel
IE6首先实现了mousewheel事件,此后opera,chrome和safari也都实现了这个事件,当用户通过鼠标滚轮与页面交互,在垂直方向上滚动页面时(无论向上还是向下),就会触发mousewheel事件,这个事件可以在任何元素上触发,最终会冒泡到document(IE8)或window(IE9,Opera,Chrome,Safari)对象,与mousewheel事件对应的event对象外,还有一个属性wheelDelta属性,当用户向前滚动鼠标滚轮时,wheelDelta是120的倍数,当用户向后滚动鼠标滚轮时,wheelDelta是-120的倍数。
将mousewheel事件给页面任何元素或document对象,即可处理鼠标滚轮操作;如下代码:
EventUtil.addHandler(btn,'mousewheel',function(event){
event = EventUtil.getEvent(event);
alert(event.wheelDelta);
});
如上代码,我不是在document对象或者window对象上,而是在页面btn元素上触发的;但是我们要注意,在Opera9.5之前的版本中,wheelDelta值的正负号是颠倒的,如果我们要支持Opera9.5版本之前的话,那么我们需要浏览器检测技术来检测下;如下代码
EventUtil.addHandler(document, "mousewheel", function(event){ event = EventUtil.getEvent(event); // var delta = (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta); var delta = event.wheelDelta; alert(delta); });
但是client.engine.opera 这句代码运行下会报错,因为目前还没有封装这个方法,所以等下一个博客我会研究代理检测封装下这个方法;所以先不考虑opera9.5,先注释掉这句代码;
但是FireFox支持一个为DOMMouseScroll的事件,也是在鼠标滚轮滚动时触发,与mousewheel事件一样,但是他的有关鼠标滚轮信息保存在detail属性中,当鼠标向前滚动时,这个属性值是-3的倍数,当鼠标滚轮向后滚动时,这个属性值是3的倍数;也可以给DOMMouseScroll事件使用在任何元素上,且这个事件会冒泡到window对象上,因此我们可以这样添加滚轮信息的代码如下:
EventUtil.addHandler(document, "DOMMouseScroll", function(event){
event = EventUtil.getEvent(event);
alert(event.detail);
});
我们现在可以给跨浏览器下的滚轮事件;代码如下:
function getWheelDelta (event) { if(event.wheelDelta) { // return (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta); return event.wheelDelta; }else { return -event.detail * 40 } }
getWheelDelta方法首先检测了事件对象是否包含了wheelDelta属性,如果包含则返回属性值,如果不包含,那么我们就当作是firefox浏览器,那么假设相应的值保存在detail属性中,有了上面的方法后,我们现在可以将相同的事件指定给mousewheel事件和DOMMouseScroll事件了;
EventUtil.addHandler(document, "DOMMouseScroll", handleMouseWheel);
EventUtil.addHandler(document, "mousewheel", handleMouseWheel);
function handleMouseWheel(event) {
event = EventUtil.getEvent(event);
var delta = EventUtil.getWheelDelta(event);
alert(delta);
}
滚轮向上滚动是正数120,向下滚动是负数-120,所以根据是否大于0,可以判断是向下滚动还是向上滚动;
理解字符编码charCode
IE9+,firefox,chrome和safari的event对象都支持一个charCode属性,这个属性只有在发生keypress事件时才包含值,而且这个值是按下的那个键所代表字符的ASCLL编码,但是IE8及之前或者opera不支持这个属性,但是我们可以使用keyCode这个属性代替.在取得了字符编码之后,就可以使用String.fromCharCode()将其转换成实际的字符。
如下代码:
getCharCode: function(event) {
if(typeof event.charCode == 'number') {
return event.charCode;
}else {
return event.keyCode;
}
}
我们现在可以给EventUtil添加事件了,如下代码:
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;
}
},
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;
}
},
stopPropagation: function(event) {
if(event.stopPropagation) {
event.stopPropagation();
}else {
event.cancelBubble = true;
}
},
getRelatedTarget: function(event){
if (event.relatedTarget){
return event.relatedTarget;
} else if (event.toElement){
return event.toElement;
} else if (event.fromElement){
return event.fromElement;
} else {
return null;
}
},
getWheelDelta: function(event) {
if(event.wheelDelta) {
return event.wheelDelta;
}else {
return -event.detail * 40
}
},
getCharCode: function(event) {
if(typeof event.charCode == 'number') {
return event.charCode;
}else {
return event.keyCode;
}
}
};
我们现在可以做一个demo如下:
//如下代码:
var inputDiv = document.getElementById("inputDiv");
EventUtil.addHandler(inputDiv,'keypress',function(event){
event = EventUtil.getEvent(event);
var code = EventUtil.getCharCode(event);
alert(EventUtil.getCharCode(event)); // 弹出字符编码
alert(String.fromCharCode(code)); // 弹出字符
});
HTML5事件
1. contextmenu事件
contextmenu事件在windows操作系统下,我们是使用右键就可以自定义右键弹出菜单,但是我们使用右键的时候会有默认的菜单,因此我们需要使用阻止默认事件这个方法来阻止掉;此事件也是属于鼠标事件,因此此事件包含与光标位置中所有的属性,比如我们右键如下图所示:
// JS代码如下:
var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "contextmenu", function(event){
event = EventUtil.getEvent(event);
EventUtil.preventDefault(event);
var menu = document.getElementById("myMenu");
menu.style.left = event.clientX + "px";
menu.style.top = event.clientY + "px";
menu.style.visibility = "visible";
});
EventUtil.addHandler(document, "click", function(event){
document.getElementById("myMenu").style.visibility = "hidden";
});
如上,我们是通过右键的clientX和clientY来确定菜单的位置;当我点击文档document的时候 就隐藏该菜单;
浏览器支持有:IE,Firefox,Safari,chrome和Opera11+
beforeunload事件
此事件是给页面在卸载之前,给用户一个提示,是否需要卸载页面提示给用户,为了显示这个对话框,对IE和firefox而言,必须将event.returnValue的值设置为要显示给用户的字符串;但是对于safari和chrome而言,可以返回此字符串即可;
EventUtil.addHandler(window, "beforeunload",function(event){
event = EventUtil.getEvent(event);
var message = "I'm really going to miss you if you go.";
event.returnValue = message;
return message;
});
浏览器支持:IE,firefox,chrome和safari都支持,但是Opera11及之前的版本不支持;
理解hashchange事件
HTML5中新增加了hashchange事件,以便在URL的参数列表(url中的#号后面的所有参数发生改变时通知开发人员),在Ajax应用中,开发人员经常使用url参数列表保存状态或导航信息;
我们必须把hashchange事件添加到window对象中,然后当url参数列表只要发生变化就会调用此事件,此事件对象event包含2个属性,oldURL和newURL,这两个属性分别保存着URL变化前后的完整URL;
EventUtil.addHandler(window, "hashchange", function(event){
alert("Old URL: " + event.oldURL + "
New URL: " + event.newURL);
});
有些浏览器并不支持oldURL和newURL,因此我们可以使用location.hash来保存当前的参数列表,如下代码:
EventUtil.addHandler(window, "hashchange", function(event){
alert(location.hash);
});
当#号后面我改成bbb参数时候,会弹出如下所示:
可以使用如下代码来检测浏览器是否支持hashchange事件;
var isSupported = ("onhashchange" in window);
alert(isSupported);
如果IE8 是在IE7 文档模式下运行,即使功能无效它也会返回true。为解决这个问题,可以使用
以下这个更稳妥的检测方式:
var isSupported = ("onhashchange" in window) && (document.documentMode === undefined || document.documentMode > 7);
设备事件中的—orientationchange事件
苹果公司为移动safari添加的orientationchange事件是能让用户确定何时将设备由横向查看模式切换到纵向模式触发的事件;此属性中包含三个值,0表示肖像模式;90表示向左旋转的横向模式(主屏幕按钮在右侧),-90表示向右旋转的横向模式(主屏幕按钮在左侧),如下图所示:
只要用户改变了设备的查看模式,就会触发orientationchange事件,
使用IOS设备即可演示效果:代码如下:
EventUtil.addHandler(window, "load", function(event){
var div = document.getElementById("myDiv");
div.innerHTML = "Current orientation is " + window.orientation;
EventUtil.addHandler(window, "orientationchange", function(event){
div.innerHTML = "Current orientation is " + window.orientation;
});
});
理解移动端的事件---触摸与手势事件
有以下几个触摸事件:
- touchstart: 当手指触摸屏幕时触发,即使是一个手指放在屏幕上也会触发。
- touchmove:当手指在屏幕上滑动时连续地触发,这个事件发生期间,我们可以使用preventDefault()事件可以阻止滚动。
- touchend: 当手指从屏幕上移开时触发。
- touchcancel: 当系统停止跟踪触摸时触发。
上面几个事件都属于冒泡事件,我们可以对此进行取消事件,每个触摸的event对象都提供了在鼠标中常见的属性;
bubbles, cancelable,view,clientX, clientY ,screenX, screenY,detail, altKey, shiftKey, ctrlKey, metaKey,
除了常见的DoM属性外,触摸事件还包含下列三个用于跟踪触摸的属性;
touches: 表示当前跟踪的触摸操作的Touch对象数组;
targetTouches: 特定与事件目标的Touch对象数组;
changeTouches: 表示自上次触摸以来发生了什么改变的Touch对象数组;
事件委托:
事件委托,利用事件冒泡的行为,将事件绑定在DOM树相对较高的元素上,子元素获得事件后,会冒泡到这个DOM树相对较高的元素,从而触发这个元素上的事件:
这样减少了绑定大量事件处理程序,占用内存较少,从而提高页面性能;
来个例子;这里面没用跨浏览器的方式,不过可以让大家明白事件委托;
<body> <a href="http://www.baidu.com" class="link">百度</a> <ul id="list"> <li id=list1>111111111111</li> <li id="list2">222222222222</li> <li id="list3">33333333</li> </ul> <div class="aa">haaa</div> <script> var myList=document.getElementById("list"); var aa=document.getElementsByClassName("aa")[0]; myList.onclick=function(event){ var target=event.target; switch(target.id){ case "list1": aa.innerHTML="11111111111"; break; case "list2": aa.innerHTML="2222222222"; break; case "list3": aa.innerHTML="33333333"; break; } } </script>
移除事件处理程序;
可以用load方法给元素加入事件处理程序,用unload方法,在卸载页面时,将事件处理程序移除,在移除元素之前,先将事件移除,
如:
btn.onclick=null;//移除事件
父元素.innerHTML="";//移除元素
模拟事件:
模拟事件都有三个过程:
1、创建事件
2、初始化使劲对象event
3、触发事件
但是,这些模拟事件大多只针对个别浏览器,用到的不多,想了解更多可以在树上查看,这里只做简单介绍,javascript事件到这里就告一段落;