js事件的命名空间的详解

Posted simon麦田

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js事件的命名空间的详解相关的知识,希望对你有一定的参考价值。

转自:http://www.cnblogs.com/lyzg/p/5347857.html


阅读目录

jquery现在的事件API:on,off,trigger支持带命名空间的事件,当事件有了命名空间,就可以有效地管理同一事件的不同监听器,在定义组件的时候,能够避免同一元素应用到不同组件时,同一事件类型之间的影响,还能控制一些意外的事件冒泡。在实际工作中,相信大家都用的很多,但是不一定了解它的所有细节,至少我有这样的经验,经常在碰到疑惑的时候,还得重新写例子去验证它的相关作用,所以本文想把事件命名空间相关的细节都梳理出来,将来再犯迷糊的时候可以回来翻着看看以便加深对它的理解和运用。

在详细了解命名空间之前,得先认识下什么是自定义事件,因为命名空间可以同时应用于自定义事件和浏览器默认事件当中。

1. 自定义事件

我们在定义组件的时候,浏览器的默认事件往往不能满足我们的要求,比如我们写了一个树形组件,它有一个实例方法init用来完成这个组件的初始化工作,在这个方法调用结束之后,我们通常会自定义一个init事件,以便外部可以在树组件初始化完成之后做一些回调处理:

- Hide code
<script src="../js/lib/jquery.js"></script>
<div id="tree">

</div>
<script>
    var Tree = function(element, options) 
        var $tree = this.$tree = $(element);
        //监听init事件,触发
        $tree.on('init', $.proxy(options.onInit, this));
        this.init();
    ;

    Tree.prototype.init = function() 
        console.log('tree init!');
        this.$tree.trigger('init');
    ;

    var tree = new Tree('#tree', 
        onInit: function() 
            console.log(this.$tree.outerHeight());
        
    );
</script>

以上代码中.on('init',…)中的init就是一个类似click这样的自定义事件,该代码运行结果如下

 
自定义事件的使用就跟浏览器默认事件的使用没有任何区别,就连事件冒泡和阻止事件默认行为都完全支持,唯一的区别在于:浏览器自带的事件类型可以通过浏览器的UI线程去触发,而自定义事件必须通过代码来手动触发: 

2. 事件命名空间

事件命名空间类似css的类,我们在事件类型的后面通过点加名称的方式来给事件添加命名空间:

- Hide code
<script>
    var Tree = function(element, options) 
        var $tree = this.$tree = $(element);
        //监听init事件,触发
        $tree.on('init.my.tree', $.proxy(options.onInit, this));
        this.init();
    ;

    Tree.prototype.init = function() 
        console.log('tree init!');
        this.$tree.trigger('init.my.tree');
    ;

    var tree = new Tree('#tree', 
        onInit: function() 
            console.log(this.$tree.outerHeight());
        
    );
</script>

以上代码中.on('init.my.tree',…)通过.my和.tree给init这个事件添加了2个命名空间,注意命名空间是类似css的类,而不是类似java中的package,所以这两个命名空间的名称分别是.my和.tree,而不是my和my.tree,注意命名空间的名称前面一定要带点,这个名称在off的时候可以用到。在监听和触发事件的时候带上命名空间,当触发带命名空间的事件时,只会调用匹配该命名空间的监听器。所以命名空间可以有效地管理同一事件的不同监听器,尤其在定义组件的时候可以有效地保证组件内部的事件只在组件内部有效,不会影响到其它组件。

现在假设我们不用命名空间,同时定义两个组件Tree和Dragable,并且同时对#tree这个元素做实例化,以便实现一棵可以拖动的树:

- Hide code
<script>
    var Tree = function(element, options) 
        var $tree = this.$tree = $(element);
        //监听init事件,触发
        $tree.on('init', $.proxy(options.onInit, this));
        this.init();
    ;

    Tree.prototype.init = function() 
        console.log('tree init!');
        this.$tree.trigger('init');
    ;

    var tree = new Tree('#tree', 
        onInit: function() 
            console.log(this.$tree.outerHeight());
        
    );

    var Dragable = function(element, options) 
        var $element = this.$element = $(element);
        //监听init事件,触发
        $element.on('init', $.proxy(options.onInit, this));
        this.init();
    ;

    Dragable.prototype.init = function() 
        console.log('tree init!');
        this.$element.trigger('init');
    ;

    var drag = new Dragable('#tree', 
        onInit: function() 
            console.log('start drag!');
        
    );
</script>

结果会发现Tree的onInit回调被调用两次: 
 
根本原因就是因为#tree这个元素被应用到了多个组件,在这两个组件内部对#tree这个元素定义了同一个名称的事件,所以后面实例化的组件在触发该事件的时候也会导致前面实例化的组件的同一事件再次被触发。通过命名空间就可以避免这个问题,让组件各自的事件回调互不影响:

+ View code

这样tree实例的onInit就不会被调用2次了:

3. 命名空间的匹配规则

在第2部分的举例当中,触发带命名空间的事件时,触发方式是: 
 
然后就会调用这里监听的回调: 
 
如果把触发方式改一下,不改监听方式,改成以下三种方式的一种,结果会怎么样呢:

- Hide code
this.$element.trigger('init');
this.$element.trigger('init.dragable');
this.$element.trigger('init.my');

答案是该监听回调依然会被调用。这个跟命名空间的匹配规则有关,为了说明这个规则,可以用以下的这个代码来测试:

- Hide code
<!DOCTYPE html>
<html lang="en">
<head>
    <title>xxxxx</title>
    <style type="text/css">
        #parent 
            margin: 100px auto 0 auto;
            width: 600px;
            height: 200px;
            border: 1px solid #ccc;
            position: relative;
        

        .log 
            position: absolute;
            left: 0;
            width: 100%;
            height: 100%;
            overflow: auto;
        

        p 
            margin: 0;
        

        #btns 
            margin: 20px auto 0 auto;
            width: 600px;
        
    </style>
</head>
<body>
<script src="../js/lib/jquery.js"></script>
<div id="parent">
    <div class="log"></div>
</div>
<div id="btns">
    <button id="btn1" type="button" onclick="$p.trigger('click.n1.n2.n3.n4');">trigger('click.n1.n2.n3.n4')</button>
    <button id="btn2" type="button" onclick="$p.trigger('click.n1.n2.n3');">trigger('click.n1.n2.n3')</button>
    <button id="btn3" type="button" onclick="$p.trigger('click.n1.n2');">trigger('click.n1.n2')</button>
    <button id="btn4" type="button" onclick="$p.trigger('click.n1');">trigger('click.n1')</button>
    <button id="btn5" type="button" onclick="$p.trigger('click');">trigger('click')</button>
</div>
<script>
    function log($e, msg) 
        var $log = $e.find(

以上是关于js事件的命名空间的详解的主要内容,如果未能解决你的问题,请参考以下文章

jquery的事件命名空间详解

js事件监听器用法实例详解

jQuery 命名空间的使用

jQuery().on方法的使用详解

jQuery().on方法的使用详解

命名空间jquery