JavaScript笔记:事件

JavaScript和HTML之间的交互是通过事件实现的。事件,就是文档或者浏览器窗口中发生一些特定的交互瞬间。可以使用侦听器来预定时间,以便事件发生的时候,执行相应的代码。

1、事件流

首先要明确的概念:当你点击了一个按钮,那么,点击事件将不止发生在按钮上,同时也发生在按钮的所有父级元素上。事件流,描述的是从页面中接受时间的顺序。

1、事件冒泡

即事件由最具体的元素接受,然后逐级向上传递。
例如下面这样的页面结构:

<!DOCTYPE html>
<html>
<head>
   <title>Event Bubbling Example</title>
</head>
<body>
   <div id="myDiv">Click Me</div>
</body>
</html>

如果你点击了div,那么事件就会从div开始,div 》body 》html 》document这样传递上去。所有的现代浏览器都支持事件冒泡。

2、事件捕获

事件捕获的事件顺序和事件冒泡正好相反,最父级的元素将最先接收到事件,然后依次向下传递。大多数浏览器也都支持事件捕获。

3、DOM事件流

DOM2级事件规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。首先发生的是事件捕获阶段,为截获事件提供了机会,然后是实际目标接受到事件,最后是事件冒泡阶段,可以在这个阶段对事件作出相应。还以前面的html页面为例,单击div会按照如下的顺序触发事件:document–html–body–div–body–html–document。
在DOM事件流中,实际的目标div在捕获阶段不会接收到事件。
即使DOM2级事件规范明确要求在捕获阶段时不会涉及到事件目标,但是IE9、Safari、Chrome、Firefox一季Opera 9.5和更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。

2、事件处理程序

事件,就是用户或者浏览器执行的某种动作。例如:click、load、mouseover等都是事件的名字。而响应某个事件的函数就叫做事件处理程序。

1、HTML事件处理程序

某个元素支持的每种事件,都可以用和事件处理程序同名的HTML性质确定,这个特性的值应该是可以执行的JS代码:

<input type="button" value="Click Me" onclick="alert('Clicked')" />

或者,函数可以单独写在JS代码片中:

<script type="text/javascript">
   function showMessage(){
       alert("Hello world!");
   }
</script>
<input type="button" value="Click Me" onclick="showMessage()" />

这样指定事件处理程序有一些独到之处。首先,这样会创建一个封装着元素属性值的函数。这个函数中有一个局部变量event,也就是事件对象。

<input type="button" value="Click Me" onclick="alert(event.type)" />
// 输出click

同时,在函数内部this指针等于事件的目标函数:

<!-- "Click Me" -->
<input type="button" value="Click Me" onclick="alert(this.value)">

上面这段代码可以化简为如下所示:

<!-- "Click Me" -->
<input type="button" value="Click Me" onclick="alert(value)">

另外,如果当前元素是一个表单输入元素,它还可以访问其他表单字段:

<form method="post">
    <input type="text" name="username" value="">
    <input type="button" value="Echo Username" onclick="alert(username.value)">
 </form>

不过,在HTML中指定事件处理程序存在缺点:

存在一个时差的问题。因为用户可能在HTML元素一出现就点击,触发事件,但是事件处理函数可能是在页面底端写的,那么此时,就会出现错误。为此,很多HTML事件处理程序都被放在try-catch块中,不让错误浮出水面:

<input type="button" value="Click Me" onclick="try{showMessage();}catch(ex){}">

2、DOM0级事件处理程序

通过JS指定事件处理程序的传统方式:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert("Clicked");
};

需要注意,在这段代码执行前,事件处理程序不会被指定。因此若代码在页面中位于按钮的后面,可能在有一段时间内,点击按钮无法实现功能。

使用DOM0级的事件处理程序被认为是元素的方法,因此,这时候事件处理方法是在元素的作用域内执行,可以使用this来访问元素。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id);    //"myBtn"
};

另外,事件处理程序也可以被删除:

btn.onclick = null; 

2、DOM2级事件处理程序

DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有的DOM节点中都包含着两个方法,并且该方法接受三个参数:要处理的事件名,作为事件的处理程序,和一个布尔值。布尔值是真表示:在捕获阶段调用程序;是假则表示,在冒泡阶段调用程序。并且事件可以添加多个。代码示例:

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
    alert(this.id);
}, false);
btn.addEventListener("click", function(){
    alert("Hello world!");
}, false);

通过addEventListener添加的函数可以通过removeEventListener函数移除,移除时传入的参数和添加的方法相同。但是需要注意的是,想上面那样添加的匿名函数是无法移除的。
如果想要移除,需要这样定义:

