Javascript开发之九:事件

     每个事件只能注册一个函数,如果多个就后被才替换掉。

     解决方法 一个对象上添加多个方法

 

 

 

function addEvent(obj,evt,fn) {
if (obj.addEventListener) {
if (String(window.opera)=="[object Opera]") {
obj.addEventListener(evt,function (evt) {
evt.layerX=evt.offsetX;
evt.layerY=evt.offsetY;
fn.call(this,evt);
},false);
} else {
obj.addEventListener(evt,fn,false);
}
obj.addEventListener(evt,fn,false);
return obj;
}


if (!obj.functions) obj.functions={};
if (!obj.functions[evt])
obj.functions[evt]=[];

//obj.functions["mousedown"]=[]
var functions=obj.functions[evt];
for (var i=0;i<functions.length;i++) {
if (functions[i]===fn) return obj;
}
functions.push(fn);
//fn.index=functions.length-1;


if (typeof obj["on"+evt]=="function") {
//alert(obj["on"+evt]);//当这一行执行到时,obj["on"+evt] 就是handler
//alert(obj["on"+evt]==handler);
if (obj["on"+evt]!=handler)
functions.push(obj["on"+evt]);
}
obj["on"+evt]=handler;
return obj;
}

 

 

 

function addEvent(obj,evt,fn){
//给一个对象添加多个事件
var saved;
if(typeof obj["on"+evt]=="function")//如果已经定义了就把它拿出来,然后赋给saved保存起来
{
saved=obj["on"+evt];
}
obj["on"+evt]=function(){ //开始一个事,调用一个方法,如果saved方法存在,则执行saved,
if(saved){
saved();//如果有则执行,最后再执行 fn();
}
fn();
};
}

addEvent(window,'load',initAll);
function initAll()
{
var oDiv=document.getElementById("oDiv");
oDiv.onclick=toBig;
function toBig(){
oDiv.className="toBig";
oDiv.onclick=toSmall;
}

function toSmall(){
oDiv.className="toSmall";
oDiv.onclick=toBig;
}
}

addEvent(window,'load',alertTest);
function alertTest()
{
alert("第二个方法")
}

addEvent(window,'load',alertTest);
function alertTest()
{
alert("第四个方法")
}
</script>
<style type="text/css">
.toBig{
width:200px;
height:200px;
}
.toSmall{
width:50px;
height:50px;
}
</style>
</head>

<body>
<h1>事件(上)</h1>
<div id="oDiv" class="small" style="border:2px solid blue;background:pink;">oDiv</div>
<script type="text/javascript">
addEvent(window,'load',init)
function init()
{
alert("第三个方法");
}
</script>
</body>
</html>

 

事件 Event 

   evt=evt || event    

   button属性: event.button(要在mouseup mousedown) 按下左,中,右键    0,1,2

   

事件对象的属性及方法

鼠标相关
属性名值类型读/写描述
button Integer R

对于特定的鼠标事件,表示按下的鼠标按钮,该属性仅可以在mouseup与mousedown事件中访问。W3C 规定:0表示按下了左键,1表示按下了中键,2表示按下了右键,相当于对于鼠标键从左到右进行的编号,而编号从0开始; 而IE有另外一套规定:0表示没有任何键按下,1表示左键,2表示右键,4表示中键,而其它按键的组合则只要将键码相加即可,如:同时按下左右键时button值为3

clientX Integer R 事件发生时,鼠标在客户端区域的X坐标,客户端区域是指页面可视区域
clientY Integer R 事件发生时,鼠标在客户端区域的Y坐标
screenX Integer R(IE) R/W(W3C) 相对于屏幕的鼠标X坐标
screenY Integer R(IE) R/W(W3C) 相对于屏幕的鼠标Y坐标
x(仅IE) Integer R 鼠标相对于引起事件的元素的父元素的X坐标
y(仅IE) Integer R 鼠标相对于引起事件的元素的父元素的Y坐标
offsetX(仅IE) layerX(仅W3C) Integer R 鼠标相对于引起事件的对象的X坐标
offsetY(仅IE) layerY(仅W3C) Integer R 鼠标相对于引起事件的对象的Y坐标
pageX(仅W3C) Integer R 鼠标相对于页面的X坐标
pageY(仅W3C) Integer R 鼠标相对于页面的Y坐标

 

 

键盘相关
属性名值类型读/写描述
altKey Boolean R true表示按下了ALT键;false表示没有按下
ctrlKey Boolean R true表示按下了CTROL,false表示没有
shiftKey Boolean R true表示按下了shift,false表示没有
keyCode Integer R/W(IE) R(W3C) 对于keypress事件,表示按下按钮的Unicode字符;对于keydown/keyup事件 ,表示按下按钮的数字代号
charCode(仅W3C) Integer R 在keypress事件中所按键的字符Unicode编码,如果不是字符键,则该属性为0,并且,当CapsLock打开与关闭时charCode的值也对应着大小写字母

 

 

如下是事件对两大浏览器的修正过程:

 

