简要提纲
- addEventListener,the 3rd parameter,true: parent to child,false: child to parent
- onMouseOver/Out,与 child 节点交互时,先 Out 再 Over
- style.display=none 的 Element,在某些情况下用 document.getElementById() 获取不到
Part 1
在支持 DOM2 事件模型的浏览器上,给元素绑定事件要用 addEventListener 这个方法,他有第 3 个参数:capturePhase。手册上的说明没太看懂,就自己做了个测试(在 http://leakon.googlecode.com/svn/trunk/leakon/javascript/event_phase 可以看到代码),明白了 true 与 false 的区别:当已注册的事件在某个节点上发生时,会按照某个顺序传递下去。true 指定由根节点向叶子节点传递,false 指定由叶子节点向根节点传递。
举个例子,有个 div,内部有个 img,2 个元素都注册了 onclick 事件,分别调用不同的处理函数。当你在图片上点击鼠标时,div 和 img 哪个先触发,就靠这个 capturePhase 来决定。div 是 img 的 parent,如果设置为 true,就是 div 先触发,然后才是 img。
不过一般来说都是设置为 false,即由叶子向根传递,因为 IE 就是这样做的,而且貌似不能改变顺序。
Part 2
还是父子节点事件处理的问题,这回讨论 onMouseOver 和 onMouseOut 事件。借用上面的例子,div 内部是 img。我们只给 div 注册一对 onMouseOver/Out ,当光标从 div 部分(这里假设 div 很大,而 img 只占 div 较少一部分面积)经过 img 的边框进入 img 面积内时,会先后触发 div 的 2 个事件:onMouseOut -> onMouseOver。也就是,div 认为鼠标是先离开 div 然后又立刻进入。
换到一个实际的应用场景,比如我们添加 Over 和 Out 事件就是为了给 div 添加一个高亮的样式表,div 背景默认是白色,鼠标划过变成红色,出去后恢复成白色。
要是你在 Over 和 Out 处理函数内部仅仅是 addClass 和 removeClass 这么一下,那你会看到跳动的感觉,也就是鼠标按如下路线行进:inDiv -> inImg -> OutOfImg -> inDiv ,别看鼠标一直在 div 内部,你以为不会有 onMouseOut 事件,事实不是这样,光标进入 img 的那一瞬间,就先后执行了 Out 和 Over 函数。而且,有些情况下,后续的 Over 函数没能得到成功执行,现实是 div 的样式表移除后就没能再添加上。
Part 3
这个问题大家应该早就知道,只是我今天才发现。我以为 document.getElementById() 跟 DOM 节点当前的样式无关。今天给一个 Element 注册事件的时候发现找不到该节点,原因是那个节点的 display 样式设置为 none。
按说不应该是这样,可能跟当时运行的环境有关,也可能是我绑定事件的函数有问题,总之看到的现象是跟样式定义有关。总结在这里,以备日后回忆,参考。
我现在觉得 Javascript 最难的就是事件处理这部分了。这里的事件不是简单地给某个 div 绑定一个函数来响应鼠标点击,而是一整套的处理流程和逻辑。
事件发生源、事件类型、传递方向、再哪个节点上应该停止传递,等等。
当页面中元素很多,关系错综复杂时,就需要一个事件处理框架来帮忙。我们需要的是结构清晰逻辑合理的代码,而不是满篇重复地 if else 判断。
举个简单的例子,如果你做一个邮件系统,类似 Gmail 那样的,你每点一封未读邮件,或者给某些邮件加上 tag,都会触发一系列的关联事件,例如要把未读邮件总数减少,给 tag 列表里添加新项目,同时用突出的颜色高亮选中的邮件标题,还要在页面顶部给出处理成功的提示,甚至还要问用户是否要撤销刚才的一系列动作。
天啊,看这 2 行文字描述我就觉得很晕了,把他用代码实现出来,并处理各种异常,还要保证代码的可读性,我只能说我自己能力还差得太远太远。
前两天听说 MooTools 这个 Javascript 框架在面向对象方面做的不错,当项目越来越复杂时,按照 MooTools 方式来组织代码可以有很好的可维护性。
找到了电子书,已经放到 blog 中可以下载了。
IE 的一个特点
当你对一个 Element 的一个事件,比如 onClick 多次绑定了同一个处理函数时,每次你 click 这个 Element,绑定的函数也会执行多次。
Firefox 等现代浏览器就没有这个问题。
你只理解对了一半, addEventListener 的第三个参数用来指定事件处理函数在捕获时执行还是在冒泡时执行,DOM2的事件模型分为两个时期,第三个参数就是用于设置这个执行顺序的。