首页 » 编写高质量代码:改善JavaScript程序的188个建议 » 编写高质量代码:改善JavaScript程序的188个建议全文在线阅读

《编写高质量代码:改善JavaScript程序的188个建议》建议118:使用DOM树结构托管事件

关灯直达底部

当页面中存在大量元素,并且每个元素有一个或多个事件句柄连接(如onclick)时,可能会影响性能。连接每个句柄都是有代价的,这代价可能是加重了页面负担(更多的页面标记和JavaScript代码),也可能表现在运行期的运行时间上。要访问和修改更多的DOM节点,程序就会更慢,特别是事件连接过程都发生在onload(或DOMContentReady)事件中时,对任何一个富交互网页来说这都是一个繁忙的时间段。连接事件占用了处理时间,另外,浏览器需要保存每个句柄的记录,也会占用更多内存。当这些工作结束时,由于这些事件句柄中的相当一部分根本不需要(因为并不是百分之百的按钮或链接都会被用户单击到),所以很多工作都是不必要的。

一个简单而优雅的处理DOM事件的技术是事件托管。它基于这样一个事实:事件逐层冒泡总能被父元素捕获。采用事件托管技术后,只需要在一个包装元素上连接一个句柄,用于处理子元素发生的所有事件。根据DOM标准,每个事件有3个阶段:

❑捕获

❑到达目标

❑冒泡

IE不支持捕获,只要实现托管技术使用冒泡就足够了。


<p>

<ul>

<li><a urn="#1"></a></li>

</ul>

</p>


例如,对于上面的结构,当用户单击“menu#1”链接时,单击事件首先被<a>元素收到,然后它沿着DOM树冒泡,被<li>元素收到,之后被<ul>元素收到,接着是<p>等,一直到达文档的顶层,甚至window对象。这使得可以只在父元素上连接一个事件句柄,以接收所有子元素产生的事件通知。

假设要为上面文档结构提供一个逐步增强的Ajax体验,用户关闭了JavaScript,菜单中的链接仍然可以正常地重载页面。当已经打开JavaScript且用户代理有足够能力时,如果希望截获所有单击,阻止默认行为(转入链接),发送一个Ajax请求获取内容,然后不刷新页面就能够更新部分页面,那么使用事件托管实现此功能,可以在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;

}

};


事件托管技术并不复杂,只需要通过监听事件侦测事件是不是从目标元素中发出的。这里有一些冗余的跨浏览器代码,如果将它们移入一个可重用的库中,代码就变得相当干净。跨浏览器部分包括:

❑访问事件对象,并判断事件源(目标)。

❑结束文档树上的冒泡(可选)。

❑阻止默认动作(可选,在本示例中是必须的,因为任务是捕获这些链接而不转入这些链接)。