博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JS基础篇--事件绑定及深入
阅读量:6933 次
发布时间:2019-06-27

本文共 10732 字,大约阅读时间需要 35 分钟。

学习要点:

1.传统事件绑定的问题

2.W3C事件处理函数
3.IE事件处理函数
4.事件对象的其他补充

事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定(DOM2级模型)。现代事件绑定在传统绑定上提供了更强大更方便的功能。

一 传统事件绑定的问题

传统事件绑定中的内联模型不做讨论,基本很少去用。先来看一下脚本模型,脚本模型将一个函数赋值给一个事件处理函数。传统绑定如:

window.onload=function(){    var box=document.getElementById('box');    box.onclick = function(){        alert('Lee');    };};

问题一:一个事件处理函数触发两次事件

如果一个页面有两个或者多个js,并且第一个js是第一个程序开发的,第二个js是第二个程序员开发的。第一个window.onload被覆盖了,如

window.onload=function(){    alert('Lee');};window.onload=function(){    alert('Mr.lee');}

结果只是打印了 Mr.lee

其实是有办法解决这个问题的,看下面这两种形式。

a:

alert(window.onload);//一开始没有注册window.onload,那么就是nullwindow.onload=function(){    alert('Lee');};alert(window.onload);//如果已经有window.onload,打印的是函数functionwindow.onload=function(){    alert('Mr.lee');}

b:

alert(typeof window.onload);//一开始没有window.onolad,旧版火狐显示undefined,新版显示object,window.onload=function(){    alert('Lee');};alert(typeof window.onload);//如果已经有window.onload,所有浏览器都会显示functionwindow.onload=function(){    alert('Mr.lee');}

所以解决办法有了。