<script type="text/javascript">
window.onload=function(){
var oDiv=document.getElementById("oDiv");
var para=document.getElementById("para");
//alert(para.target);
//alert(oDiv.target)
/*oDiv.onmousedown=function(evt){
var evt=evt || window.event;
//oDiv.onclick=function(event){alert(event.target.tagName);}
//para.onclick=function(event){alert(event.target.tagName);}
var target=evt.target || evt.srcElement;
alert(this.tagName);
alert(target.tagName);*/
document.onclick=function(evt)
{
evt=fixEvt(evt);
alert("document:"+evt.target.tagName);
}

oDiv.onclick=function(evt){
evt=fixEvt(evt);
alert("oDiv:"+evt.target.tagName);
}

para.onclick=function(evt)
{
evt=fixEvt(evt);
alert("para:"+evt.target.tagName);
}
function fixEvt(evt)
//修正事件对象在各个浏览器中的不同
{
evt=evt || window.event;
if(!evt.target){
evt.target=evt.srcElement;
evt.layerX=evt.offsetX;
evt.layerY=evt.offsetY;
}
return evt;
}
}
</script>
<style type="text/css">
#oDiv{
width:200px;
height:200px;
background:blue;
}
#para{
width:100px;
height:20px;
background:yellow;
display:block;
}
</style>
</head>

<body>
<div id="oDiv">oDiv
<p id="para">P</p>
</div>
</body>
</html>

 冒泡与捕获( w3c 标准都可以)而微软是冒泡

 冒泡:从里向外 反之是捕获

 停止事件流:evt.stopPropagation(); 但在IE7.0以则要用: evt.cancelBubble=true;

所以如下总结:

 

事件传播——冒泡与捕获

DOM事件标准定义了两种事件流,这两种事件流有着显著的不同并且可能对你的应用有着相当大的影响。这两种事件流分别是捕获和冒泡。和许多Web 技术一样,在它们成为标准之前,Netscape和微软各自不同地实现了它们。Netscape选择实现了捕获事件流,微软则实现了冒泡事件流。幸运的 是,W3C决定组合使用这两种方法,并且大多数新浏览器都遵循这两种事件流方式。

默认情况下,事件使用冒泡事件流,不使用捕获事件流。然而,在Firefox和Safari里,你可以显式的指定使用捕获事件流,方法是在注册事件时传入useCapture参数,将这个参数设为true。

冒泡事件流

当事件在某一DOM元素被触发时,例如用户在客户名字节点上点击鼠标,事件将跟随着该节点继承自的各个父节点冒泡穿过整个的DOM节点层次,直到 它遇到依附有该事件类型处理器的节点,此时,该事件是onclick事件。在冒泡过程中的任何时候都可以终止事件的冒泡,在遵从W3C标准的浏览器里可以 通过调用事件对象上的stopPropagation()方法,在Internet Explorer里可以通过设置事件对象的cancelBubble属性为true。如果不停止事件的传播,事件将一直通过DOM冒泡直至到达文档根。

捕获事件流

事件的处理将从DOM层次的根开始,而不是从触发事件的目标元素开始,事件被从目标元素的所有祖先元素依次往下传递。在这个过程中,事件会被从文 档根到事件目标元素之间各个继承派生的元素所捕获,如果事件监听器在被注册时设置了useCapture属性为true,那么它们可以被分派给这期间的任 何元素以对事件做出处理;否则,事件会被接着传递给派生元素路径上的下一元素,直至目标元素。事件到达目标元素后,它会接着通过DOM节点再进行冒泡。

现代事件绑定方法

针对如上节课所讨论的,使用传统事件绑定有许多缺陷,比如不能在一个对象的相同事件上注册多个事件处理函数。而浏览器和W3C也并非没有考虑到这一点,因此在现代浏览器中,它们有自己的方法绑定事件。

W3C DOM

  • obj.addEventListener(evtype,fn,useCapture)——W3C提供的添加事件处理函数的方法。obj是 要添加事件的对象,evtype是事件类型,不带on前缀,fn是事件处理函数,如果useCapture是true,则事件处理函数在捕获阶段被执行, 否则在冒泡阶段执行
  • obj.removeEventListener(evtype,fn,useCapture)——W3C提供的删除事件处理函数的方法

微软IE方法

  • obj.attachEvent(evtype,fn)——IE提供的添加事件处理函数的方法。obj是要添加事件的对象,evtype是事件类型,带on前缀,fn是事件处理函数,IE不支持事件捕获
  • obj.detachEvent(evtype,fn,)——IE提供的删除事件处理函数的方法,evtype包含on前缀

