事件冒泡

  当浏览者在页面上执行某个动作时,页面上实际上有多个元素可以响应该事件,假如单击页面的某个按钮,而该按钮又处于<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讲义 ,详细内容请参阅书籍。