冒泡那些事
本文正真的标题:阻止子元素事件冒泡触发父元素 mouseover 和 mouseout 的事件函数。
(因为太长,作为标题实在是看不下去。。。)
首先你可以猛戳这里: 问题的出现
我们在父元素上写mouseover 和 mouseout,鼠标在子元素之间移动的时候发生了子元素的mouseover 和 mouseout事件,并冒泡触发了父元素的事件函数。这个时候我们并不希望触发父元素的mouseover 和 mouseout事件。
注:jquery中的mouseover/out事件也有此问题
解决方法一
在ie下有mouseenter 与 mouseleave事件来替代mouseover 和 mouseout。
网上很多说法,这两个事件只有ie支持,其他浏览器不支持。
但是我在最新版本的火狐与谷歌都支持了mouseenter 与 mouseleave!!!!!
另外ie的支持范围是:[ie5+ ,所以我们还是别喷ie了…
其他浏览器测试了下:
在Firefox/3.6.28是不支持mouseenter 与 mouseleave的,Firefox具体从哪个版本开始支持这两个事件,就不得而知了…
在Opera9.50 Alpha 与Opera9.00 Beta都不支持。其实Opera现在完全可以不要测试了,最新版的Opera都是webkit内核...
谷歌低版本未测试…
当然这些老版本浏览器基本可以不用管了,所以这应该是最好的解决办法了:用mouseenter 与 mouseleave事件来替代mouseover 和 mouseout。
此二事件的实例戳这:mouseenter与mouseleave
注:jquery中也有mouseenter 与 mouseleave事件,兼容所有浏览器。
解决方法二
上面那个方法在老版本的火狐与谷歌是不支持的,如果你希望得到最大范围的兼容,那可以继续往下看
我们利用var reltg = e.relatedTarget ? e.relatedTarget : e.type == ‘mouseout’ ? e.toElement : e.fromElement 来获取事件相关元素。再通过这个事件相关元素它跟事件元素的关系(包含的关系),来判断是否做相关事件处理。
对于mouseout事件来说,reltg就是鼠标指针离开目标时,鼠标指针进入的节点。
对于mouseover 事件来说,reltg就是鼠标指针移到目标节点上时所离开的那个节点。
在li的mouseout的事件函数中,如果reltg为li的子元素我们就不要运行相关操作,如果reltg为li的父元素就运行相关操作。
我们可以通过下面的isMouseLeaveOrEnter函数来判断li与reltg的包含关系:
//判断事件相关元素与li的关系 如果事件相关元素为li的子元素就返回false 反之返回true1
2
3
4
5
6
7
8
9
10
11
12
13function isMouseLeaveOrEnter(e, handler) {
if (e.type != 'mouseout' && e.type != 'mouseover') return false;
var reltg = e.relatedTarget ? e.relatedTarget : e.type == 'mouseout' ? e.toElement : e.fromElement;
while (reltg && reltg != handler) reltg = reltg.parentNode;
return (reltg != handler);
};
Li.onmouseout = function(e) {
e = e||window.event;
if (isMouseLeaveOrEnter(e,this)) {
//运行相关操作
};
}
来看看效果:戳我
此方法明显的缺点就是isMouseLeaveOrEnter中要遍历所有的父元素。
解决方法三
此方法与方法二其实思路是一样的,只是我们这里通过compareDocumentPosition/contains来判断li与reltg的包含关系,优化了方法二遍历所有父元素带来的性能问题。
直接看代码吧:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//判断node是否为parent的子元素
//if node == parent 也会返回true
function contains(parent, node) {
if(parent.compareDocumentPosition){ //ff
var _flag = parent.compareDocumentPosition(node);
return (_flag == 20 || _flag == 0)? true : false;
}else if(parent.contains){ //ie
return parent.contains(node);
}
};
Li.onmouseout = function(e) {
e = e||window.event;
var relatedEle = e.relatedTarget ? e.relatedTarget : e.type == 'mouseout' ? e.toElement : e.fromElement
if (!contains(this, relatedEle)) {
show.innerHTML=show.innerHTML+'0';
}
}
看效果:戳我
compareDocumentPosition() 方法比较两个节点,并返回描述它们在文档中位置的整数。
返回值可能是:
1:没有关系,两个节点不属于同一个文档。
2:第一节点(P1)位于第二个节点后(P2)。
4:第一节点(P1)定位在第二节点(P2)前。
8:第一节点(P1)位于第二节点内(P2)。
16:第二节点(P2)位于第一节点内(P1)。
32:没有关系,或是两个节点是同一元素的两个属性。
注释:返回值可以是值的组合。例如,返回 20 意味着在 p2 在 p1 内部(16),并且 p1 在 p2 之前(4)。
而[ie8- 不支持compareDocumentPosition()方法,需要用contains代替compareDocumentPosition()方法那么强大,它是用来确定 nodeB 是否包含在另一个 nodeA 中:nodeA .contains( nodeB )
解决方法四
前面说的方法,并没有像标题所说的那样阻止时间冒泡。都是通过一些方法判断目标元素与父元素的关系,来决定是否要执行事件函数中的相关操作。要利用阻止事件冒泡的方法也能解决此问题。但是要在父元素和所有子元素之间加一个div,来注册mouseover 和 mouseout事件,进而阻止事件冒泡。不推荐此方法,
Comments