var btn = document.getElementById("myBtn");
var handler = function(){
    alert(this.id);
};
btn.addEventListener("click", handler, false);
//一些其他的操作
btn.removeEventListener("click", handler, false);

注意,一般都把事件添加在冒泡阶段,这样可以最大限度的兼容浏览器。

4、IE事件处理程序

IE中实现了两个与DOM类似的方法:attachEvent()和detachEvent()。这两个方法接受相同的两个参数,事件处理名称和事件处理函数。由于IE8以前只接受冒泡,所以通过attachEvent添加的函数都是在冒泡阶段被执行。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
    alert("Clicked");
});

attachEvent方法和addEventListener方法的主要区别在于事件处理函数的作用域不同,前者的作用域是全局作用域,也就是说,在函数中使用this是window。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
    alert(this === window); //true
});

在跨浏览器编写代码时,这一点非常重要。
使用attachEvent方法也可以为事件添加多个函数:

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
    alert("Clicked");
});
btn.attachEvent("onclick", function(){
    alert("Hello world!");
});

不过,与DOM程序不同的是,函数出发的次序是后绑定的先触发。
使用attachEvent添加的方法可以通过detachEvent函数移除。同样的,匿名函数添加了就无法移除。

var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
btn.attachEvent("onclick", handler);
// 其他功能代码
btn.detachEvent("onclick", handler);

5、跨浏览器的事件处理函数

为了以跨浏览器的方式处理事件,不少开发人员会使用隔离浏览器差异的JS库,还有一些人会自己开发最适合的浏览器处理方法。其实跨浏览器只要适当的使用能力检测即可。要保证处理的事件在大部分浏览器中能正常运行,只需关注冒泡阶段。
一段跨浏览器的实例代码:

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

// 使用方法
var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler); 
//其他代码
EventUtil.removeHandler(btn, "click", handler);

3、事件对象

在触发DOM某个事件的时候,会产生一个事件对象event,这个对象包含着与事件有关的全部信息。包括导致事件的信息,事件的类型等等。例如,鼠标点击事件的事件对象包含着鼠标点击的位置,而键盘导致的事件产生的事件对象则包括按键的信息。所有浏览器都支持event对象,只是,支持方式不同。

1、DOM中的事件对象

兼容DOM的浏览器,会将一个event对象传入到事件处理程序中,无论指定事件处理程序时使用的什么方法,都会存在event对象。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){ 
    alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
    alert(event.type); //"click" 
}, false);

在通过html特性指定处理程序时,变量event中保存着event对象。

<input type="button" value="Click Me" onclick="alert(event.type)"/>

在需要一个函数处理多个事件的时候,可以使用event.type属性:

var btn = document.getElementById("myBtn");
var handler = function(event){
    switch(event.type){
        case "click":
            alert("Clicked");
            break;
        case "mouseover":
            event.target.style.backgroundColor = "red";
            break;
         case "mouseout":
            event.target.style.backgroundColor = "";
            break;
 } };
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

要阻止特定事件的默认行为,可以使用preventDefault方法。
例如,如下代码可以阻止导航链接:

var link = document.getElementById("myLink");
link.onclick = function(event){
    event.preventDefault();
};

另外,stopPropagation方法用于阻止事件在DOM中的传播。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
   alert("Clicked");
   event.stopPropagation();
};
document.body.onclick = function(event){
   alert("Body clicked");
};

事件对象的eventPhase属性,可以用来确定事件目前处于事件流的哪个阶段。如果是捕获阶段,则该值为1;如果在冒泡阶段,该值为3;如果事件正位于目标上,则该值为2。来看例子:

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.eventPhase); //2
};
document.body.addEventListener("click", function(event){
    alert(event.eventPhase); //1
}, true);
document.body.onclick = function(event){
    alert(event.eventPhase); //3
};

注意,只有在程序处理过程中,event对象才存在,当事件之行完毕,event对象就会被销毁。

2、IE中的事件对象

与访问DOM中的event不同,要访问IE中的event有几种不同的方式,取决于指定事件处理程序的方法。

在使用DOM0级方法添加事件时,event对象作为window的一个属性存在。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
   var event = window.event;
   alert(event.type);     //"click"
};

可是,如果事件是用attachEvent添加的,那么,就会有一个event对象作为参数被传入事件处理程序中。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(event){ 
    alert(event.type); //"click"
});

在使用attachEvent的情况下,其实也可以使用window.event来访问事件对象。

如果是实用html特性指定的事件处理程序,那么,同在DOM中的事件模型相同,也可以通过变量event来访问事件对象:

