事件处理程序

兼容性的事件处理程序:

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.datachEvent){
			element.detachEvent("on"+type,handler);
		}else {
			element["on"+type] = null;
		}
	}
}

DOM中的事件对象

event对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。

属性如下:

bubbles:事件是否冒泡 cancalable:是否可以取消事件的默认行为 currentTarget:其事件处理程序当前正在处理事件的那个元素。 defaultPrevented:为true表示已经调用了preventDefault() detail:与事件相关的详细信息 eventPhase:调用事件处理程序的阶段:1表示捕获阶段,2表示处于目标,3表示冒泡阶段 preventDefault():取消事件的默认行为 stopImmediatePropagation():取消事件的进一步捕获或冒泡,同时阻止任何事件程序程序被调用 stopPropagation():取消事件的进一步捕获或冒泡。 target:事件的目标 trusted:为表示事件时浏览器生成的,为false表示事件是由开发人员通过js创建的(DOM3) type:被触发事件的类型 view:与事件关联的抽象视图。等同于发生事件的window对象。

IE中的事件对象

属性:

cancalBubble读写 默认值为false,设置为true时就可以取消事件冒泡 returnValue读写 默认值为true,设置为false时就可以取消事件的默认行为 srcElement事件的目标 type被触发的事件的类型

跨浏览器的事件对象

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.datachEvent){
			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.cancalBubble = true;
		}
	}
}

事件类型

UI事件:当用户也页面上的元素交互时触发 焦点事件 鼠标事件 滚轮事件 文本事件 键盘事件 合成事件 变动事件:当底层的DOM结构发生改变时触发

1.UI事件

load:当页面完全加载后在window上面触发,当所有框架都加载完毕时在框架集上触发,当图像加载完毕时在<img>上触发,或者当嵌入的内容加载完毕时在<object>元素上触发。

unload:卸载(与上面相反)

abort:在用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>上面触发。

error:当发生javascript错误时在window上面触发,当无法加载图像时在<img>上触发。当无法加载嵌入内容时在<object>元素上触发。

select:当用户选择文本框(<input><textarea>)中的一或多个字符时触发。

resize:当窗口或框架的大小变化时在window或框架上面触发

scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。

EventUtil.addHandler(window,"load",function(){
	var image = document.createElement("img");
	EventUtil.addHandler(image,"load",function(event){
		event = EventUtil.getEvent(event);
		alert(EventUtil.getTarget(event).src);
	});
	document.body.appendChild(image);
	image.src = "smile.jpg";
});

注意 新图像元素不一定要从添加到文档后才开始下载,只要设置了src属性就会开始下载。

DOM0级的Image对象实现。使用image对象在客户端预加载图像。可以使用Image对象,只不过无法将其添加到DOM树中(因为image对象不是DOM元素)

EventUtil.addHandler(window,"load",function(){
	var image = new Image();
	EventUtil.addHandler(image,"load",function(event){
		alert("Image loaded!");
	});
	image.src = "smile.jpg";
});

<script>元素也会触发load事件,以便开发人员确定动态加载的javascript文件是否加载完毕。与图像不同,只是在设置了<script>元素的src属性并将该元素添加到文档后,才会开始下载javascript文件。换句话说,对于<script>元素而言,指定src属性和指定事件处理程序的先后顺序不重要了。

注意: 像resize事件(除了firefox下,都会重复触发)一样,scroll事件会在文档被滚动期间重复被触发,所以有必要尽量保持事件处理程序的代码简单。

2.焦点事件 利用这些事件和document.hasFocus()方法及document.activeElement属性配合,可以知晓用户在页面上的行踪。

blur:在元素失去焦点时触发。不会冒泡。 focus:在元素获得焦点时触发。不会冒泡。 focusin:在元素获得焦点时触发。同focus,但会冒泡(ff不支持) focusout:在元素失去焦点时触发。同blur。

3.鼠标与滚动事件

