一次關於js事件出發機制反常的解決記錄

原由:正常狀況下我點擊s2時是先彈出我是children,再彈出我是father,可是卻出現了先彈出我是father,後彈出我是children的狀況,這種狀況是在和安卓app交互的h5頁面中出現的,本地測試沒有問題,可是在安卓打包的內嵌h5頁面就出現了問題。簡單化的代碼先展現出來。javascript

html代碼以下html

<div id="father" class="ss1">s1
    <div id="children" class="ss2">s2
    </div>
</div>

事件綁定以下java

$('#father').on('click',function (e) {
   alert('我是father')
 })
$('#children').on('click',function (e) {
   alert('我是children')
   e.stopPropagation();
 })

 

藉此問題,複習了一下js事件,先看一下幾個定義node

先來看事件註冊

// IE之外的其餘瀏覽器
// target :文檔節點、document、window 或 XMLHttpRequest。 
// type :字符串,事件名稱,不含「on」,好比「click」、「mouseover」、「keydown」等。 
// listener :實現了 EventListener 接口或者是 JavaScript 中的函數。 
// useCapture :是否使用捕捉,通常用 false,事件觸發時,會將一個 Event 對象傳遞給事件處理程序。

target.addEventListener(type,listener,useCapture);//添加
target.removeEventListener(type,listener,useCapture);//刪除
// IE瀏覽器
// target :文檔節點、document、window 或 XMLHttpRequest。 
// type :字符串,事件名稱,含「on」,好比「onclick」、「onmouseover」、「onkeydown」等。 
// listener :實現了 EventListener 接口或者是 JavaScript 中的函數。

target.attachEvent(type, listener);//添加
target.detachEvent(type, listener);// 移除

兼容寫法

兼容後的方法 
var func = function(){}; 
//例:addEvent(window,"load",func) 
function addEvent(elem, type, fn) { 
    if (elem.attachEvent) { 
        elem.attachEvent('on' + type, fn); 
        return; 
    } 
    if (elem.addEventListener) { 
        elem.addEventListener(type, fn, false); 
    } 
} 

//例:removeEvent(window,"load",func) 
function removeEvent(elem, type, fn) { 
    if (elem.detachEvent) { 
        elem.detachEvent('on' + type, fn); 
        return; 
    } 
    if (elem.removeEventListener) { 
        elem.removeEventListener(type, fn, false); 
    } 
}

獲取事件對象和事件源(觸發事件的元素) 

function eventHandler(e){ 
     //獲取事件對象 
    e = e || window.event;//IE和Chrome下是window.event FF下是e 
     //獲取事件源 
     var target = e.target || e.srcElement;//IE和Chrome下是srcElement FF下是target 
 } 

事件委託

myTable.onclick = function () { 
    e = e || window.event; 
    var targetNode = e.target || e.srcElement; 
    // 測試若是點擊的是TR就觸發 
    if (targetNode.nodeName.toLowerCase() === 'tr') { 
        alert('You clicked a table row!'); 
    } 
}

事件函數的解除綁定

和事件的綁定實際上是相對應的,若是須要接觸事件的綁定,運行對應的函數就能夠了。若是是原生JS綁定則對應運行removeEventListener()和detachEvent()。瀏覽器

若是是jQuery的bind()和delegate()綁定,也是存在對應的解綁函數用以清除註冊事件,好比unbind()和undelegate()。網絡

看一個代碼示例:app

var EventUtil = { 
    //註冊 
    addHandler: function(element, type, handler){ 
        if (element.addEventListener){ 
            element.addEventListener(type, handler, false); 
        } else if (element.attachEvent){ 
            element.attachEvent("on" + type, handler); 
        } else { 
            element["on" + type] = handler; 
        } 
    }, 
    //移除註冊 
    removeHandler: function(element, type, handler){ 
        if (element.removeEventListener){ 
            element.removeEventListener(type, handler, false); 
        } else if (element.detachEvent){ 
            element.detachEvent("on" + type, handler); 
        } else { 
            element["on" + type] = null; 
        } 
    }              
};

再來看看事件流

幾個概念dom

捕獲階段:事件對象經過目標的祖先從傳播窗口到目標的父。這個階段也被稱爲捕獲階段異步

目標階段:本次活動對象到達事件對象的事件的目標這個階段也被稱爲目標階段若是事件類型指示事件不起泡,則在完成此階段後,事件對象將中止。函數

 