<script type="text/javascript">
window.onload=function(){
var oDiv=document.getElementById("oDiv");
var para=document.getElementById("para");
//alert(para.target);
//alert(oDiv.target)
/*oDiv.onmousedown=function(evt){
var evt=evt || window.event;
//oDiv.onclick=function(event){alert(event.target.tagName);}
//para.onclick=function(event){alert(event.target.tagName);}
var target=evt.target || evt.srcElement;
alert(this.tagName);
alert(target.tagName);*/
document.addEventListener('click',function(evt)
{
evt=fixEvt(evt);
alert("document:"+evt.target.tagName);
},true)

oDiv.addEventListener('click',function(evt){
evt=fixEvt(evt);
alert("oDiv:"+evt.target.tagName);
},true)

para.addEventListener('click',function(evt)
{
evt=fixEvt(evt);
alert("para:"+evt.target.tagName);
//evt.stopPropagation();
},true)
function fixEvt(evt)
//修正事件对象在各个浏览器中的不同
{
evt=evt || window.event;
if(!evt.target){
evt.target=evt.srcElement;
evt.layerX=evt.offsetX;
evt.layerY=evt.offsetY;
}
return evt;
}
}
</script>
<style type="text/css">
#oDiv{
width:200px;
height:200px;
background:blue;
}
#para{
width:100px;
height:20px;
background:yellow;
display:block;
}
</style>
</head>

<body>
<div id="oDiv">oDiv
<p id="para">P</p>
</div>
</body>
</html>

 

 

 

标准化事件对象

IE的事件对象与W3C DOM的事件对象有许多不一样的地方,解决的最好的方法就是调整IE的事件对象,以使它尽可能的与标准相似!下表列出了IE事件对象中一些和W3C DOM名称或值不一样但含义相同的属性

IE与W3C DOM事件对象的不同
W3C DOMIE
button——按键编码为:0-左键,1-中键,2-右键 button——按键编码为:1-左键,2-右键,4-中键
charCode 没有对应属性,但可以用keyCode来代替
preventDefault 没有对应方法,但可以将event对象的returnValue设为false来模拟
target srcElement
relatedTarget fromElement与toElement
stopPropagation 没有对应方法,但可以通过将event对象的cancelBubble属性设为true来模拟

 

定义和用法

取消事件的默认动作。

语法

event.preventDefault()

说明

该方法将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。例如,如果 type 属性是 "submit",在事件传播的任意阶段可以调用任意的事件句柄,通过调用该方法,可以阻止提交表单。注意,如果 Event 对象的 cancelable 属性是 fasle,那么就没有默认动作,或者不能阻止默认动作。无论哪种情况,调用该方法都没有作用。

总结出fixEvent函数

 

 

 

	function fixEvent(evt) {
		if (!evt.target) {
			evt.target = evt.srcElement;
			evt.preventDefault = fixEvent.preventDefault;
			evt.stopPropagation = fixEvent.stopPropagation;
			if (evt.type == "mouseover") {
				evt.relatedTarget = evt.fromElement;
			} else if (evt.type =="mouseout") {
				evt.relatedTarget = evt.toElement;
			}
			evt.charCode =  (evt.type=="keypress")?evt.keyCode:0;
			evt.eventPhase = 2;//IE仅工作在冒泡阶段
			evt.timeStamp = (new Date()).getTime();//仅将其设为当前时间
		}
		return evt;
	}
	fixEvent.preventDefault =function () {
		this.returnValue = false;//这里的this指向了某个事件对象,而不是fixEvent
	};
	fixEvent.stopPropagation =function () {
		this.cancelBubble = true;
	};




Load事件( jquery中已经提供了所以不用去分析它)

使用JavaScript操纵DOM,必须等待DOM加载完毕才可以执行代码,但window.onload有个坏处,它非要等到页面中的所有图 片及视频加载完毕才会触发load事件。结果就是一些本来应该在打开时隐藏起来的元素,由于网络延迟,在页面打开时仍然会出现,然后又会突然消失,让用户 觉得莫名其妙。必须与这种丑陋的闪烁告别!

	function addLoadEvent(fn) {
	    var init = function() {
	        if (arguments.callee.done) return;
	        arguments.callee.done = true;
	        fn.apply(document,arguments);
	    };
	    //注册DOMContentLoaded事件,如果支持的话
	    if (document.addEventListener) {
	        document.addEventListener("DOMContentLoaded", init, false);
	    }
	    //但对于Safari,我们需要使用setInterval方法不断检测document.readyState
	    //当为loaded或complete的时候表明DOM已经加载完毕
	    if (/WebKit/i.test(navigator.userAgent)) {
	        var _timer = setInterval(function() {
	            if (/loaded|complete/.test(document.readyState)) {
	                clearInterval(_timer);
	                init();
	            }
	        },10);
	    }
	    //对于IE则使用条件注释,并使用script标签的defer属性
	 	//IE中可以给script标签添加一个defer(延迟)属性,这样,标签中的脚本只有当DOM加载完毕后才执行
	 	/*@cc_on @*/
	 	/*@if (@_win32)
	 	document.write("<script id=\"__ie_onload\" defer=\"defer\" src=\"javascript:void(0)\"><\/script>");
	 	var script = document.getElementById("__ie_onload");
	 	script.onreadystatechange = function() {
		 	if (this.readyState == "complete") {
		 		init();
		 	}
	 	};
	 	/*@end @*/
	 	return true;
	 }
	









posted on 2012-11-22 16:21  peter.peng  阅读(223)  评论(0编辑  收藏  举报