<input type="button" value="Click Me" onclick="alert(event.type)">

IE的event事件同样也包括与创建它的事件相关的属性和方法:

event.srcElement:事件的目标(等于DOM中的target)
这里,需要注意作用域和this指向谁的问题:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(window.event.srcElement === this);//true
};
btn.attachEvent("onclick", function(event){
    alert(event.srcElement === this);//false
});

returnValue:默认为true,但是设置成false就可以取消事件的默认行为。与DOM中的preventDefault方法相同。

var link = document.getElementById("myLink");
link.onclick = function(){
    window.event.returnValue = false;//阻止链接默认行为
};

cancelBubble:与DOM中的stopPropagation相同。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert("Clicked");
    window.event.cancelBubble = true;
};
document.body.onclick = function(){
    alert("Body clicked");
};

4、跨浏览器的事件对象实例代码:

var EventUtil = {
        addHandler: function(element, type, handler){
            //省略的代码
        },
        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;
            }
        },
        removeHandler: function(element, type, handler){
            //省略的代码
        },
        stopPropagation: function(event){
            if (event.stopPropagation){
                event.stopPropagation();
            } else {
                event.cancelBubble = true;
            } 
        }
};

4、事件类型

事件汇总:
UI事件
焦点事件
鼠标事件
滚轮事件
文本事件
键盘事件
合成事件
变动事件

1、UI事件

ui事件汇总:
load,unload,abort,error,select,resize,scroll
这些事件都被DOM2规定为HTML事件,要确定浏览器是否支持DOM2级规定的HTML事件,可以用如下代码:

var isSupported = document.implementation.hasFeature("HTMLEvents", "2.0");

注意,只有根据DOM2级事件实现这些事件的浏览器才会返回true。

load事件:

方法1:

window.onload = function(event){
    alert("嗨");
};

方法2:

<!DOCTYPE html>
<html>
    <head>
        <title>Load Event Example</title>
    </head>
        <body onload="alert('Loaded!')">
    </body>
</html>

图像上也可以指定load事件,同样有两种方式:

var image1 = document.getElementById("myImg");
image1.onload = function(event){
    alert("picture");
};
 <img src="smile.gif" onload="alert('Image loaded.')">

关于加载图像的其它代码,这里用到了自定义的变量EventUtil:

var EventUtil = {
       addHandler: function(element, type, handler){
        if (type == "load") {
            element.onload = handler;
        }
    },
       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;
           }
    },
       removeHandler: function(element, type, handler){
        //省略的代码
    },
       stopPropagation: function(event){
        if (event.stopPropagation){
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        } 
    }
};

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.gif";
});
//使用Image对象实现
EventUtil.addHandler(window, "load", function(){
    var image = new Image();
    EventUtil.addHandler(image, "load", function(event){
        alert("Image loaded!");
});
    image.src = "smile.gif";
});

注意,新图像不一定是从添加到文档才开始加载,只要为其设置了src元素,图片就会开始加载。
另外,还有一些元素也支持load,例如script元素:

EventUtil.addHandler(window, "load", function(){
    var script = document.createElement("script");
    EventUtil.addHandler(script, "load", function(event){
        alert("Loaded");
    });
    script.src = "example.js";
    document.body.appendChild(script);
});

unload事件:

unload与load事件相互对应,这个事件在文档完全被卸载后触发。只要用户从一个页面切换到另一个页面,这个方法就会被触发。利用该事件,可以及时清除引用,防止内存泄漏。同样,我们可以用两种方式来指定处理事件的函数:

EventUtil.addHandler(window, "unload", function(event){
    alert("Unloaded");
});

<!DOCTYPE html>
<html>
<head>
    <title>Unload Event Example</title>
</head>
<body onunload="alert('Unloaded!')">
</body>
</html>

3、resize事件

当浏览器窗口被调整到一个新的高度或者宽度的时候,就会触发该事件。这个事件在window上触发。

window.onresize = function(){
    alert("change");
}

除了这个方式外,我们也可以通过body元素的onresize属性来指定事件处理程序。
注意,事件处理函数可能在窗口大小变化的时候反复被触发,所以尽量不要写太过复杂的函数,以免浏览器卡顿。

4、scroll事件

跨浏览器写法:

EventUtil.addHandler(window, "scroll", function(event){
    if (document.compatMode == "CSS1Compat"){
        alert(document.documentElement.scrollTop);
    } else {
        alert(document.body.scrollTop);
    }
});

2、焦点事件

