jquery的事件命名空間詳解

来源:http://www.cnblogs.com/lyzg/archive/2016/04/04/5347857.html
-Advertisement-
Play Games

jquery現在的事件API:on,off,trigger支持帶命名空間的事件,當事件有了命名空間,就可以有效地管理同一事件的不同監聽器,在定義組件的時候,能夠避免同一元素應用到不同組件時,同一事件類型之間的影響,還能控制一些意外的事件冒泡。在實際工作中,相信大家都用的很多,但是不一定瞭解它的所有細 ...


jquery現在的事件API:on,off,trigger支持帶命名空間的事件,當事件有了命名空間,就可以有效地管理同一事件的不同監聽器,在定義組件的時候,能夠避免同一元素應用到不同組件時,同一事件類型之間的影響,還能控制一些意外的事件冒泡。在實際工作中,相信大家都用的很多,但是不一定瞭解它的所有細節,至少我有這樣的經驗,經常在碰到疑惑的時候,還得重新寫例子去驗證它的相關作用,所以本文想把事件命名空間相關的細節都梳理出來,將來再犯迷糊的時候可以回來翻著看看以便加深對它的理解和運用。

在詳細瞭解命名空間之前,得先認識下什麼是自定義事件,因為命名空間可以同時應用於自定義事件和瀏覽器預設事件當中。

1. 自定義事件

我們在定義組件的時候,瀏覽器的預設事件往往不能滿足我們的要求,比如我們寫了一個樹形組件,它有一個實例方法init用來完成這個組件的初始化工作,在這個方法調用結束之後,我們通常會自定義一個init事件,以便外部可以在樹組件初始化完成之後做一些回調處理:

<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這樣的自定義事件,該代碼運行結果如下

image
自定義事件的使用就跟瀏覽器預設事件的使用沒有任何區別,就連事件冒泡和阻止事件預設行為都完全支持,唯一的區別在於:瀏覽器自帶的事件類型可以通過瀏覽器的UI線程去觸發,而自定義事件必須通過代碼來手動觸發:
image

2. 事件命名空間

事件命名空間類似css的類,我們在事件類型的後面通過點加名稱的方式來給事件添加命名空間:

<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這個元素做實例化,以便實現一棵可以拖動的樹:

<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回調被調用兩次:
image
根本原因就是因為#tree這個元素被應用到了多個組件,在這兩個組件內部對#tree這個元素定義了同一個名稱的事件,所以後面實例化的組件在觸發該事件的時候也會導致前面實例化的組件的同一事件再次被觸發。通過命名空間就可以避免這個問題,讓組件各自的事件回調互不影響:

<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());
        }
    });

    var Dragable = function(element, options) {
        var $element = this.$element = $(element);
        //監聽init事件,觸發
        $element.on('init.my.dragable', $.proxy(options.onInit, this));
        this.init();
    };

    Dragable.prototype.init = function() {
        console.log('drag init!');
        this.$element.trigger('init.my.dragable');
    };

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

這樣tree實例的onInit就不會被調用2次了:

image

3. 命名空間的匹配規則

在第2部分的舉例當中,觸髮帶命名空間的事件時,觸發方式是:
image
然後就會調用這裡監聽的回調:
image
如果把觸發方式改一下,不改監聽方式,改成以下三種方式的一種,結果會怎麼樣呢:

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

答案是該監聽回調依然會被調用。這個跟命名空間的匹配規則有關,為了說明這個規則,可以用以下的這個代碼來測試:

<!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('.log');
        $log.append('<p>' + msg + '</p>');
    }

    var $p = $('#parent');

    $p.on('click.n1.n2.n3.n4', function(){
        log($p, 'click n1 n2 n3 n4');
    });
    $p.on('click.n1.n2.n3', function(){
        log($p, 'click n1 n2 n3');
    });
    $p.on('click.n1.n2', function(){
        log($p, 'click n1 n2');
    });
    $p.on('click.n1', function(){
        log($p, 'click n1');
    });
    $p.on('click', function(){
        log($p, 'click');
    });

</script>
</body>
</html>

初始化效果如下:
image 
依次點擊界面上的按鈕(不過點擊按鈕前得先刷新頁面,這樣的話各個按鈕效果才不會混在一起),界面列印的效果如下:
image
image
image
image
image

以上的測試代碼一共給$p元素的click事件定義了4個命名空間,然後針對不同的命名空間數量,添加了五個監聽器,通過外部的按鈕來手動觸發各個帶命名空間的事件,從最後的結果,我們能得出這樣一個規律:
1)當觸發不帶命名空間的事件時,該事件所有的監聽器都會觸發;(從最後一個按鈕的測試結果可看出)
2)當觸髮帶一個命名空間的事件時,在監聽時包含該命名空間的所有監聽器都會被觸發;(從第4個按鈕的測試結果可看出)
3)當觸髮帶多個命名空間的事件時,只有在監聽時同時包含那多個命名空間的監聽器才會被觸發;(從第2,3個按鈕的測試結果可看出)
4)只要觸髮帶命名空間的事件,該事件不帶命名空間的監聽器就不會被觸發;(從1,2,3,4個按鈕可看出)
5)2跟3其實就是一個,2是3的一種情況