click:在用户单击鼠标按钮或者按下回车键时触发 dbclick:在用户双击住鼠标按钮时触发 mousedown:在用户按下了任意鼠标按钮时触发 mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。 mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。不冒泡,而且在光标移动到后代元素上不会触发。 mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,在光标移动到后代元素上不触发。 mousemove:当鼠标指针在元素内部移动时重复的触发。不可通过键盘触发。 mouseout:在鼠标指针位于一个元素上方,然后用户将其移入一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不可通过键盘触发。 mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。不能通过键盘触发这个事件。

只有在同一个元素上相继触发了mousedown和mouseup事件,才会触发click事件。如果这两个事件中的一个被取消,就不会触发click事件。

类似地,只有触发两次click事件,才会触发一次dbclick事件。如组织了连续两次触发click事件,就不会触发dbclick事件。

mousedown->mouseup->click->mousedown->mouseup->click->dbclick

(用mouseenter和mouseleave可以解决之前放大图片带来的问题,只需要将小的遮罩层overlay设成为大图片的子元素。再通过mouseenter和mouseleave来控制,就不会发生之前图片和遮罩层来回跳转的问题。)

(1)客户区坐标位置

鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的clientX和clientY属性中。所有浏览器均支持这两个属性。它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标。

var div = document.getElementById("myDiv");
EventUtil.addHandler("div",click,function(event){
	event = EventUtil.getEvent(event);
	alert("client coordinates:" + event.clientX + "," + event.clientY);
});

(2)页面坐标位置

页面坐标位置通过事件对象的pageX和pageY属性,能告诉你事件是在页面中的什么位置发生的。这两个属性表示鼠标光标在页面中的位置,因此坐标是从页面本省而非视口的左边和顶边计算的。

var div = document.getElementById("myDiv");
EventUtil.addHandler("div",click,function(event){
	event = EventUtil.getEvent(event);
	alert("client coordinates:" + event.pageX + "," + event.pageY);
});

在页面没有发生滚动的情况下,clientX(Y)和pageX(Y)的值相同。

IE8-不支持事件对象上的页面坐标,可以使用客户区坐标和滚动信息计算出来。这时候需要用到document.body(混杂模式)或document.documentElement(标准模式)中的scrollLeft和scrollTop属性。

浏览器兼容下的页面坐标:

var div = document.getElementById("myDiv");
EventUtil.addHandler("div",click,function(event){
	event = EventUtil.getEvent(event);
	var pageX = event.pageX;
	var pageY = event.pageY;
	if(pageX === undefined){
		pageX = event.clientX + (document.body.scrollLeft||document.documentElement.scrollLeft);
		pageY = event.clientY + (document.body.scrollTop||document.documentElement.scrollTop);
	}
	alert("client coordinates:" + pageX + "," + pageY);
});

(3)屏幕坐标位置

相对于整个电脑屏幕的位置。screenX和screenY属性就可确定鼠标事件发生时鼠标指针相对于整个屏幕的坐标位置。

(4)修改键

shift、Ctrl、Alt、Meta。它们经常用于修改鼠标事件的行为。DOM规定了4个属性,表示这个修改键的状态:shiftKey、ctrlKey、altKey、metaKey。这些属性中包含的都是布尔值,响应的键被按下了,其值为true,否则为false。当某个鼠标事件发生时,通过检测这几个属性就可以知道是否同时按下了其中的键。

var div = document.getElementById("myDiv");
EventUtil.addHandler("div",click,function(event){
	event = EventUtil.getEvent(event);
	var keys = new Array();
	if(event.shiftKey){
		keys.push("shift");
	}
	if(event.ctrlKey){
		keys.push("ctrl");
	}
	if(event.altKey){
		keys.push("alt");
	}
	alert("keys: "+keys.join(","));
});

(5).相关元素

在发生mouseover和mouseout事件时,还会涉及更多的元素。

对于mouseover事件:事件的主目标是获得光标的元素,而相关元素就是失去光标的元素。 mouseout事件:事件的主目标是失去光标的元素,而相关元素就是获得光标的元素。

DOM通过event对象的relatedTarget属性提供了相关元素的信息。这个属性只对于mouseover和mouseout事件才包含值;对于其他时间,这个属性的值是null。IE8-不支持,提供了保存着同样信息的不同属性。在mouseover事件触发时,IE的fromElement属性中保存了相关元素。在mouseout事件触发时,IE的toElement属性中保存着相关元素。