焦点事件会在页面元素得到或者失去焦点时触发。利用这个事件,可以知晓用户在页面的行踪。

有以下6个焦点事件:
blur:元素失去焦点时触发,该事件不会冒泡。所有浏览器都支持。
focus:元素获得焦点时触发,这个事件不会冒泡。所有浏览器都支持。
DOMFocusIn、focusin:元素获得焦点时触发,这个事件会冒泡。
DOMFocusOut、focusout:元素失去焦点时触发。

要确定浏览器是否支持这些事件,可以用如下方法:

var isSupported = document.implementation.hasFeature("FocusEvent", "3.0");

鼠标和滚轮事件

click:用户单击主鼠标按钮或者键盘回车时触发。
dblclick:用户双击主鼠标按键时触发。
mousedown:用户按下鼠标任意按钮触发,键盘不能触发该事件。
mouseenter:鼠标光标从外部首次移动到元素内部时触发。事件不冒泡,而且鼠标移动到后代元素上不会触发。
mouseleave:位于元素上方的鼠标光标移动到元素外部后触发,事件不冒泡,而且鼠标移动到后代元素上不会触发。
mousemove:当鼠标光标在元素内部移动时反复触发。
mouseout:鼠标光标位于一个元素上方,当用户将光标移入其它元素时,触发该方法。其它元素可以位于该元素内部或者外部。
mouseover:鼠标指针位于一个元素外部,用户首次将其移入另一个元素边界之内时触发。
mouse:用户释放鼠标时触发。

检测支持度:

var isSupported = document.implementation.hasFeature("MouseEvents", "2.0");
//检测所有
var isSupported = document.implementation.hasFeature("MouseEvent", "3.0")

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、页面坐标位置

该组坐标是从页面本身,而非视口开始计算的。

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

在鼠标没有滚动的情况下,这两组坐标是相等的。

兼容IE8:

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

3、屏幕坐标位置

相对于整个坐标的位置。

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

4、修改键

虽然鼠标事件是靠鼠标来触发的,但是点击鼠标时,键盘上某些案件的状态也能影响到所要采取的操作。这些键是:Shift、Ctrl、Alt、Meta(window或者command),它们经常被用来修改鼠标的行为。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");
    }
    if (event.metaKey){
        keys.push("meta");
    }
    alert("Keys: " + keys.join(","));
});

注意,IE8和以前的版本不支持这四个属性。

5、相关元素

在发生 mouseover mouserout事件时,还会涉及更多的元素。对mouseover而言,事件的主元素就是获得鼠标光标的元素,而相关的元素就是失去光标的元素。类似的mouseout事件而言,主元素就是失去光标的元素,而相关元素就是获得光标的元素。

DOM通过事件对象的relatedTarget提供了相关元素的信息。这个属性只针对事件mouseover mouserout,对于其它事件,这个值就变成了null。IE8之前的版本不支持这个属性,但是有fromElement和toElement保存着相关元素。

var EventUtil = { 
    //省略了其他代码
    getRelatedTarget: function(event){
        if (event.relatedTarget){
             return event.relatedTarget;
        } else if (event.toElement){
            return event.toElement;
        } else if (event.fromElement){
            return event.fromElement;
        } else {
            return null;
        }
    },
    //省略了其他代码
};

6、鼠标按钮

对于 mousedown mouse事件来说,在事件对象中还保存着一个button属性,表示按下或者释放的按钮。该属性可能有三个值:0,表示主鼠标按钮;1,表示中间鼠标按钮(滚轮按钮);2,表示次鼠标按钮。

IE8和以前版本也提供了button属性,但是却和DOM的button有很大差异:数字表示的意义很不相同,大家可以自行百度~

由于单独使用能力检测无法确定差异(两种模型有同名的button属性),因此,我们必须采取别的方法:

var EventUtil = {
    //其它代码
    getButton: function(event){
        if (document.implementation.hasFeature("MouseEvents", "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;
        } 
    }
    //其它代码
},

通过检测MouseEvents这个特性,可以确定事件对象中的button属性中是否包含正确的值。

7、更多事件信息

DOM2级事件规范在event对象中还包含detail属性,用于给出事件更多的相关信息。

8、鼠标滚轮事件

当用户使用鼠标滚轮上下滚动时,就会触发mousewheel事件。不管在什么元素上进行鼠标滚轮,该事件都会冒泡到document或window上。与mousewheel事件对应的event对象中,除了有常规鼠标事件的属性外,还有一个wheelDelta属性。当用户向前滚动时,wheelDelta是120的倍数,当用户向后滚动时,wheelDelta是-120的倍数。

Firefox支持一个名为DOMMouseScroll的类似事件,而有关鼠标滚轮的信息则包含在detail属性中。向前滚动,则是-3倍数,向后滚动,则是+3倍数。

9、触摸设备

在面向触屏手机开发时,需要注意:
不支持dblclick事件。
轻击可单击元素会触发mousemove事件,如果此操作会导致屏幕变化,则没有其它事件会发生;如果屏幕因此没有变化,则会一次发生mousedown mouseup click事件。轻击不可单击的元素不会触发任何事件。
mousemove事件也会触发mouseover mouse out事件。
两个手指放在屏幕上且页面随手指一动会触发mousewheel scroll事件。

4、键盘与文本事件

用户在使用键盘时会触发键盘事件。对键盘事件的支持主要遵循的是DOM0级。
有三个键盘事件:
keydown:当用户按下键盘上的任意按钮时触发,如果用户按住不放,会重复触发该事件。
keypress:当用户按下键盘上的字符键时触发,如果用户按住不放,会重复触发该事件。按下esc也会触发该事件。
keyup:当用户释放键盘上的按键时触发。

只有一个文本事件:textInput。这个事件是对keypress事件的补充,可以在将文本现实给用户以前进行拦截。在文本插入文本框之前,会先触发该事件。

键盘事件和鼠标事件一样,都支持一样的修改键。

1、键码

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

对于数字和字符键,keyCode的值和ASCII中对应的数字或小写字母的编码相同:例如,a的编码是65。与shift的状态无关。

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

非字符的键码大家可以自行百度。

2、字符编码

发生keypress事件,意味着按下的键,会影响到屏幕上的文本显示。
IE9 Firefox Chrome Safari都支持一个charCode属性,这个属性只有在发生keypress事件的时候才有值,并且这个值是按下字符键的ascii码。此时keyCode等于0,或者也可能等于charCode的值。
跨浏览器代码如下:

var EventUtil = {
    //省略其它代码
    getCharCode: function(event){
        if (typeof event.charCode == "number"){
            return event.charCode;
        } else {
            return event.keyCode;
        }
    },
    //省略其它代码
};

5、复合事件

<待续>

5、内存和性能

事件处理程序可以给web应用添加与用户交互的能力,因此,许多开发人员会不分青红皂白的添加许多事件。可是在JS中,添加到页面上的处理程序的数量,会直接影响到页面的整体性能。下面,为大家介绍几种提升性能的方式。

1、事件委托

事件委托利用了事件冒泡,只指定一个事件处理程序,来处理一类事件。例如,click事件会一直冒泡到document,也就是说,我们可以为一个页面只指定一个单击的处理程序。

例如,有下面这样一个html代码:

<ul id="myLinks">
    <li id="goSomewhere">Go somewhere</li>
    <li id="doSomething">Do something</li>
    <li id="sayHi">Say hi</li>
</ul>

// 逐个添加
var item1 = document.getElementById("goSomewhere");
var item2 = document.getElementById("doSomething");
var item3 = document.getElementById("sayHi");

EventUtil.addHandler(item1, "click", function(event){
    location.href = "http://www.wrox.com";
});
EventUtil.addHandler(item2, "click", function(event){
    document.title = "change";
});
EventUtil.addHandler(item3, "click", function(event){
    alert("hi");
});

// 统一添加
var list = document.getElementById("myLinks");
EventUtil.addHandler(list, "click", function(event){
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    switch(target.id){
        case "doSomething":
            document.title = "I changed the document's title";
            break;
        case "goSomewhere":
            location.href = "http://www.wrox.com";
            break;
        case "sayHi": 9 alert("hi");
            break; 
    }
});

这样,我们利用事件委托,只给ul元素添加了一个点击事件的处理程序。

2、移除事件处理程序

将事件处理程序指定给元素时,运行中的浏览器代码和支持页面交互的JS代码就会建立一个连接。这种连接越多,页面执行起来就越慢。因此,再不需要的时候移除那些无用的事件处理程序(称为:空事件处理程序)也很有必要。

<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
        //先执行了某些操作
        document.getElementById("myDiv").innerHTML = "Processing..."; 
        //Oops,这样不好,因为被移除的元素上还绑定着事件呢!
    }; 
</script>

//正确的做法是,移除元素前:
btn.onclick = null;

导致空事件处理程序的原因还有一个就是卸载页面的时候。如果页面被卸载之前,没有清理干净事件处理程序,那么这些程序就会滞留在内存中。

因此,在页面卸载之前,最好用onunload事件处理程序移除所有的事件处理程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值