這個規律完全適用於瀏覽器預設事件和自定義事件,自定義事件的測試可以用下麵的代碼,結論是一致的:

<!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('hello.n1.n2.n3.n4');">trigger('hello.n1.n2.n3.n4')</button>
    <button id="btn2" type="button" onclick="$p.trigger('hello.n1.n2.n3');">trigger('hello.n1.n2.n3')</button>
    <button id="btn3" type="button" onclick="$p.trigger('hello.n1.n2');">trigger('hello.n1.n2')</button>
    <button id="btn4" type="button" onclick="$p.trigger('hello.n1');">trigger('hello.n1')</button>
    <button id="btn5" type="button" onclick="$p.trigger('hello');">trigger('hello')</button>
</div>
<script>
    function log($e, msg) {
        var $log = $e.find('.log');
        $log.append('<p>' + msg + '</p>');
    }

    var $p = $('#parent');

    $p.on('hello.n1.n2.n3.n4', function(){
        log($p, 'hello n1 n2 n3 n4');
    });
    $p.on('hello.n1.n2.n3', function(){
        log($p, 'hello n1 n2 n3');
    });
    $p.on('hello.n1.n2', function(){
        log($p, 'hello n1 n2');
    });
    $p.on('hello.n1', function	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • AntiModerate 是一個漸進式圖片載入的 JavaScript 庫。我們多數看到的圖片顯示模式,都是從上到下逐漸顯示的,這是“標準式”圖像;而有的圖片是先出現一個很低解析度的圖像輪廓,類似加了馬賽克的模糊樣子,當圖片完全載入完畢時,圖片就由模糊變得清晰了,這就是“漸進式”圖像。 線上演示 源 ...
  • 圖像映射 圖像映射也稱為圖像熱點。 作用: 讓同一張圖片上的不同區域,可以實現多個不同的超鏈接功能。 圖示: 圖像映射三步走: 圖像映射的實現需要三方面配合完成: 1、圖像映射容器,就是一個img標簽,需使用usemap屬性與map標簽建立聯繫 2、圖像映射,就是一個map標簽,需要使用name屬性... ...
  • 當IE8發佈時,它將支持很多新的CSS display屬性值,包括與表格相關的屬性值:table、table-row和table-cell,它也是最後一款支持這些屬性值的主流瀏覽器。它標志著複雜CSS佈局技術的結束,同時也給了HTML表格佈局致命一擊。最終,使用CSS佈局來製作出類似於table佈局 ...
  • IP:是網路中唯一標識一臺電腦的邏輯標識。 特例:127.0.0.1 localhost 例子:192.168.33.xxx (對應門牌號碼,身份證號碼) 點分十進位形式,分成四段 範圍:0~255.0~255. 0~255. 0~255 DN:Domain Name 功能變數名稱 例子:www.baidu... ...
  • 瀏覽器相容性常見 瀏覽器相容問題⼀:不同瀏覽器的標簽預設的外補丁和內補丁不同 問題癥狀:隨便寫⼏個標簽,不加樣式控制的情況下,各⾃的margin 和padding差異較⼤。 碰到頻率:100% 解決⽅案:CSS⾥ *{margin:0;padding:0;} 備註:這個是最常 的也是最易解決的⼀個瀏 ...
  • 之前在公司做項目的時候,有這麼一個需求,要我寫一個評論框,可以隨著評論的行數增加而自動擴大,最開始我想用textarea實現,但是後來嘗試後發現textarea並不適合,textarea的高度不會隨著輸入行數的增多而增大,於是我上網尋求了下幫助,發現大神張鑫旭的這篇文章《div模擬textarea文 ...
  • 如果你把這些當做文章來看,那你始終是學不會,而是應該當做手冊來看,這些也是自己在寫網站遇到的問題。轉載請出處。 追夢子前端博客。 1. logo添加內容給h1設置text-index:-9999px的時候會把裡面的其他標簽也給定位過去。 解決方法:如果要添加內容,那麼圖片用背景來做。 2. butt ...
  • js面向對象的組成是 1、屬性 2、方法 使用的時候是再構造函數裡面加屬性,在原型裡面加方法。 如果直接在構造函數裡面:傳值、新建對象、增加屬性/方法、返回對象的這種方法,會產生問題,主要是 1、沒有new 2、函數重覆定義,這樣會讓資源浪費 所以,我們要在構造函數裡面使用this構造屬性,把方法放 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...