2013年4月24日

JavaScript之事件托管


当页面中存在大量元素,而且每个元素有一个或多个事件句柄与之挂接(例如onclick)时,可能会影响性能。连接每个句柄都是有代价的,无论其形 式是加重了页面负担(更多的页面标记和JavaScript 代码)还是表现在运行期的运行时间上。你需要访问和修改更多的DOM 节点,程序就会更慢,特别是因为事件挂接过程都发生在onload(或DOMContentReady)事件中,对任何一个富交互网页来说那都是一个繁忙 的时间段。挂接事件占用了处理时间,另外,浏览器需要保存每个句柄的记录,占用更多内存。当这些工作结束时,这些事件句柄中的相当一部分根本不需要(因为 并不是100%的按钮或者链接都会被用户点到),所以很多工作都是不必要的。
一个简单而优雅的处理DOM 事件的技术是事件托管。它基于这样一个事实:事件逐层冒泡总能被父元素捕获。采用事件托管技术之后,你只需要在一个包装元素上挂接一个句柄,用于处理子元素发生的所有事件。
根据DOM 标准,每个事件有三个阶段:捕获;到达目标;冒泡IE不支持捕获,但实现托管技术使用冒泡就足够了。

<html>
<head>
<title>事件冒泡</title>
</head>
<body>
<div>
<ul>
<li><a href=”#”>事件冒泡</a></li>
</ul>
<div>
<body>
<html>
当用户点击了“事件冒泡”链接,点击事件首先被<a>元素收到。然后它沿着DOM 树冒泡,被<li>元素收到,然后是<ul>,接着是<div>,等等,一直到达文档的顶层,甚至window。这 使得你可以只在父元素上挂接一个事件句柄,来接收所有子元素产生的事件通知。
假设你要为图中所显示的文档提供一个逐步增强的Ajax 体验。如果用户关闭了JavaScript,菜单中的链接仍然可以正常地重载页面。但是如果JavaScript 打开而且用户代理有足够能力,你希望截获所有点击,阻止默认行为(转入链接),发送一个Ajax 请求获取内容,然后不刷新页面就能够更新部分页面。使用事件托管实现此功能,你可以在UL”menu”单元挂接一个点击监听器,它封装所有链接并监听所有 click 事件,看看他们是否发自一个链接。
document.getElementById(‘menu’).onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
var pageid, hrefparts;
if (target.nodeName !== ‘A’) {
return;
}
hrefparts = target.href.split(‘/’);
pageid = hrefparts[hrefparts.length – 1];
pageid = pageid.replace(‘.html’, ”);
ajaxRequest(‘xhr.php?page=’ + id, updatePageContents);
if (typeof e.preventDefault === ‘function’) {
e.preventDefault();
e.stopPropagation();
} else {
e.returnValue = false;
e.cancelBubble = true;
}
};
正如你所看到的那样,事件托管技术并不复杂;你只需要监听事件,看看他们是不是从你感兴趣的元素中发出的。这里有一些冗余的跨浏览器代码,如果你将它们移入一个可重用的库中,代码就变得相当干净。
跨浏览器部分包括:访问事件对象,并判断事件源(目标),结束文档树上的冒泡(可选),阻止默认动作(可选,但此例中是必须的,因为任务是捕获链接而不转入这些链接)。

标签:,

微信扫一扫二维码访问


16年前端经验
加微信好友直接沟通
了解《我的十年》