冒泡階段事件對象經過目標的祖先中傳播以相反的順序,開始與目標的父和與所述結束窗口這個階段也被稱爲冒泡階段

默認行爲事件一般由實現做爲用戶操做的結果分派,以響應任務的完成,或者在異步活動(例如網絡請求)期間發信號通知進度。有些事件能夠用來控制下一個實現可能採起的行爲(或者撤銷實現已經採起的行動)。這個類別中的事件被認爲是可取消的,他們取消的行爲被稱爲他們的默認行爲

取消事件:可取消的事件對象能夠與一個或多個「默認動做」相關聯。要取消事件,請調用該preventDefault()方法。

一個圖片

再上個小demo

<ul>
    <li>點我試試</li>
</ul>
<div id="s1" class="ss1">s1
    <div id="s2" class="ss2">s2</div>
</div>
var ul = document.getElementsByTagName('ul')[0];
var li = document.getElementsByTagName('li')[0];
element.addEventListener(eventfunctionuseCapture) document.addEventListener(
'click',function(e){console.log('document clicked')},true);//第三個參數爲true使用捕獲,false爲冒泡,false爲默認 ul.addEventListener('click',function(e){console.log('ul clicked')},true); li.addEventListener('click',function(e){console.log('li clicked')},true); //IE低版本兼容寫法 li.attachEvent('onclick',function(event){ debugger console.log('li clicked'); event.cancelBubble=true; }); s1.addEventListener('click',function () { console.log('s1 捕獲方式') },true) s1.addEventListener('click',function () { console.log('s1 冒泡方式') },false) s2.addEventListener('click',function (e) { console.log('s2 捕獲方式') // e.stopPropagation(); },true) s2.addEventListener('click',function () { console.log('s2 冒泡方式') },false)

點擊li時,打印 依次爲

ul clicked li clicked  

點擊s1時,打印依次爲

s1 捕獲方式    s1 冒泡方式

點擊s2時,打印依次爲

s1 捕獲方式 s2 捕獲方式 s2 冒泡方式 s1 冒泡方式

處理事件冒泡和默認事件

一、e.preventDefault()

var a = document.getElementById("testA");
a.onclick =function(e){
    if(e.preventDefault){
        e.preventDefault();//
    }else{
        window.event.returnValue = false;//IE
    //注意:這個地方是沒法用return false代替的 
    //return false只能取消元素
    }
}

二、return false  javascript的return false只會阻止默認行爲,而是用jQuery的話則既阻止默認行爲又防止對象冒泡。

//原生js,只會阻止默認行爲,不會中止冒泡
var a = document.getElementById("testA");
a.onclick = function(){
    return false;//固然 也阻止了事件自己
};
//既然return false 和 e.preventDefault()都是同樣的效果,那它們有區別嗎?固然有。
//僅僅是在HTML事件屬性 和 DOM0級事件處理方法中 才能經過返回 return false 的形式組織事件宿主的默認行爲。
1 //jQuery,既阻止默認行爲又中止冒泡
2 $("#testA").on('click',function(){
3     return false;//固然 也阻止了事件自己
4 });

總結使用方法

當須要中止冒泡行爲時

function stopBubble(e) { 
    //若是提供了事件對象,則這是一個非IE瀏覽器 
    if ( e && e.stopPropagation ){ 
        e.stopPropagation(); //所以它支持W3C的stopPropagation()方法 
    }else{ 
        window.event.cancelBubble = true; //不然,咱們須要使用IE的方式來取消事件冒泡 
    }
}

當須要阻止默認事件時

function stopDefault( e ) { 
    if ( e && e.preventDefault ){
        e.preventDefault(); //阻止默認瀏覽器動做(W3C) 
    }else {
        window.event.returnValue = false; //IE中阻止函數器默認動做的方式 
    }
    return false; 
}

最後的解決方法:

讓咱們回顧一下最初的問題,可能部分瀏覽器把事件的useCapture默認爲true,致使點擊子元素時父元素的事件先響應了,因而個人辦法是在父元素的事件裏進行判斷

好比容器爲#a,動態插入的元素爲#b,在#a上監聽click事件,判斷event.target.id是否是等於b便可,若是.bclass這種,以此類推。

 

咱們常常能遇到阻止冒泡,可是阻止捕獲通常不會遇到,由於瀏覽器通常默認就給咱們阻止了,只能說什麼狀況都有啊,萬事仍是得考慮周全。

相關文章
相關標籤/搜索