Henry Sun

没有所谓的偶然,一切皆是因果

博客园 首页 新随笔 管理

 事件传播

    为了说明不可单击的页面元素处理单击事件的能力,例如样式转换器中包含按钮的div元素或者兄弟元素h3,我们来实现一个鼠标指针进入元素和离开元素时的效果,首先需要添加一种翻转状态,表明能与鼠标进行某种交互,然后我们.hover()方法给h3元素添加这个样式,伪代码如下:

<style>
.hover { 
cursor: pointer; 
background-color: #afa; 
}
</style
<script>
$(document).ready(function() { 
$('#switcher h3').hover(function() { 
$(this).addClass('hover'); 
}, function() { 
$(this).removeClass('hover'); 
}); 
});
</sctipt>

<body>
...
</body>
代码

      注:.hover()方法接受两个函数参数。第一个函数会在鼠标指针进入被选择的元素时执行,而第二个函数会在鼠标指针离开该元素时触发

  • 事件的旅程

     当页面上发生一个事件时,每个层次上的DOM元素都有机会处理这个事件。以下面的页面模型为例:

 1 <div class="foo"> 
 2 <span class="bar"> 
 3 <a href="http://www.example.com/"> 
 4 The quick brown fox jumps over the lazy dog. 
 5 </a> 
 6 </span> 
 7 <p> 
 8 How razorback-jumping frogs can level six piqued gymnasts! 
 9 </p> 
10 </div>

允许多个元素响应单击事件的一种策略叫做事件捕获。在事件捕获的过程中,事件首先会交给最外层的元素,接着再交给更具体的元素。在这个例子中,意味着单击事件首先会传递给<div>,然后是<span>,最后是<a>。另一种相反的策略叫做事件冒泡。即当事件发生时,会首先发送给最具体的元素,在这个元素获得响应机会之后,事件会向上冒泡到更一般的元素。在我们的例子中,<a>会首先处理事件,然后按照顺序依次是<span>和<div>

  • 事件冒泡的副作用

     假设在我们的例子中,为<div>添加了一个mouseout事件处理程序。当用户的鼠标指针退出这个<div>时,会按照预期运行mouseout处理程序。因为这个过程发生在顶层元素上,所以其他元素不会取得这个事件。但是,当指针从<a>元素上离开时,<a>元素也会取得一个mouseout事件。然后,这个事件会向上冒泡到<span>和<div>,从而触发上述的事件处理程序。这种冒泡序列很可能不是我们所希望的

 通过事件对象改变事件的旅程

       假设我们希望增大触发样式转换器折叠或扩展的可单击区域。一种方案就是将事件处理程序从标签移至包含它的<div>元素

$(document).ready(function() { 
$('#switcher').click(function() { 
$('#switcher button').toggleClass('hidden'); 
}); 
});
JS

完整代码如下:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <style type="text/css">
        body.large {
            font-size: 1.5em;
        }

        body.narrow {
            width: 250px;
        }

        .selected {
            font-weight: bold;
        }


        .hover {
            cursor: pointer;
            background-color: #afa;
        }

        .hidden {
         display:none;
        }

    </style>
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script src="Scripts/jquery-1.8.2.min.js"></script>
    <script>

      

        $(document).ready(function () {
            $('#switcher-default').addClass('selected');
            $('#switcher button').on('click', function () {
                    var bodyClass = this.id.split('-')[1];
                    var bodyClass = this.id.split('-')[1];
                    $('body').removeClass().addClass(bodyClass);
                    $('#switcher button').removeClass('selected');
                    $(this).addClass('selected');                    
                    //eventobj.stopPropagation(); //阻止事件冒泡       
                //return false;  //也可以阻止事件冒泡,也可以阻止默认事件
            });
        });

      
        $(document).ready(function () {
            $('#switcher h3').hover(function () {
                $(this).addClass('hover');
            }, function () {
                $(this).removeClass('hover');
            });
        });

        $(document).ready(function () {
            $('#switcher').click(function () {
                $('#switcher button').toggleClass('hidden');
            });
        });

    </script>
