`

js事件

 
阅读更多
先说下js事件中几个重要的概念:事件对象、目标元素(事件源)、冒泡事件流、捕获事件流。
事件对象:事件对象只有在事件发生时才会产生,是用来记录事件发生时相关信息的对象,只能在事件处理函数内部访问。
//W3C DOM把事件对象作为事件处理函数的第一个参数传入进去 
domObj.onclick = function(e){
    alert(e);
};
//IE将事件对象作为window对象的一个属性(相当于全局变量)
domObj.onclick = function(){
    alert(window.event);
};
//所以为了兼容各种浏览器,获取事件对象可以这样处理:
domObj.onclick = function(e){
    var evt = e || window.event;
    alert(evt);
};

//也可以这样直接在页面中获取事件对象。注意:1.此时event就是事件对象,必须写成event,不能写为event22等其他形式;2.event参数可以在任意位置,比如onclick="clickFun(this,1,2,3,event,4,5)"也是可以的
<div onclick="clickFun(this,event)"></div>

目标元素: 任何一个事件的目标元素都是最开始的那个元素,并且它在事件对象中以属性的形式出现。使用事件代理的话我们可以把事件处理器添加到一个元素上,等待事件从它的子级元素里冒泡上来,并且可以很方便地获取这个事件的目标元素。js中目标元素又称为事件源。

冒泡事件流: 当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发,从原始元素开始一直冒泡到DOM树的最上层,这一过程被称为事件冒泡。在冒泡过程中的任何时候都可以终止事件的冒泡,在现代浏览器里可以通过调用事件对象上的stopPropagation()方法,在IE里可以通过设置事件对象的cancelBubble属性为true。也就是说,如果不停止事件的传播,由于js事件冒泡的机制,子元素会触发父级的事件,一直冒泡到最上层的元素(事件将一直通过DOM冒泡直至到达文档根)。
总结:冒泡型事件按照从最特定的事件目标到最不特定的事件目标的顺序触发(冒泡是从下级元素到上级元素)。
捕获事件流:事件的处理将从DOM层次的根开始,而不是从触发事件的目标元素开始,事件从目标元素的所有祖先元素依次往下传递。在这个过程中,事件会从文档根到事件目标元素之间各个继承派生的元素所捕获,如果事件监听器在被注册时设置了useCapture属性为true,那么它们可以被分派给这期间的任何元素以对事件做出处理;否则,事件会被接着传递给派生元素路径上的下一元素,直至目标元素。事件到达目标元素后,它会接着通过DOM节点再进行冒泡。
总结:捕获型事件按照从最不特定的事件目标到最特定的事件目标的顺序触发(与冒泡型事件顺序相反,捕获是从上级元素到下级元素)。

IE浏览器中的事件流:只支持冒泡事件流,不支持捕获事件流。
现代浏览器中的事件流:同时支持两种事件模型:捕获型事件和冒泡型事件,且捕获型事件先发生。 window->document->html->body->div->body->html->document->window  所以对于事件目标是接受了2次事件,一次是捕获过程,一次是冒泡过程。

事件冒泡的优点和缺点
想象一下现在我们有一个10列、100行的HTML表格(采用div-ul-li合成的表格),你希望在用户点击表格中的某一单元格(li)的时候做点什么,比如需要让表格中的每一个单元格在被点击的时候变成可编辑状态。如果把事件处理器加到这1000个单元格会产生一个很大的性能问题,有可能导致内存泄露甚至是浏览器的崩溃。相反地,使用事件代理的话,你只需要把一个事件处理器添加到表格div上就可以了,这个函数可以把点击事件给截下来,并且判断出是哪个单元格被点击了。
我们为这个表格div添加一个事件处理器以调用clickFun函数,此函数需要先判断出传到div来的事件的目标元素,然后再根据具体的条件去做相应的处理即可:
var clickFun = function(e){
    var evt = e || window.event;
    var src = evt.target || evt.srcElement; //获取事件源对象
    if(src.tagName=='LI'){
        //do something...
    }
}

所以总结下冒泡的优点有:
1.需要创建的以及驻留在内存中的事件处理器少了,提高了性能,并降低了崩溃的风险。
2.在DOM更新后无须重新绑定事件处理器。如果你的页面是动态生成的,你不再需要在元素被载入或者卸载的时候添加或者删除事件处理器。
缺点:你的事件管理代码有成为性能瓶颈的风险,所以尽量使它能够短小精悍。
并不是所有的事件都能冒泡,blur、focus、load和unload不能像其它事件一样冒泡。

停止事件冒泡
IE:window.event.cancelBubble=true;
现代浏览器:e.stopPropagation();

阻止事件的默认行为
IE:window.event.returnValue=false;
现代浏览器:e.preventDefault();


事件处理函数
IE中:
obj.attachEvent(evtype,fn)---IE提供的添加事件处理函数的方法。obj是要添加事件的dom对象,evtype是事件类型,带on前缀,fn是事件处理函数。缺点:IE不支持事件捕获,且使用IE的attachEvent方法时在事件处理函数内部,this指向了window,而不是obj!解决attachEvent中this指向问题的方法是:
obj.attachEvent("on"+evtype,function(){fn.call(obj)});
obj.detachEvent(evtype,fn,)---IE提供的删除事件处理函数的方法,evtype包含on前缀。
现代浏览器:
obj.addEventListener(evtype,fn,useCapture)——W3C提供的添加事件处理函数的方法。obj是要添加事件的对象,evtype是事件类型,不带on前缀,fn是事件处理函数,如果useCapture是true,则事件处理函数在捕获阶段被执行,否则在冒泡阶段执行
obj.removeEventListener(evtype,fn,useCapture)——W3C提供的删除事件处理函数的方法
obj.event=fn这样事件处理函数会被添加到冒泡阶段

attachEvent和addEventListener方法的使用:
//如果这样写,那么将会只有medhot3被执行
document.getElementById("btn").onclick = method1;   
document.getElementById("btn").onclick = method2;   
document.getElementById("btn").onclick = method3;

//IE下,运用object.attachEvent(event,function),写成这样:
var btn1Obj = document.getElementById("btn1"); 
btn1Obj.attachEvent("onclick",method1);   
btn1Obj.attachEvent("onclick",method2);   
btn1Obj.attachEvent("onclick",method3);
执行顺序为method3->method2->method1  

//现代浏览器需要用到addEventListener,写成这样:
btn1Obj.addEventListener("click",method1,false); 
btn1Obj.addEventListener("click",method2,false); 
btn1Obj.addEventListener("click",method3,false);
执行顺序为method1->method2->method3

addEventListener参数说明
addEventListener有三个参数:第一个参数表示事件名称(不含on,如"click");第二个参数表示要接收事件处理的函数;第三个参数为useCapture,下面就讲解它:
<div id="outDiv">
  <div id="middleDiv">
    <div id="inDiv">请在此点击鼠标。</div>
  </div>
</div>
<div id="info"></div>

var outDiv = document.getElementById("outDiv");
var middleDiv = document.getElementById("middleDiv");
var inDiv = document.getElementById("inDiv");
var info = document.getElementById("info");

outDiv.addEventListener("click",function(){info.innerHTML+="outDiv" +"<br>";},false);
middleDiv.addEventListener("click",function(){info.innerHTML +="middleDiv"+"<br>";},false);
inDiv.addEventListener("click",function(){info.innerHTML+="inDiv" +"<br>";},false);

上述是我们测试的代码,根据info的显示来确定触发的顺序,有三个addEventListener,而useCapture可选值为true和false,所以2*2*2,可以得出8段不同的程序:
全为false时,触发顺序为:inDiv、middleDiv、outDiv;
全为true时,触发顺序为:outDiv、middleDiv、inDiv;
outDiv为true,其他为false时,触发顺序为:outDiv、inDiv、middleDiv;
middleDiv为true,其他为false时,触发顺序为:middleDiv、inDiv、outDiv;
……
最终得出如下结论:
true的触发顺序总是在false之前;
如果多个均为true,则外层的触发先于内层;
如果多个均为false,则内层的触发先于外层。

附:几个常用的事件相关的处理方法
/**
* 模拟触发调用
*/
可适用于所有html元素及任何事件。
当eventName为"click"时,exeEvent有的时候产生的是执行了btn.onclick()的效果。嗯,确实是这个效果,但是意义却完全不同,btn.onclick()只是一个对象对其方法的调用,它的执行必须依赖于用户对其赋值,否则btn.onclick为null,是不能执行btn.onclick()的。而exeEvent的效果,“等同于”鼠标在元素上进行了点击。
function exeEvent(domObj, eventName){
    if(document.all && typeof(document.all)=="object"){//判断是否是IE浏览器
        domObj.fireEvent('on' + eventName);   
    }else{
        var event=document.createEvent("HTMLEvents");//或document.createEvent("MouseEvents"); 
        event.initEvent(eventName, true, true);   
        domObj.dispatchEvent(event);   
    }   
}
其中event.initEvent(eventName,canBubble,cancelable)参数 描述  
eventName 字符串值。事件的类型。可以是"click"、"mouseup"等
canBubble 事件是否起泡。  
cancelable 事件是否可以用preventDefault()方法取消事件。

/**
* 添加事件
*/
function addEvent(target, eventName, fn){
    if(document.all && typeof(document.all)=="object"){//判断是否是IE浏览器
        target.attachEvent("on"+eventName, fn);
    }else{
        target.addEventListener(eventName, fn, false);
    }
}
使用IE的attachEvent方法时在事件处理函数内部,this指向了window,而不是target!下面是改进后的方法,可以解决attachEvent中this指向问题
function addEvent(target, eventName, fn){
    if(document.all && typeof(document.all)=="object"){//判断是否是IE浏览器
        target.attachEvent("on"+eventName, function(){   
            fn.call(target);   
        });   
    }else{
        target.addEventListener(eventName, fn, false);
    }
}

/**
* 删除事件
*/
function removeEvent(target, eventName, fn){
    if(document.all && typeof(document.all)=="object"){//判断是否是IE浏览器
        target.detachEvent("on"+eventName, fn);
    }else{
        target.removeEventListener(eventName, fn, false);
    }
}

/**
* 获取事件对象与事件源
*/
function getEventElement(e){
    var evt = e || window.event;//获取事件对象
    var src = evt.target || evt.srcElement;//获取事件源对象
    return src;
}

/**
* 事件停止冒泡
*/
function stopBubble(e){
    if(document.attachEvent) {//ie
        window.event.cancelBubble = true;
    }else{
        e.stopPropagation();
    }
}

/**
* 阻止事件的默认行为
*/
function preventDefault(e){
    if(document.attachEvent) {//ie
        window.event.returnValue=false;
    }else{
        e.preventDefault();
    }
}

附:js常用事件
//一般事件
onclick      鼠标点击时触发此事件
ondblclick   鼠标双击时触发此事件
onmousedown  按下鼠标时触发此事件
onmouseup    鼠标按下后松开鼠标时触发此事件
onmouseover  鼠标移动到某对象范围的上方时触发此事件
onmousemove  鼠标移动时触发此事件
onmouseout   鼠标离开某对象范围时触发此事件
onkeypress   键盘上的某个键被按下并且释放时触发此事件.
onkeydown    键盘上某个按键被按下时触发此事件
onkeyup      键盘上某个按键被按放开时触发此事件
 
//页面相关事件
onabort      图片在下载时被用户中断
onbeforeunload当前页面的内容将要被改变时触发此事件
onerror      出现错误时触发此事件
onload       页面内容完成时触发此事件
onmove       浏览器的窗口被移动时触发此事件
onresize     浏览器的窗口大小被改变时触发此事件
onscroll     浏览器的滚动条位置发生变化时触发此事件
onstop       浏览器的停止按钮被按下时触发此事件或者正在下载的文件被中断
oncontextmenu当弹出右键上下文菜单时发生
onunload     当前页面将被改变时触发此事件
 
//表单相关事件
onblur       当前元素失去焦点时触发此事件
onchange     当前元素失去焦点并且元素的内容发生改变而触发此事件
onfocus      当某个元素获得焦点时触发此事件
onreset      当表单中RESET的属性被激发时触发此事件
onsubmit     一个表单被递交时触发此事件


参考:http://www.iteye.com/topic/517899
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics