这篇文章主要介绍了 javascript 事件绑定学习要点, 主要包含下面四个方面 1. 传统事件绑定的问题,2.W3C 事件处理函数,3.IE 事件处理函数,4. 事件对象的其他补充,有需要的小伙伴可以参考下
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定(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,那么就是null
- window.onload=function(){
- alert('Lee');
- };
- alert(window.onload);//如果已经有window.onload,打印的是函数function
- window.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,所有浏览器都会显示function
- window.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 是比较安全,也实现兼容浏览器的效果。
三. 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://www.phperz.com/article/17/0228/266762.html