</head>
<body>
    <div id="switcher" class="switcher">
        <h3>样式转换器</h3>
        <button id="switcher-default">
            默认视图
        </button>
        <button id="switcher-narrow">
            窄列视图
        </button>
        <button id="switcher-large">
            大字视图
        </button>
    </div>
    <h2>Shakespeare's Plays</h2>

    <table>
        <tr>
            <td>As You Like It</td>
            <td>Comedy</td>
            <td></td>
        </tr>
        <tr>
            <td>All's Well that Ends Well</td>
            <td>Comedy</td>
            <td>1601</td>
        </tr>
        <tr>
            <td>Hamlet</td>
            <td>Tragedy</td>
            <td>1604</td>
        </tr>
        <tr>
            <td>Macbeth</td>
            <td>Tragedy</td>
            <td>1606</td>
        </tr>
        <tr>
            <td>Romeo and Juliet</td>
            <td>Tragedy</td>
            <td>1595</td>
        </tr>
        <tr>
            <td>Henry IV, Part I</td>
            <td>History</td>
            <td>1596</td>
        </tr>
        <tr>
            <td>Henry V</td>
            <td>History</td>
            <td>1599</td>
        </tr>
    </table>
    <h2>Shakespeare's Sonnets</h2>
    <table>
        <tr>
            <td>The Fair Youth</td>
            <td>1–126</td>
        </tr>
        <tr>
            <td>The Dark Lady</td>
            <td>127–152</td>
        </tr>
        <tr>
            <td>The Rival Poet</td>
            <td>78–86</td>
        </tr>
    </table>
</body>
</html>
完整代码

  这种改变会使样式转换器的整个区域都可以通过单击切换其可见性。但时也造成了一个问题,即单击按钮会在修改内容区的样式之后折叠样式转换器。导致这个问题的原因就是事件冒泡,即事件首先被按钮处理,然后又沿着DOM树向上传递,直至到达<div id="switcher">激活事件处理程序并隐藏按钮。要解决这个问题,必须访问事件对象

        注:事件对象是一种DOM结构,它会在元素获得处理事件的机会时传递给被调用的事件处理程序。这个对象中包含着与事件有关的信息(例如事件发生时的鼠标指针位置),也提供了可以用来影响事件在DOM中传递进程的一些方法

  • 事件目标         

         变量event保存着事件对象。而event.target属性保存着发生事件的目标元素。通过.target,可以确定DOM中首先接收到事件的元素(即实际被单击的元素)。而且,我们知道this引用的是处理事件的DOM元素,修改上面的代码可以得到如下代码

$(document).ready(function() { 
$('#switcher').click(function(event) { 
if (event.target == this) { 
$('#switcher button').toggleClass('hidden'); 
} 
}); 
});
代码 

         

    此时的代码确保了被单击的元素是<div id="switcher">,而不是其他后代元素。现在,单击按钮不会再折叠样式转换器,而单击转换器背景区则会触发折叠操作。但是,单击标签(<h3>)同样什么也不会发生,因为它也是一个后代元素

  • 停止事件传播

     删除刚才的event.target==this,我们来给按钮的事件处理添加一个事件对象并调用event.stopPropagation()方法,这样可以阻止按钮的事件冒泡行为,这样一来,单击按钮的事件会被按钮处理,而且只会被按钮处理。单击样式转换器的其他地方则可以折叠和扩展整个区域,代码如下:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <style type="text/css">
        body.large {
            font-size: 1.5em;
        }

        body.narrow {
            width: 250px;
        }

        .selected {
            font-weight: bold;
        }


        .hover {
            cursor: pointer;
            background-color: #afa;
        }

        .hidden {
         display:none;
        }

    </style>
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script src="Scripts/jquery-1.8.2.min.js"></script>
    <script>

      

        $(document).ready(function () {
            $('#switcher-default').addClass('selected');
            $('#switcher button').on('click', function (event) {
                    var bodyClass = this.id.split('-')[1];
                    var bodyClass = this.id.split('-')[1];
                    $('body').removeClass().addClass(bodyClass);
                    $('#switcher button').removeClass('selected');
                    $(this).addClass('selected');                    
                    event.stopPropagation(); //阻止事件冒泡       
                //return false;  //也可以阻止事件冒泡,也可以阻止默认事件
            });
        });

      
        $(document).ready(function () {
            $('#switcher h3').hover(function () {
                $(this).addClass('hover');
            }, function () {
                $(this).removeClass('hover');
            });
        });

        $(document).ready(function () {
            $('#switcher').click(function (event) {                
                $('#switcher button').toggleClass('hidden');
            });
        });

    </script>
