JavaScript DOM 事件模型

事件驅動機制

  1. 當事件發生時,咱們收到事件的反饋,在 JavaScript 中,事件反饋是咱們自行定義的事件處理函數
  2. 事件,如點擊事件、鼠標移入事件等,是每個元素與生俱來的能力
  3. 一般說的綁定事件,其實是綁定事件的反饋,即事件處理函數
  4. 例如點擊一個按鈕,按鈕元素對象是事件發送器或事件源,事件是鼠標點擊事件,事件處理函數是偵聽器
  5. 元素對象發出事件,事件處理函數作出反應,這就是 JS 的事件驅動機制

在觀察者模式中,事件發送器就是主題,事件處理函數即偵聽器就是觀察者javascript

綁定事件反饋

  1. 內聯屬性html

    <button onclick="test()">按鈕</button>
    複製代碼

    介於結構和邏輯要相分離,不建議使用內聯方式綁定java

  2. 事件句柄chrome

    var oBtn = document.getElementsByTagName('button')[0];
    oBtn.onclick = function() {
        // this -> oBtn
    }
    複製代碼

    兼容性好,可是重複綁定會覆蓋數組

  3. 事件監聽器瀏覽器

    var oBtn = document.getElementsByTagName('button')[0];
    oBtn.addEventListener("click", funtion(){
        // this -> oBtn
     }, false);
    oBtn.addEventListener("click", test, false);
    funtion test(){ 
        // 事件處理函數
    }
    複製代碼

    重複添加,不會覆蓋以前添加的監聽器,可是若是事件類型、事件處理函數和最後一個布爾參數都相同,則不會重複執行markdown

    IE8 及如下不支持 addEventListener,可用 attachEvent 代替app

    var oBtn = document.getElementsByTagName('button')[0];
    oBtn.attachEvent("onclick", funtion(){
    	// this -> window
        }); 
    // 區別於 addEventListener,第一個參數使用 'onclick',而不是 'click'
    // 而且內部 this 指向 window
    // 對於 attachEvent,若是事件類型、事件處理函數都相同,仍是會重複執行
    複製代碼

    兼容性封裝函數

    function addEvent(elem, type, fn) {
        if (elem.addEventListener) {
            elem.addEventListener(type, fn, false);
        } else if (elem.attachEvent) {
            elem.attachEvent('on' + type, function(ev) {
                    fn.call(elem, ev); // call 兼容性比 bind 好
                });
        } else {
            elem['on' + type] = fn;
        }
    }
    複製代碼
  4. 解除綁定ui

    oBtn.onclik = null;
    oBtn.removeEventListener("click", test, false); // 解除 addEventListener
    oBtn.detachEvent('onclick', test); // 解除 attachEvent
    複製代碼

    示例:點擊一次後清除事件反饋

    oBtn.onclik = function() {
    	// ...
        this.onclick = null;
    }
    // 非嚴格模式
    oBtn.addEventListener("click", funtion() {
    	// ...
        this.removEventListener('cilck', arguments.callee, false);
    	}, false);
    // 嚴格模式
    oBtn.addEventListener("click", funtion temp() {
    	// ...
        this.removeEventListener('click', temp, false);
    	}, false);
    複製代碼