window.onload=function(){    alert('Lee');};if(typeof window.onload=='function'){    var saved=null;//保存上一个事件对象    saved=window.onload;}//saved 就是window.onload,saved()相当于window.onload(),但是window.onload()不能执行的//所以saved()相当于window.onload=function(){}window.onload=function(){    if(saved){        saved();//执行上一个事件 window.onload=function(){}    }    alert('Mr.lee'); //执行本事件}

问题二:事件切换器

切换一个id为box的div,让里面的背景red与blue直接切换,并且切换之前弹框一次,如:

window.onload=function(){    var box=document.getElementById('box');    box.className="red";    box.onclick=function(){        alert('Lee'); //只执行了一次        blue.call(this);//通过匿名函数执行某一函数,那么里面的this就是代表的window,所以可以通过call传递    };}function blue(){    this.className="blue";    this.onclick=red;    }function red(){    this.className="red";    this.onclick=blue;}

上面的代码虽然实现了切换功能,但是弹框只执行了一次。

//添加事件函数//obj相当于window//type相当于onload//fn相当于function(){}function addEvent(obj,type,fn){    //用于保存上一个事件    var saved=null;    if(typeof obj['on'+type]=='function'){        saved=obj['on'+type];//保存上一个事件    }    obj['on'+type]=function(){        if(saved){            saved();        }        fn.call(this);    }    }addEvent(window,'load',function(){    var box=document.getElementById("box");    //addEvent(box,'click',function(){ //目的达到,每次都执行了,没有被覆盖    //    alert('ss');    //});    addEvent(box,'click',blue);});function red(){    this.className="red";    addEvent(box,'click',blue);}function blue(){    this.className="blue";    addEvent(box,'click',red);}//当不停的切换的时候,浏览器突然卡死,并且报错:too much recursion,太多的递归//因为积累了太多的保存的事件//解决方案,就是用完的事件,就立刻移除掉

按照上面的代码出现了注释中的错误,解决的办法如下:

//添加事件函数//obj相当于window//type相当于onload//fn相当于function(){}function addEvent(obj,type,fn){    //用于保存上一个事件    var saved=null;    if(typeof obj['on'+type]=='function'){        saved=obj['on'+type];//保存上一个事件    }    obj['on'+type]=function(){        if(saved){            saved();        }        fn.call(this);    }    }//当不停的切换的时候,浏览器突然卡死,并且报错:too much recursion,太多的递归//因为积累了太多的保存的事件//解决方案,就是用完的事件,就立刻移除掉//移除事件函数function removeEvent(obj,type){    if(obj['on'+type]){        obj['on'+type]=null;    }}addEvent(window,'load',function(){    var box=document.getElementById("box");    //addEvent(box,'click',function(){ //目的达到,每次都执行了,没有被覆盖    //    alert('ss');    //});    addEvent(box,'click',blue);});function red(){    this.className="red";    removeEvent(this,'click');    addEvent(box,'click',blue);}function blue(){    this.className="blue";    removeEvent(this,'click');    addEvent(box,'click',red);}

二 W3C事件处理函数

addEventListener()与removeEventListener()

W3C事件处理函数两个,addEventListener()与removeEventListener()。

//W3C自带的两个添加事件和删除事件

1.覆盖问题,解决

window.addEventListener('load',function(){    alert('Lee');},false);window.addEventListener('load',function(){    alert('Mr.Lee');},false);window.addEventListener('load',function(){    alert('Mrs.Lee');},false);

2.相同函数屏蔽的问题,解决

window.addEventListener('load',init,false);window.addEventListener('load',init,false);window.addEventListener('load',init,false);function init(){    alert('Lee');}

3.是否可以传递this,解决

例子1:

window.addEventListener('load',function(){    var box=document.getElementById('box');    box.addEventListener('click',function(){        alert(this);    },false);},false);

例子2:

window.addEventListener('load',function(){    var box=document.getElementById('box');    box.addEventListener('click',blue,false);},false);function red(){    this.className="red";    this.removeEventListener('click',red,false);    this.addEventListener('click',blue,false);}function blue(){    this.className="blue";    this.removeEventListener('click',blue,false);    this.addEventListener('click',red,false);}

4.添加一个额外的方法,会不会被覆盖,或者只能执行一次,解决

window.addEventListener('load',function(){    var box=document.getElementById('box');    box.addEventListener('click',function(){        alert('Lee');    },false);    box.addEventListener('click',blue,false);},false);

综上所述:W3C是比较完美的解决了这些问题,非常好用,但是IE8和之前的浏览器并不支持,而是采用了自己的事件,当然IE9已经完全支持了W3C的这两个事件处理函数。

W3C可以设置冒泡和捕获方式。

支持W3C标准的浏览器在添加事件时用addEventListener(event,fn,useCapture)方法,基中第3个参数useCapture是一个Boolean值,用来设置事件是在事件捕获时执行,还是事件冒泡时执行。而不兼容W3C的浏览器(IE)用attachEvent()方法,此方法没有相关设置,不过IE的事件模型默认是在事件冒泡时执行的,也就是在useCapture等于false的时候执行,所以把在处理事件时把useCapture设置为false是比较安全,也实现兼容浏览器的效果。

图片描述
事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target)。
事件冒泡阶段:事件从事件目标(target)开始,往上冒泡直到页面的最上一级标签。
事件的传播是可以阻止的:
在W3c中,使用stopPropagation()方法
在IE下设置cancelBubble = true

三.IE事件处理函数

attachEvent()和detachEvent()

IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。这两个方法接受相同的参数:事件名称和函数。

在使用这两组函数的时候,先把区别说一下:1.IE不支持捕获,只支持冒泡;2.IE添加事件不能屏蔽重复的函数;3.IE中的this指向的是window而不是DOM对象。4.在传统事件上,IE是无法接受到event对象的,但使用了attchEvent却可以,但有些区别。

1.覆盖问题,解决了,但有不同,结果是Mrs.Lee,Mr.Lee,最后是Lee

window.attachEvent('onload',function(){    alert('Lee');});window.attachEvent('onload',function(){    alert('Mr.Lee');});window.attachEvent('onload',function(){    alert('Mrs.Lee');});

2.相同函数屏蔽的问题,未解决。

window.attachEvent('onload',init);window.attachEvent('onload',init);function init(){    alert('Lee');}

3.是否可以传递this,不能,this指的是window。需要用call方法。

window.attachEvent('onload',function(){    var box=document.getElementById('box');    box.attachEvent('onclick',function(){        //alert(this===box);        alert(this===window);  //true    });});

下面还有办法就是通过window.event.srcElement。代码如下:

window.attachEvent('onload',function(){    var box=document.getElementById('box');    box.attachEvent('onclick',blue);});function red(){    var that=window.event.srcElement;    that.className="red";    that.detachEvent('onclick',red);    that.attachEvent('onclick',blue);}function blue(){    var that=window.event.srcElement;    that.className="blue";    that.detachEvent('onclick',blue);    that.attachEvent('onclick',red);}

4.添加一个额外的方法,会不会被覆盖,或者只能执行一次,解决。

在传统绑定上,IE是无法像W3C那样通过传参接受event对象,但是使用attachEvent()却可以。

window.attachEvent('onload',function(){    var box=document.getElementById('box');    box.onclick=function(evt){ //传统方法IE无法通过参数获取evt        alert(evt);//undefined    }    box.attachEvent('onclick',function(evt){        alert(evt);//object        alert(evt.type);//click        alert(evt.srcElement.tagName);//DIV        alert(window.event.srcElement.tagName);//DIV    });});

跨浏览器的兼容

跨浏览器添加事件

function addEvent(obj,type,fn){    if(obj.addEventListener){        obj.addEventListener(type,fn,false);    }else if(obj.attachEvent){        obj.attachEvent('on'+type,fn);    }}

跨浏览器移除事件

function removeEvent(obj,type,fn){    if(obj.removeEventListener){        obj.removeEventListener(type,fn,false);    }else if(obj.detachEvent){        obj.detachEvent('on'+type,fn);    }}

跨浏览器获取目标对象

function getTarget(evt){    if(evt.target){        return evt.target;    }else if(window.event.srcElement){        return window.event.srcElement;    }}

调用方式:

addEvent(window,'load',function(){    var box=document.getElementById('box');    addEvent(box,'click',blue);});function red(evt){    var that=getTarget(evt);    that.className="red";    removeEvent(that,'click',red);    addEvent(that,'click',blue);}function blue(evt){    var that=getTarget(evt);    that.className="blue";    removeEvent(that,'click',blue);    addEvent(that,'click',red);}

四.事件对象的其他补充

relatedTarget事件

w3c中的一个relatedTarget事件。

例如:

addEvent(window,'load',function(){    var box=document.getElementById('box');    addEvent(box,'mouseover',function(evt){        alert(evt.relatedTarget); //得到移入box最近的那个DOM对象    });        addEvent(box,'mouseout',function(evt){        alert(evt.relatedTarget); //从box移出最近的那个DOM对象    });});

IE提供了两组分别用于移入移出的属性fromElement和toElement,分别对应mouseover和mouseout。

addEvent(window,'load',function(){    var box=document.getElementById('box');    addEvent(box,'mouseover',function(){        alert(window.event.fromElement.tagName); //得到移入box最近的那个DOM对象    });        addEvent(box,'mouseout',function(){        alert(window.event.toElement.tagName); //从box移出最近的那个DOM对象    });});

PS:fromElement和toElement如果分别对应相反的鼠标事件,没有任何意义。

剩下要做的就是跨浏览器兼容操作:

function getTarget(evt){    var e=evt || window.event;    if(e.srcElment){ //IE        if(e.type=='mouseover'){            return e.fromElement.tagName;        }else if(e.type="mouseout"){            return e.toElement.tagName;        }    }else if(e.relatedTarget){ //w3c        return e.relatedTarget;    }}

屏蔽跳转操作

取消事件的默认行为有一种不规范的做法,就是返回false。

link.onclick=function(){    alert('Lee');    return false;}

PS:虽然return false;可以实现这个功能,但是有漏洞。

第一:必须写到最后,这样导致中奖的代码执行后,有可能执行不到return false;
第二:return false 写到最前那么之后的自定义操作就失效了。
所以最好的办法应该是在最前面就阻止默认行为,并且后面的代码还可以执行。

link.onclick=function(evt){    evt.preventDefault;//w3c,阻止默认行为    alert('Lee');}link.onclick=function(evt){    window.event.returnValue=false;//IE,阻止默认行为    alert('Lee');}

那么跨浏览器的兼容:

function preDef(evt){    var e=evt || window.event;    if(e.preventDefault){        e.preventDefault();    }else{        e.returnValue=false;    }}

右键菜单contextmenu

兼容:

function preDef(evt){    var e=evt || window.event;    if(e.preventDefault){        e.preventDefault();    }else{        e.returnValue=false;    }}addEvent(window,"load",function(){    var body=document.getElementsByTagName('body')[0];    addEvent(body,'contextmenu',function(evt){        preDef(evt);    })});

PS:contextmenu事件很常用,这直接导致浏览器兼容性较为稳定。

卸载前事件:beforeunload

这个事件可以帮助在离开本页的时候给出相应的提示,“离开”或者“返回”操作。

addEvent(window,'beforeonload',function(){    preDef(evt);});

鼠标滚轮(mousewheel)和DOMMouseScroll

用于获取鼠标上下滚轮的距离

addEvent(document,'mousewheel',function(evt){ //非火狐    alert(getWD(evt));});addEvent(document,'DOMMouseScroll',function(evt){ //火狐    alert(getWD(evt));});function getWD(evt){    var e=evt|| window.event;    if(e.wheelDelta){        return e.wheelDelta;    }else if(e.detail){ //火狐        return -evt.detail*30;    }}

PS:通过浏览器检测可以确定火狐只执行DOMMouseScroll。

DOMContentLoaded事件和readystatechange事件

DOMContentLoaded事件和readystatechange事件,有关DOM加载方面的事件。

转载地址:http://sqmjl.baihongyu.com/

你可能感兴趣的文章
jdbc 取 oracle comments
查看>>
linux kickstart无人值守安装实现多版本的引导
查看>>
先来个基础的windos内部机制与ipc$
查看>>
宫崎骏首次因为自己的新作流泪
查看>>
SAP sybase16 安装的一些细节问题
查看>>
linux服务器同步时间
查看>>
Android开发常见问题及解决方法
查看>>
Linux 基础 - 磁盘管理 - 01
查看>>
我的友情链接
查看>>
cisco 设备配置ACL访问控制列表
查看>>
冒泡排序
查看>>
Linux grep 命令正则匹配IP地址
查看>>
华为7.0系统手机最完美激活Xposed框架的教程
查看>>
繁忙的IT基础设施可能导致安全灾难
查看>>
Objective-C之Block
查看>>
iOS 图片加载框架-SDWebImage 解读
查看>>
Flash ActionScript3.0 中txt文档根据模板转换成html文档的实现
查看>>
安景业安京业安敬业anjingye
查看>>
java rest的说明
查看>>
Angular在页面加载很慢的时候,会出现双花括号的问题
查看>>