兼容的方法:

var EventUtil = {
	getRelatedTarget:function(event){
		if(event.relatedTarget){
			return event.relatedTarget;
		}else if(event.fromElement){
			return event.fromElement;
		}esle if(event.toElement){
			return event.toElement;
		}else{
			return null;
		}
	}	
}

(6)鼠标按钮

只有在主鼠标按钮被单击时才会触发click事件,因此检测按钮的信息也有必要。

DOM的button属性:0表示主鼠标按钮 1.表示中间鼠标按钮(滚轴) 2.表示次数表按钮

IE8下差别很大…

兼容性的处理:

var EventUtil = {
	getButton:function(event){
		if(document.implement.hasFeature("MouseEvnet",'2.0')){
			return event.button;
		}else{
			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;
			}
		}
	}
}

(7)更多的事件信息

DOM中,对于鼠标事件来说,detail中包含了一个数值,表示在给定位置上发生了多少次单击。

(8)鼠标滚动事件

mousewheel事件,

对应的event.wheelDelta属性:

EventUtil.addHandler(document,"mousewheel",function(event){
	event = EventUtil.getEvent(event);
	var delta = (client.engine.opera && client.engine.opera <9.5?-event.wheelDelta:event.wheelDelta);
	alert(delta);
});

(9)触摸设备

不支持dbclick事件。双击浏览器窗口会放大画面,而且没有办法改变该行为。

轻击可单击元素会触发mousemove事件。轻击不可点击的元素不会触发任何事件。

mousemove事件也会触发mouseover和mouseout

两个手指放在屏幕上且页面上随手指一动而滚动时会触发mousewheel和scroll事件。

(10)无障碍性问题(对于盲人)

a.使用click事件执行代码。

b.不要使用onmouseover向用户显示新的选项

c.不要使用dbclick执行重要的操作。

4.键盘与文本事件

3个键盘事件

keydown:当用户按下键盘上的任意按键时触发,按住不放,会重复触发事件。 keypress:当用户按下键盘上的字符键时触发,按住不放,会重复触发事件。 keyup:当用户释放键盘上的按键时触发。

(1)键码

keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键盘上一个特定的键对应。

var textbox = document.getElementById("mtText");
EventUtil.addHandler(textbox,"keyup",function(event){
	event = EventUtil.getEvent(event);
	alert(event.keyCode);
});

兼容下的按键码获取(非ASCII)

getCharCode:function(event){
	if(typeof event.charCode == "number"){
		return event.charCode;
	}else{
		return event.keyCode;
	}
}

7.HTML5事件

(1)contextmenu事件

var menu = document.getElementById("myMenu");
EventUtil.addHandler(window,"load",function(event){
	var div = document.getElementById("myDiv");
	EventUtil.addHandler(div,"contextmenu",function(event){
		event = EventUtil.getEvent(event);
		EventUtil.preventDefault(event);
		
		menu.style.left = event.clientX + "px";
		menu.style.top = event.clientY + "px";
		menu.style.visibility = "visible";
	});

	EventUtil.addHandler(document,"click",function(event){
		menu.style.visibility = "hidden";
	});	
});

支持这个事件的浏览器IE、ff、safari、chrome和Opera 11+

(2)beforeunload事件

这个事件将控制权交给用户。显示的消息会告诉用户页面将被卸载,是否真正要关闭,还是继续留下来。

EventUtil.addHandler(document,"beforeunload",function(event){
	event = EventUtil.getEvent(event);
	var message = "I'm ready going to miss you if you go";
	event.returnValue = message;
	return message;
});

3.DOMContentLoaded事件

window的load事件会在页面中的一切都加载完了触发。而DOMContentLoaded事件则在形成完整的DOM数之后就会触发,不理会图像,javascript文件、CSS文件或其他资源是否加载完毕。与load不同,DOMContentLoaded支持在页面下载的早期添加事件处理程序,尽早的与页面交互。

DOMContentLoaded事件会冒泡到window,目标为document。

EventUtil.addHandler(document,"DOMContentLoaded",function(event){
	alert("Content loaded");
});