事件冒泡和捕獲

  1. 事件冒泡:當一個元素髮生事件時,該事件會向父級元素傳遞,按由子到父的順序觸發一連串的事件反饋,稱之爲事件冒泡

    DOM 上的嵌套關係會產生事件冒泡,例如兩個 div 嵌套,點擊內部的 div,觸發內部 div 的點擊事件,內部 div 的點擊事件處理函數進行響應,這個事件向其父級即外部 div 傳遞,外部 div 也有點擊事件,外部 div 所綁定的點擊事件反饋也會響應

    <div class="outer">
        <div class="inner"></div>
    </div>
    複製代碼
    var outer = document.getElementsByClassName('outer')[0],
        inner = outer.getElementsByClassName('inner')[0];
    outer.addEventListener('click', function () {
        console.log('bubble outer');
    }, false);
    inner.addEventListener('click', function () {
        console.log('bubble inner');
    }, false);
    // addEventListener 最後一個參數默認值爲 false,表示事件冒泡
    // 點擊 inner,打印出
    // bubble inner 
    // bubble outer
    複製代碼
  2. 事件捕獲:當一個元素髮生事件時,該事件會向父級元素傳遞,按由父到子的順序觸發一連串的事件反饋,稱之爲事件捕獲

    事件捕獲與事件冒泡的觸發順序相反,一樣須要 DOM 上的嵌套關係

    outer.addEventListener('click', function () {
        console.log('outer');
    }, true);
    inner.addEventListener('click', function () {
        console.log('inner');
    }, true);
    // addEventListener 最後一個參數使用 true,表示事件捕獲
    // 點擊 inner,打印出
    // outer
    // in
    複製代碼
  3. 捕獲和冒泡的執行順序

    outer.addEventListener('click', function () {
        console.log('bubble outer');
    }, false); // 冒泡
    inner.addEventListener('click', function () {
        console.log('bubble inner');
    }, false); // 冒泡
    outer.addEventListener('click', function () {
        console.log('outer');
    }, true); // 捕獲
    inner.addEventListener('click', function () {
        console.log('inner');
    }, true); // 捕獲
    // 點擊 inner,打印出
    // outer
    // bubble inner
    // inner
    // bubble outer
    複製代碼

    點擊一個元素,元素即事件源,若事件源綁定了事件處理函數,且設定了事件捕獲,則先執行捕獲,捕獲執行完畢後,按照綁定順序執行該事件源綁定的事件,若是設定了事件冒泡,再執行冒泡

  4. focus blur change submit reset select 事件沒有冒泡和捕獲,IE 瀏覽器沒有事件捕獲

阻止事件冒泡

  1. 阻止冒泡的方法

    Event 的原型上有 stopPropagation 方法,能夠阻止冒泡,是 w3c 的規範

    Event 的原型上有 cancleBubble 屬性,賦值爲 true,能夠阻止冒泡

  2. addEventListener 綁定事件處理函數,拿到事件對象

    var outer = document.getElementsByClassName('outer')[0],
        inner = outer.getElementsByClassName('inner')[0];
    inner.addEventListener('click', function (ev) {
        	console.log(ev); // 事件對象 ev
        	ev.stopPropagation(); // 阻止事件冒泡
        }, false);
    複製代碼
  3. IE 瀏覽器沒有 stopPropagation 方法,可使用 cancelBubble 屬性

    注意:IE 瀏覽器中事件對象存放在 window.event 中。IE8 不支持 addEventListener 方法

    // 封裝阻止冒泡的方法
    function cancelBubble(ev) {
        if (ev.stopPropagation) {
            ev.stopPropagation();
        } else ev.cancelBubble = true; // 兼容 IE8 及如下
    }
    // 使用上文中封裝好的 addEvent 方法
    function addEvent(elem, type, fn) {
        if (elem.addEventListener) {
            elem.addEventListener(type, fn);
        } else if (elem.attachEvent) {
            elem.attachEvent('on' + type, function (ev) {
                fn.call(elem, ev);
            });
        } else {
            elem['on' + type] = fn;
        }
    }
    // 綁定事件處理函數
    var outer = document.getElementsByClassName('outer')[0],
        inner = outer.getElementsByClassName('inner')[0];
    addEvent(inner, 'click', function (ev) {
            var ev = ev || window.event; // IE 兼容性寫法
        	cancelBubble(ev); // 阻止冒泡
        });
    複製代碼