</head>
<body>
    <div id="switcher" class="switcher">
        <h3>样式转换器</h3>
        <button id="switcher-default">
            默认视图
        </button>
        <button id="switcher-narrow">
            窄列视图
        </button>
        <button id="switcher-large">
            大字视图
        </button>
    </div>
    <h2>Shakespeare's Plays</h2>

    <table>
        <tr>
            <td>As You Like It</td>
            <td>Comedy</td>
            <td></td>
        </tr>
        <tr>
            <td>All's Well that Ends Well</td>
            <td>Comedy</td>
            <td>1601</td>
        </tr>
        <tr>
            <td>Hamlet</td>
            <td>Tragedy</td>
            <td>1604</td>
        </tr>
        <tr>
            <td>Macbeth</td>
            <td>Tragedy</td>
            <td>1606</td>
        </tr>
        <tr>
            <td>Romeo and Juliet</td>
            <td>Tragedy</td>
            <td>1595</td>
        </tr>
        <tr>
            <td>Henry IV, Part I</td>
            <td>History</td>
            <td>1596</td>
        </tr>
        <tr>
            <td>Henry V</td>
            <td>History</td>
            <td>1599</td>
        </tr>
    </table>
    <h2>Shakespeare's Sonnets</h2>
    <table>
        <tr>
            <td>The Fair Youth</td>
            <td>1–126</td>
        </tr>
        <tr>
            <td>The Dark Lady</td>
            <td>127–152</td>
        </tr>
        <tr>
            <td>The Rival Poet</td>
            <td>78–86</td>
        </tr>
    </table>
</body>
</html>
代码
  • 阻止事件默认行为

     对于一个锚元素(<a>),当用户单击时浏览器会加载一个新页面,这种行为与事件处理程序不是同一个概念,它是单击锚元素的默认操作。即便在事件对象上调用.stopPropagation()方法也不能禁止这种默认操作,因为默认操作不是在正常的事件传播流中发生的。在这种情况下,.preventDefault()方法则可以阻止事件的默认行为。另外,如果想要同时停止事件传播和默认操作,可以在事件处理程序中返回false,代码如下:

 $(document).ready(function () {
            $('#switcher-default').addClass('selected');
            $('#switcher button').on('click', function (event) {
                    var bodyClass = this.id.split('-')[1];
                    var bodyClass = this.id.split('-')[1];
                    $('body').removeClass().addClass(bodyClass);
                    $('#switcher button').removeClass('selected');
                    $(this).addClass('selected');                    
                //event.stopPropagation(); //阻止事件冒泡      
                    //event.preventDefault(); //阻止默认事件和事件冒泡
                    return false;  //也可以阻止事件冒泡,也可以阻止默认事件
            });
        });
代码

 事件委托

    事件冒泡也有好处,通过事件冒泡我们可以用很少的代码为元素注册重复的事件,例如,一个table里有很多td,如果为每个td都注册一个hover事件,不仅需要遍历table的子节点,而且严重降低了性能,通过事件委托可以这样写

$("table").delegate("td", "hover", function(){
$(this).toggleClass("hover"); });
  将每个单元格的hover事件委托给了table元素,每当单元格td触发hover事件是就会执行元素table绑定的函数fn
posted on 2015-04-21 16:53  Sam Flynn  阅读(199)  评论(2编辑  收藏  举报