事件冒泡
当浏览者在页面上执行某个动作时,页面上实际上有多个元素可以响应该事件,假如单击页面的某个按钮,而该按钮又处于<div.../>
元素之内,则实际上用户既单击了该按钮,也单击了该<div.../>
元素。
Internet Explorer中事件的传递方向是从事件发生的对象开始,然后依次向该对象所在的父节点传递。因为这种传递方式是从下向上传递的,因此这种事件的传递方式也称为冒泡。并不是所有的事件都有冒泡机制,Internet Explorer中有些特殊的事件,例如表单提交、获得焦点等并不会冒泡。
冒泡的事件会沿着父节点一直向上,依次触发多个事件处理函数。但对于非冒泡的事件,则只在特定组件上触发事件处理函数,而不会一直向上触发。
例子
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<title> Internet Explorer事件冒泡机制 </title>
</head>
<body onclick="gotClick('body元素');">
<table onclick="gotClick('table元素');">
<tr onclick="gotClick('tr元素');">
<td onclick="gotClick('td元素');">
<p onclick="gotClick('p元素');">
<input type="button" value="单击我"
onclick="gotClick('按钮');" />
</p>
</td>
</tr>
</table>
<hr/>
<div id="results"></div>
<script type="text/javascript">
var gotClick = function(who)
{
document.getElementById("results").innerHTML
+= who + " 被单击了 <br />";
}
</script>
</body>
</html>
正如前面介绍的,并不是每个事件都可以冒泡。通常支持冒泡的事件有:onactivate、onafterupdate、onbeforeactivate、onbeforecopy、onbeforecut、onbeforedeactivate、onbeforeeditfocus、onbeforepaste、onbeforeupdate、oncellchange、onclick、oncontextmenu、oncontrolselect、oncopy、oncut、ondataavailable、ondatasetchanged、ondatasetcomplete、ondblclick、ondeactivate、ondrag、ondragend、ondragenter、ondragleave、ondragover、ondragstart、ondrop、onerrorupdate、onfocusin、onfocusout、onhelp、onkeydown、onkeypress、onkeyup、onlayoutcomplete、onmousedown、onmouesmove、onmouseout、onmouseover、onmouseup、onmousewheel、onmove、onmoveend、onmovestart、onpaste、onresizeend、onresizestart、onrowenter、onrowsdelete、onrowsinserted、onselectstart等。
如果想阻止冒泡,可修改event对象的cancelBubble属性,该属性的属性值默认是false,也就是说,支持冒泡机制,将其设为true即可。
重定向事件
事件冒泡机制严格从子节点向父节点上溯,将一路触发DOM树中所有的节点,这是Internet Explorer默认的事件触发机制。但在某些时候,我们不想让事件触发严格地按DOM树上溯,而是希望事件在不同节点之间跳跃,此时就可以借助Internet Explorer的事件重定向机制来实现。
Internet Explorer的事件重定向不仅可以将事件重定向到DOM树上的父节点,也可以转发到自己的子节点,甚至转发到根本不在DOM树上的其他节点。
Internet Explorer的事件重定向通过fireEvent方法实现,该方法的语法格式如下。
➢ target.fireEvent(String eventType , Event event):将event事件重定向到target对象。
例子
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<title> new document </title>
</head>
<!-- 整个DOM树都绑定了事件处理函数 -->
<body onclick="gotClick('body元素');">
<table onclick="gotClick('table元素');">
<tr onclick="gotClick('tr元素');">
<td onclick="gotClick('td元素');">
<p onclick="gotClick('p元素');">
<input type="button" value="单击我"
onclick="gotClick('按钮');" />
</p>
</td>
</tr>
</table>
<input id="forward" type="button"
value="被转发事件的按钮"
onclick="gotClick2('被转发的按钮')" />
<hr />
<div id="results"></div>
<script type="text/javascript">
// 事件处理函数
var gotClick = function(who)
{
document.getElementById("results").innerHTML
+= who + " 被单击了 <br />";
// 取消事件的冒泡
event.cancelBubble = true;
// 将事件重定向到id为forward的元素
document.getElementById("forward")
.fireEvent("onClick" , event);
}
// 用于处理“被转发事件的按钮”按钮的事件处理函数
var gotClick2 = function(who)
{
document.getElementById("results").innerHTML +=
who + " 被单击了 <br />";
// 取消事件冒泡
event.cancelBubble = true;
}
</script>
</body>
取消事件默认行为
前面介绍了一种取消事件默认行为的方式:让事件处理函数的返回值为false,即可取消事件的默认行为。Internet Explorer还提供了另一种方式来取消事件的默认行为:将事件对象的returnValue属性设为false。
例子
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=GBK" />
<title> 取消事件默认行为 </title>
</head>
<body>
<!-- 增加了事件处理函数的超链接 -->
<a id="wjc" href="http://www.baidu.com">疯狂Java联盟</a>
<script type="text/javascript">
var clickHandler = function()
{
// 使用确认对话框获取一个布尔值
var ok = confirm('是否转入百度?');
// 修改event的returnValue属性值
event.returnValue = ok;
}
document.getElementById("wjc").onclick=clickHandler;
</script>
</body>
</html>
捕获鼠标事件
在实现某些用户界面时,我们需要处理鼠标拖动事件(例如实现下拉菜单或拖放行为),这就需要某个HTML元素完全捕获鼠标事件,这样可以让事件完全不会发生冒泡。
提示
根据DOM事件模型,事件传播需要先后经过两个阶段:捕获阶段和冒泡阶段,如果捕获阶段已经阻止了事件传播,则事件传播根本不会发生冒泡。Internet Explorer没有完全实现DOM事件模型,所以需要使用特殊方法来捕获鼠标事件。
Internet Explorer事件模型的事件对象提供了如下两个方法来捕获事件、释放捕获。
➢ target.setCapture():设置target对象捕获该事件。
➢ target.releaseCapture():设置target对象释放捕获。
这是所有HTML元素的方法,一旦我们调用某元素的setCapture()方法来捕获事件,该事件将被直接定位到该元素上,直接触发该元素上绑定的事件处理器,而根本不会发生事件冒泡!
这两个方法仅仅对鼠标事件有效,这些鼠标事件包括所有鼠标相关事件,如mousedown、mouseup、mousemove、mouseover、mouseout、click和dblclick等。
当某个元素调用了setCapture()方法之后,在元素上发生的鼠标事件将直接定位到该元素,触发该元素上绑定的事件处理器,直到该元素调用releaseCapture()方法或“鼠标捕获”被中断。浏览器失去焦点、弹出alert()对话框或显示系统菜单都会导致“鼠标捕获”被中断。当该元素的“鼠标捕获”被中断时,该元素会触发“失去捕获”事件,程序可通过onlosecapture属性来监听该事件。
例子
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<title> 拖放效果 </title>
</head>
<body>
<!-- 导入JavaScript脚本文件 -->
<script src="drag.js"></script>
<!-- 定义被拖放的元素 -->
<div style="position:absolute;
left:120px;
top:150px;
width:250px;
border:1px solid black;">
<div style="background-color:#416ea5;
width:250px;
height:22px;
cursor:move;
font-weight:bold;
border-bottom:1px solid black;"
onmousedown="drag(this.parentNode, event);">
可拖动标题
</div>
<p>可被拖动的窗口</p>
<p>窗口内容</p>
</div>
<!-- 定义一个可拖动的图片 -->
<img src="image/logo.jpg" alt="按住Shift可拖动"
style="position:absolute;"
onmousedown="if (event.shiftKey) drag(this, event);" />
</body>
</html>
drag.js
var drag = function(target, event)
{
// 定义开始拖动时的鼠标位置(相对window座标)
var startX = event.clientX;
var startY = event.clientY;
// 定义将要被拖动元素的位置(相对于document座标)
// 因为该target的position为absolutely,
// 所以认为它基于的座标系是document
var origX = target.offsetLeft;
var origY = target.offsetTop;
// 因为后面根据event的clientX、clientY来获取鼠标位置时,
// 只能获取windows座标系的位置,所以需要计算window座标系
// 和document座标系的偏移。
// 计算windows座标系和document座标系之间的偏移
var deltaX = startX - origX;
var deltaY = startY - origY;
// 设置该元素直接捕获该事件
target.setCapture();
// 鼠标移动的事件处理器
var moveHandler = function()
{
// 对于IE事件模型,获取事件对象
var evt = window.event;
// 将被拖动元素的位置移动到当前鼠标位置。
// 先将window座标系位置转换成document座标系位置,再修改目标对象的CSS位置。
target.style.left = (evt.clientX - deltaX) + "px";
target.style.top = (evt.clientY - deltaY) + "px";
// 阻止事件冒泡
evt.cancelBubble=true;
}
// 鼠标松开的事件处理器
var upHandler = function()
{
// 对于IE事件模型,获取事件对象
var evt = window.event;
// 取消该对象上绑定的事件处理器
target.detachEvent("onlosecapture", upHandler);
target.detachEvent("onmouseup", upHandler);
target.detachEvent("onmousemove", moveHandler);
// 释放该对象的“事件捕获”
target.releaseCapture();
// 阻止事件冒泡
evt.cancelBubble=true;
}
// 为该元素鼠标移动时绑定事件处理器
target.attachEvent("onmousemove", moveHandler);
// 为鼠标松开时绑定事件处理器
target.attachEvent("onmouseup", upHandler);
// 将失去捕获事件当成鼠标松开处理。
target.attachEvent("onlosecapture", upHandler);
// 阻止事件冒泡
event.cancelBubble=true;
event.returnValue = false;
}
提示
此处介绍的拖放效果仅在Internet Explorer中有效,该页面大量使用了Internet Explorer特有的事件模型,包括访问Internet Explorer事件对象、取消事件默认行为、阻止事件传播等。如果需要考虑跨浏览器,则必须先判断客户浏览器,再调用相应代码。
注:本博客内容节选自李刚编著的疯狂HTML 5/CSS 3/JavaScript讲义 ,详细内容请参阅书籍。