DOMContentLoaded事件对象不会提供任何额外的信息(其target为document)

IE9+、ff、chrome、safari、opera都支持DOMContentLoaded事件,通常这个事件既可以添加事件处理程序,也可以执行执行其他操作。始终都在load事件之前触发。

对于不支持的浏览器,设置一个时间为0毫秒的超时调用

setTimeOut(function(){
	//事件处理程序
},0);

(4)readystatechange事件

IE为DOM文档中的某些部分提供了readystatechange事件。这个事件的目的是提供与文档或元素加载装填有关的信息。

支持这个事件的每个对象都有一个readyState属性,取值为:

uninittialized(未初始化):对象存在但未初始化 loading(正在加载) loaded(加载完毕) interactive(交互) complete(完成)

(5)pageshow和pagehide事件

(6)hashchange事件

在URL测参数列表发生变化时通知开发人员。设置这个事件,是因为在Ajax应用中,开发人员经常要利用URL参数列来保存状态或导航信息。

hashchange事件处理程序添加给window对象,然后URL参数列表只要变化就会调用他。此时的event对象包含两个属性:oldURL和newURL。这两个属性保存着参数列表变化前后的完整URL。

8.设备事件

(1)orientationchange事件

safari

window.oriantation属性包含3个值:0 、90 、90

(2)MozOrientation事件

firefox

(3)deviceorientation事件

(4)devicemotion事件

9.触摸与手势事件

(1)触摸事件

touchstart: 当手指触摸屏幕时触发;即使已经有一个手指放在了屏幕上也会触发 touchmove:当手指在屏幕上滑动时连续地触发。这个时间发生期间,调用preventDefault()可以阻止滚动。 touchend:当手指从屏幕上移开时触发 touchcancel:当系统停止跟踪触摸时触发。

除了常见的属性,触摸事件还包含三个用于跟踪触摸的属性

touches:表示当前跟踪的触摸操作的touch对象的数组 targetTouches:特定于事件目标的touch对象的数组 changeTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组

其中的每一个touch对象包含以下属性:

clientX clientY identifier pageX pageY screenX screenY target

这些事件发生的顺序:

touchstart mouseover mousemove mousedown mouseup click touchend

(2)手势事件

当两个手指触摸屏幕时就会产生手势

gesturestart:当一个手指已经按在屏幕上而另一个手指又触摸屏幕时触发 gesturechange:当触发的任意一个手指的位置发生变化时触发 gestureend:当任何一个手指从屏幕上移开时触发。

手势事件的event对象都包含标准的鼠标事件属性之外,还有rotation和scale

rotation属性表示手指变化引起的旋转角度。负值表示逆时针旋转,正值表示顺时针旋转

scale属性表示两个手指间距离的变化情况

####内存和性能

1.事件委托

利用事件冒泡,所有用到按钮的事件都适合事件委托技术。

最适合采用事件委托技术的事件有:click、mousedown、mouseup、keydown、keyup、keypress。

2.移除事件处理程序

空事件处理程序也是造成web应用程序内存与性能问题的主要原因。

	btn.onclick = function(){
		btn.onclick = null; //移除事件处理程序
		document.getElementById("myDiv").innerHTML = "Processing...";
	};

注意,在事件处理程序中删除按钮也能阻止事件冒泡。目标元素在文档中是事件冒泡的前提。

事件委托技术优势:需要跟踪的时间处理程序越少,移除他们就越容易。

####模拟事件

在测试web应用程序,,模拟触发事件是一种极其有用的技术。

1.DOM中的事件模拟

createEvent()方法创建event对象。接收一个参数,即表示要创建的事件类型的字符串。

触发事件:dispatchEvent()方法,所有支持事件的DOM节点都支持这个方法。

(1)模拟鼠标事件

返回的对象有一个名为initMouseEvent()方法,用于指定与该鼠标事件有关的信息。

var btn = document.getElementById("myBtn");
var event = document.createEvent("MouseEvents");//创建事件对象
event.initMouseEvent(....);//初始化事件对象
btn.dispatchEvent(event);//触发事件

(2)模拟键盘事件

1.IE中的事件模拟