CONAN ZONE

你越挣扎我就越兴奋

BlogJava 首页 新随笔 联系 聚合 管理
  0 Posts :: 282 Stories :: 0 Comments :: 0 Trackbacks
众所周知, 浏览器中内存泄露以及内存无法回收(两者不是一回事,很多人都把他们弄混淆了),常常是由于对dom元素注册事件不当引起的.

通常的解决方案是, 自行实现一套 添加事件, 移除事件 以及删除dom元素的机制.
为dom元素添加事件时, 同时记录 这个事件 以及对应的函数,
在删除dom元素时, 先移除dom元素上已经添加的事件 再删除dom元素本身.

而当页面中添加了事件监听的dom元素很多时, 移除元素变得很麻烦.
例如 一个div 里面有个form form里有多个元素都添加了事件.
那么移除这个div时, 就要先去移除他下面每一个元素上的事件 然后再移除这个div.

这种做法很多时候是必须的, 而且自己写一个"深度遍历子节点,并移除其事件"的函数也并不是很困难.

但是 在很多时候 这种做法是可以避免的, 避免的方法就是, 把事件监听注册到更上层的dom元素中.
并且在事件函数中 通过 event.target/event.srcElement 来 事件发生在哪个元素上,然后来执行相关的方法.
这样 在移除元素时 只要移除这个元素以及它上面的事件 就可以了, 而不必执行(或者少量的执行)"移除所有子节点事件"的动作了.


见下面的例子 :

Html代码 复制代码
  1.  <table width="300" border="1"  onclick="showDetail(event)">    
  2.  <tr>  
  3. <td>1</td>  
  4. <td>Tom</td>  
  5. <td><input type="button" value="详细信息" userid="1" /></td>  
  6.  </tr>  
  7.  <tr>  
  8. <td>2</td>  
  9. <td>Kate</td>  
  10. <td><input type="button" value="详细信息" userid="2"  /></td>  
  11.  </tr>  
  12.  <tr>  
  13. <td>3</td>  
  14. <td>John</td>  
  15. <td><input type="button" value="详细信息"  userid="3" /></td>  
  16.  </tr>  
  17.  </table>  


在这个例子中, 实际上事件只是注册在table上, 而没有在"input type="button"上.

"showDetail" 可以这样写

Javascript代码 复制代码
  1. function showDetail_b(event) {   
  2.     event=event||window.event;   
  3.     var target=event.target||event.srcElement;   
  4.     if ( String(target.tagName).toLowerCase()=='input' &&  target.value=="详细信息") {   
  5.         showUserDetail(target.getAttribute('userid') );   
  6.     }   
  7. }  



当然 这种做法不是绝对的, 有时候这么做很可能让代码变得臃肿冗长.
到底是否使用"事件上提"的做法 要根据实际情况来选择.
不过 根据我的以往经验, 在列表(table)中, 使用这种技术非常合适.
因为 列表有着"行与行之间模型一致"(只是数据不一致,结构一致)的特点.

例如,下面的效果, 都可以通过在 table上注册事件来实现:

1 点击行, 行变色 (不必在 tr 上注册点击事件)
2 点击行中的某个按钮 (不必在 tr 里的 button 上注册点击事件)
3 鼠标经过行时 行变色 (不必在 tr上注册 mouseover/mouseout 事件, 而是可以在table上注册mousemove事件)
4 还有关于单元格的 很多效果.....

当然,在非列表里 这种做法也有很多的用武之地.
总之 合理的利用"事件上提"的方法, 可以增强dom元素和事件的可控性, 有效的防止内存泄露和内存无法回收的情况.
posted on 2008-07-23 23:03 CONAN 阅读(187) 评论(0)  编辑  收藏 所属分类: JS