阻止默認事件

  1. 三種方法

    • 事件對象 preventDefault() 方法,兼容 IE9 及以上
    • 事件對象 returnValue = false,兼容 IE8 及如下
    • 事件處理函數 return false
  2. 兼容性寫法

    function preventDefaultEvent(ev) {
        if (ev.preventDefault) {
            ev.preventDefault();
        } else ev.returnValue = false; // 兼容 IE8 及如下
    }
    複製代碼
  3. 右鍵菜單事件

    document.oncontextmenu = function (ev) {
        var ev = ev || window.event;
        // 1. ev.preventDefault(); // IE9 及以上
        // 2. ev.returnValue = false; // IE8 及如下
        // 3. return false;
    }
    複製代碼
  4. a 標籤跳轉事件

    href 使用僞協議

    <a href="javascript:void(0);">a 標籤</a> 
    <a href="javascript:;">a 標籤</a> 
    <a href="#">a 標籤</a> <!--跳轉到當前頁面頂部-->
    複製代碼

    onclick 事件 return false

    <a href="http://www.baidu.com" onclick="return false">a 標籤</a>
    <a href="http://www.baidu.com" onclick="return test(),false">a 標籤</a>
    <!--第二個是利用了 「,」 分隔符會返回最後一個的特色,與 test 方法無關-->
    複製代碼

    綁定事件處理函數

    <!--內聯綁定-->
    <a id='taga' href="http://www.baidu.com" onclick="return test()">a 標籤</a>
    <!--句柄綁定-->
    <script> document.getElementById('taga').onclick = test; function test(ev) { var ev = ev || window.event; // 1. ev.preventDefault(); // IE9 及以上 // 2. ev.returnValue = false; // IE8 及如下 // 3. return false; } // 前兩種方式在使用內聯屬性綁定時,不須要在屬性上加 return,第三種則須要 </script>
    複製代碼

    表單的 action 屬性支持 javascript: 僞協議,onsubmit 或者提交按鈕點擊事件均可以綁定處理函數,阻止提交的方法和阻止 a 標籤跳轉的方法相似

冒泡捕獲流

  1. 事件流:描述從頁面中接收事件的順序

  2. 事件冒泡流:微軟 IE 提出,Event Bubbling

  3. 事件捕獲流:網景 Netscape 提出,Event Capturing

  4. 事件流三個階段:事件捕獲階段、處於目標階段、事件冒泡階段

    元素觸發事件時,首先事件捕獲階段,由父到子的執行事件處理函數,而後處於目標階段,該元素的事件處理函數按綁定順序執行,最後事件冒泡階段,由子到父的執行事件處理函數

事件和事件源

  1. 事件即事件對象,能夠由事件處理函數的參數拿到

    IE8 及如下中事件對象存放在 window.event 中

    // btn 按鈕元素
    btn.onclick = function(ev) {
        var ev = ev || window.event; // IE8 兼容性寫法
    }
    複製代碼
  2. 事件源即事件源對象,是發生事件的元素,即事件發送器,能夠從事件對象中獲取

    IE8 及如下只有 srcElement,firefox 低版本只有 target,chrome 二者都有

    // btn 按鈕元素
    btn.onclick = function(ev) {
        var ev = ev || window.event; // IE8 兼容性寫法
        var tar = ev.target || ev.srcElement; // 獲取事件源的兼容性寫法
    }
    複製代碼

事件委託

  1. 事件委託也叫事件代理,指對父級元素綁定事件處理函數,經過獲取事件源來處理子元素

  2. 示例:點擊按鈕使列表 ul 增長 li 元素,點擊每一個 li 元素打印出其中的內容(innerHTML)

    若是不使用事件委託,須要循環對每一個 li 進行綁定,點擊按鈕添加新的 li 元素後也要進行綁定,效率低下

    使用事件委託,直接對 ul 綁定點擊事件處理函數,獲取事件對象、事件源對象,再對源對象進行處理

    <body>
    <button>btn</button>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>
    <script> var oBtn = document.getElementsByTagName('button')[0], oList = document.getElementsByTagName('ul')[0], oLi = oList.getElementsByTagName('li'); oBtn.onclick = function () { var li = document.createElement('li'); li.innerText = oLi.length + 1; oList.appendChild(li); } oList.onclick = function (ev) { var ev = ev || window.event, tar = ev.target || ev.srcElement; // tar 即爲被點擊的 li 元素 console.log(tar.innerHTML); // 返回在全部兄弟元素中的索引,借用數組 indexOf 方法 console.log(Array.prototype.indexOf.call(oLi, tar)); } </script>
    </body>
    複製代碼
相關文章
相關標籤/搜索