一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

深入淺出js事件

 instl 2018-09-05

一.事件流

事件冒泡和事件捕獲分別由微軟和網(wǎng)景公司提出,這兩個(gè)概念是為了解決頁面中事件流(事件發(fā)生順序)的問題。

<div id="outer">
    <p id="inner">Click me!</p>
</div>

上面的代碼當(dāng)中一個(gè)div元素當(dāng)中有一個(gè)p子元素,如果兩個(gè)元素都有一個(gè)click的處理函數(shù),那么我們怎么才能知道哪一個(gè)函數(shù)會首先被觸發(fā)呢?

為了解決這個(gè)問題微軟和網(wǎng)景提出了兩種幾乎完全相反的概念。

1.事件冒泡

微軟提出了名為事件冒泡的事件流。事件冒泡可以形象地比喻為把一顆石頭投入水中,泡泡會一直從水底冒出水面。也就是說,事件會從最內(nèi)層的元素開始發(fā)生,一直向上傳播,直到document對象。

因此上面的例子在事件冒泡的概念下發(fā)生click事件的順序應(yīng)該是p -> div -> body -> html -> document

2.事件捕獲

網(wǎng)景提出另一種事件流名為事件捕獲與事件冒泡相反,事件會從最外層開始發(fā)生,直到最具體的元素。

上面的例子在事件捕獲的概念下發(fā)生click事件的順序應(yīng)該是document -> html -> body -> div -> p

3.W3C事件階段(event phase):

當(dāng)一個(gè)DOM事件被觸發(fā)的時(shí)候,他并不是只在它的起源對象上觸發(fā)一次,而是會經(jīng)歷三個(gè)不同的階段。簡而言之:事件一開始從文檔的根節(jié)點(diǎn)流向目標(biāo)對象(捕獲階段),然后在目標(biāo)對向上被觸發(fā)(目標(biāo)階段),之后再回溯到文檔的根節(jié)點(diǎn)(冒泡階段)如圖所示(圖片來自W3C):

image

事件捕獲階段(Capture Phase)

事件從文檔的根節(jié)點(diǎn)出發(fā),隨著DOM樹的結(jié)構(gòu)向事件的目標(biāo)節(jié)點(diǎn)流去。途中經(jīng)過各個(gè)層次的DOM節(jié)點(diǎn),并在各節(jié)點(diǎn)上觸發(fā)捕獲事件,直到到達(dá)時(shí)間的目標(biāo)節(jié)點(diǎn)。捕獲階段的主要任務(wù)是簡歷傳播路徑,在冒泡階段,時(shí)間會通過這個(gè)路徑回溯到文檔根節(jié)點(diǎn)。

例如,通過下面的這個(gè)函數(shù)來給節(jié)點(diǎn)設(shè)置監(jiān)聽,可以通過將;設(shè)置成true來為事件的捕獲階段添加監(jiān)聽回調(diào)函數(shù)。

element.removeEventListener(&ltevent-name>, <callback>, <use-capture>);

而,在實(shí)際應(yīng)用中,我們并沒有太多使用捕獲階段監(jiān)聽的用例,但是通過在捕獲階段對事件的處理,我們可以阻止類似click事件在某個(gè)特定元素上被觸發(fā)。如下:

var form=document.querySeletor('form');
form.addEventListener('click',function(e){
  e.stopPropagation();
  },true);

如果你對這種用法不是很了解的話,建議設(shè)置為false或者undefined,從而在冒泡階段對事件進(jìn)行監(jiān)聽,這也是常用的方法。

目標(biāo)階段(Target Phase)

當(dāng)事件到達(dá)目標(biāo)節(jié)點(diǎn)時(shí),事件就進(jìn)入了目標(biāo)階段。事件在目標(biāo)節(jié)點(diǎn)上被觸發(fā),然后逆向回流,知道傳播到最外層的文檔節(jié)點(diǎn)。

對于多層嵌套的節(jié)點(diǎn),鼠標(biāo)和指針事件經(jīng)常會被定位到最里層的元素上。假設(shè),你在一個(gè)div元素上設(shè)置了click的監(jiān)聽函數(shù),而用戶點(diǎn)擊在了這個(gè)div元素內(nèi)部的p元素上,那么p元素就是這個(gè)時(shí)間的目標(biāo)元素。事件冒泡讓我們可以在這個(gè)div或者更上層的元素上監(jiān)聽click事件,并且時(shí)間傳播過程中觸發(fā)回調(diào)函數(shù)。

冒泡階段(Bubble Phase)

事件在目標(biāo)事件上觸發(fā)后,并不在這個(gè)元素上終止。它會隨著DOM樹一層層向上冒泡,直到到達(dá)最外層的根節(jié)點(diǎn),一直向上傳播,直到document對象。也就是說,同一事件會一次在目標(biāo)節(jié)點(diǎn)的父節(jié)點(diǎn),父節(jié)點(diǎn)的父節(jié)點(diǎn)...直到最外層的節(jié)點(diǎn)上觸發(fā)。

絕大多數(shù)事件是會冒泡的,但并非所有的。

二.事件代理(event delegation)

在JavaScript中,經(jīng)常會碰到要監(jiān)聽列表中多項(xiàng)li的情形,假設(shè)我們有一個(gè)列表如下:

復(fù)制代碼
<ul id="list">
  <li id="item1">item1</li>
  <li id="item2">item2</li>
  <li id="item3">item3</li>
  <li id="item4">item4</li>
</ul>
復(fù)制代碼

如果我們要實(shí)現(xiàn)以下功能:當(dāng)鼠標(biāo)點(diǎn)擊某一li時(shí),alert輸出該li的內(nèi)容,我們通常的寫法是這樣的:

-當(dāng)列表項(xiàng)比較少時(shí),直接在html里給每個(gè)li添加onclick事件
-列表項(xiàng)比較多時(shí),在onload時(shí)就給每個(gè)列表項(xiàng)調(diào)用監(jiān)聽


第一種方法比較簡單直接,但是沒有顧及到html與JavaScript的分離,不建議使用,第二種方法的代碼如下:

復(fù)制代碼
window.onload=function(){
  var ulNode=document.getElementById("list");
  var liNodes=ulNode.childNodes||ulNode.children;
  for(var i=0;i<liNodes.length;i++){
    liNodes[i].addEventListener('click',function(e){
      alert(e.target.innerHTML);
    },false);
  }
}
復(fù)制代碼

由上可以看出來,假如不停的刪除或添加li,則function()也要不停的更改操作,易出錯,因此推薦使用事件代理。

在傳統(tǒng)的事件處理中,你按照需要為每一個(gè)元素添加或者是刪除事件處理器。然而,事件處理器將有可能導(dǎo)致內(nèi)存泄露或者是性能下降——你用得越多這種風(fēng)險(xiǎn)就越大。JavaScript事件代理則是一種簡單的技巧,通過它你可以把事件處理器添加到一個(gè)父級元素上,這樣就避免了把事件處理器添加到多個(gè)子級元素上。

事件代理機(jī)制

事件代理用到了兩個(gè)在JavaSciprt事件中兩個(gè)特性:事件冒泡以及目標(biāo)元素。使用事件代理,我們可以把事件處理器添加到一個(gè)元素上,等待一個(gè)事件從它的子級元素里冒泡上來,并且可以得知這個(gè)事件是從哪個(gè)元素開始的。

事件代理實(shí)現(xiàn)

例如,有一個(gè)table元素,ID是“report”,我們?yōu)檫@個(gè)表格添加一個(gè)事件處理器以調(diào)用editCell函數(shù)。

第一步,找到目標(biāo)元素

editCell函數(shù)需要判斷傳到table來的事件的目標(biāo)元素??紤]到我們要寫的幾個(gè)函數(shù)中都有可能用到這一功能,所以我們把它單獨(dú)放到一個(gè)名為getEventTarget的函數(shù)中:

function getEventTarget(e) {
  e = e || window.event;//事件對象
  return e.target || e.srcElement;
}

在IE里目標(biāo)元素放在srcElemnt屬性中,而在其它瀏覽器里則是target屬性。

第二步,判斷目標(biāo)元素,進(jìn)行相關(guān)操作

接下來就是editCell函數(shù)了,這個(gè)函數(shù)調(diào)用到了getEventTarget函數(shù)。一旦我們得到了目標(biāo)元素,剩下的事情就是看看它是否是我們所需要的那個(gè)元素了。

復(fù)制代碼
function editCell(e){
           var target = getEventTarget(e);
           if(target.tagName.toLowerCase() =='td')
           {
                // DO SOMETHING WITH THE CELL
            }
}
復(fù)制代碼

由上敘述,我們可以使用事件代理來實(shí)現(xiàn)本小結(jié)開始對每一個(gè)li的監(jiān)聽。第三種方法,代碼如下:

復(fù)制代碼
window.onload=function(){
  e = e || window.event;
  target = e.target || e.srcElement;
  var ulNode=document.getElementById("list");
  ulNode.addEventListener('click',function(e){
       if(target&&target.nodeName.toUpperCase()=="LI"){/*判斷目標(biāo)事件是否為li*/
         alert(e.target.innerHTML);
       }
     },false);
};
復(fù)制代碼

注:

-tagName 屬性返回元素的標(biāo)簽名。在 HTML 中,tagName 屬性的返回值始終是大寫的。

-nodeName 屬性指定節(jié)點(diǎn)的節(jié)點(diǎn)名稱。如果節(jié)點(diǎn)是元素節(jié)點(diǎn),則 nodeName 屬性返回標(biāo)簽名。如果節(jié)點(diǎn)是屬性節(jié)點(diǎn),則 nodeName 屬性返回屬性的名稱。對于其他節(jié)點(diǎn)類型,nodeName 屬性返回不同節(jié)點(diǎn)類型的不同名稱。試一試

三.事件兼容處理

現(xiàn)代綁定中W3C 使用的是:addEventListener 和removeEventListener。IE 使用的是attachEvent 和detachEvent。我們知道IE 的這兩個(gè)問題多多,并且伴隨內(nèi)存泄漏。所以,解決這些問題非常有必要。

那么我們希望解決非IE 瀏覽器事件綁定哪些問題呢?


1)支持同一元素的同一事件句柄可以綁定多個(gè)監(jiān)聽函數(shù);
2)如果在同一元素的同一事件句柄上多次注冊同一函數(shù),那么第一次注冊后的所有注冊
都被忽略;
3)函數(shù)體內(nèi)的this 指向的應(yīng)當(dāng)是正在處理事件的節(jié)點(diǎn)(如當(dāng)前正在運(yùn)行事件句柄的節(jié)
點(diǎn));
4)監(jiān)聽函數(shù)的執(zhí)行順序應(yīng)當(dāng)是按照綁定的順序執(zhí)行;
5)在函數(shù)體內(nèi)不用使用event = event || window.event; 來標(biāo)準(zhǔn)化Event 對象;

設(shè)計(jì)原理

1.通過使用傳統(tǒng)事件綁定對IE 進(jìn)行封裝,模擬現(xiàn)代事件綁定(不使用attachEvent/detachEvent)。

2.把IE常用的Event對象配對到W3C中去

復(fù)制代碼
//跨瀏覽器添加事件綁定
function addEvent(obj, type, fn) {
    if (typeof obj.addEventListener != 'undefined') {
        obj.addEventListener(type, fn, false);
    } else {
        //創(chuàng)建一個(gè)存放事件的哈希表(散列表)
        if (!obj.events) obj.events = {};
        //第一次執(zhí)行時(shí)執(zhí)行
        if (!obj.events[type]) {    
            //創(chuàng)建一個(gè)存放事件處理函數(shù)的數(shù)組
            obj.events[type] = [];
            //把第一次的事件處理函數(shù)先儲存到第一個(gè)位置上
            if (obj['on' + type]) obj.events[type][0] = fn;
        } else {
            //同一個(gè)注冊函數(shù)進(jìn)行屏蔽,不添加到計(jì)數(shù)器中
            if (addEvent.equal(obj.events[type], fn)) return false;
        }
        //從第二次開始我們用事件計(jì)數(shù)器來存儲
        obj.events[type][addEvent.ID++] = fn;
        //執(zhí)行事件處理函數(shù)
        obj['on' + type] = addEvent.exec;
    }
}

//為每個(gè)事件分配一個(gè)計(jì)數(shù)器
addEvent.ID = 1;

//執(zhí)行事件處理函數(shù)
addEvent.exec = function (event) {
    var e = event || addEvent.fixEvent(window.event);
    var es = this.events[e.type];
    for (var i in es) {
        es[i].call(this, e);
    }
};

//同一個(gè)注冊函數(shù)進(jìn)行屏蔽
addEvent.equal = function (es, fn) {
    for (var i in es) {
        if (es[i] == fn) return true;
    }
    return false;
}

//把IE常用的Event對象配對到W3C中去
addEvent.fixEvent = function (event) {
    event.preventDefault = addEvent.fixEvent.preventDefault;
    event.stopPropagation = addEvent.fixEvent.stopPropagation;
    event.target = event.srcElement;
    return event;
};

//IE阻止默認(rèn)行為
addEvent.fixEvent.preventDefault = function () {
    this.returnValue = false;
};

//IE取消冒泡
addEvent.fixEvent.stopPropagation = function () {
    this.cancelBubble = true;
};


//跨瀏覽器刪除事件
function removeEvent(obj, type, fn) {
    if (typeof obj.removeEventListener != 'undefined') {
        obj.removeEventListener(type, fn, false);
    } else {
        if (obj.events) {
            for (var i in obj.events[type]) {
                if (obj.events[type][i] == fn) {
                    delete obj.events[type][i];
                }
            }
        }
    }
}
復(fù)制代碼

addEvent的優(yōu)點(diǎn)
-可以在所有瀏覽器中工作,就算是更古老無任何支持的瀏覽器
-this關(guān)鍵字可以在所有的綁定函數(shù)中使用,指向的是當(dāng)前元素
-中和了所有防止瀏覽器默認(rèn)行為和阻止事件冒泡的各種瀏覽器特定函數(shù)
-不管瀏覽器類型,事件對象總是作為第一個(gè)對象傳入


addEvent的缺點(diǎn)
-僅工作在冒泡階段(因?yàn)樗钊胧褂檬录壎ǖ膫鹘y(tǒng)方式)

知識說明:

添加、移除事件,代碼如下:

復(fù)制代碼
var addEvent = document.addEventListener ?
function(elem,type, listener, useCapture) {
elem.addEventListener(type, listener, useCapture);
}:
function(elem, type, listener, useCapture) {
 elem.attachEvent('on' + type, listener);
};

var delEvent = document.removeEventListener ? 
function(elem, type, listener, useCapture) {
elem.removeEventListener(type, listener, useCapture)
}:
function(elem, type, listener, useCapture) { 
elem.detachEvent('on' + type, listener);
};
復(fù)制代碼

阻止事件繼續(xù)傳播,代碼如下:

復(fù)制代碼
function stopEvent (evt) { 
var evt = evt || window.event; 
if (evt.stopPropagation) { 
evt.stopPropagation(); 
} else { 
evt.cancelBubble = true; 
} 
}
復(fù)制代碼

取消默認(rèn)行為,代碼如下:

復(fù)制代碼
function stopEvent (evt) { 
var evt = evt || window.event; 
if (evt.preventDefault) { 
evt.preventDefault(); 
} else { 
evt.returnValue = false; 
} 
}
復(fù)制代碼

四.事件類型

事件類型有:UI(用戶界面)事件,用戶與頁面上元素交互時(shí)觸發(fā) ;焦點(diǎn)事件:當(dāng)元素獲得或失去焦點(diǎn)時(shí)觸發(fā) ; 文本事件:當(dāng)在文檔中輸入文本時(shí)觸發(fā);鍵盤事件:當(dāng)用戶通過鍵盤在頁面上執(zhí)行操作時(shí)觸發(fā);鼠標(biāo)事件:當(dāng)用戶通過鼠標(biāo)在頁面上執(zhí)行操作時(shí)觸發(fā);滾輪事件:當(dāng)使用鼠標(biāo)滾輪(或類似設(shè)備)時(shí)觸發(fā)。它們之間是繼承的關(guān)系,如下圖:

936a856eb8d9456ebf60b89d2cdc0dfc

1.

a7275e5c067e4af6a2d1c5c5d49dbdee

常用:window、image。例如設(shè)置默認(rèn)的圖片:

<img src="photo" alt="photo.jpg" onerror="this.src='defualt.jpg'">

2.

23f6429ea5424d3db6032c3f92ffd591

3.

268737333e2e44c4b6de2a14618409c9[4]

4.

af69ef8af9be4843973c2474dae0888a

5.

21a63f831af24baabf5d41bd8afed5fd

6.

1e921fb859e74a7ea37fb0e5f6e2e72e

MouseEvent 順序
從元素A上方移過
-mousemove-> mouseover (A) ->mouseenter (A)-> mousemove (A) ->mouseout (A) ->mouseleave (A)
點(diǎn)擊元素
-mousedown-> [mousemove]-> mouseup->click

7.

0c7bb276b6fc441190a0637c808ea23f

詳細(xì)使用,請參考js事件手冊。如:http://www.w3school.com.cn/jsref/dom_obj_event.asp

------------------------------------------------------------------------------------------------------------------------------------- 

轉(zhuǎn)載需注明轉(zhuǎn)載字樣,標(biāo)注原作者和原博文地址。

更多閱讀:

http://blog.sina.com.cn/s/blog_5f54f0be0100cy49.html

http://www.cnblogs.com/luhangnote/archive/2012/08/16/2642657.html

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    午夜福利激情性生活免费视频| 亚洲黄色在线观看免费高清| 欧美精品一区二区三区白虎| 欧美成人精品国产成人综合| 黄色片一区二区三区高清| 亚洲国产另类久久精品| 欧美成人国产精品高清| 少妇淫真视频一区二区| 日韩高清一区二区三区四区| 精品日韩视频在线观看| 中文字幕五月婷婷免费| 欧美一区二区三区五月婷婷 | 九九热在线免费在线观看| 国产精品涩涩成人一区二区三区| 欧美区一区二在线播放| 欧美日韩亚洲国产精品| 国产三级黄片在线免费看| 日韩不卡一区二区视频| 精品日韩中文字幕视频在线| 人人妻人人澡人人夜夜| 又大又长又粗又黄国产| 一本色道久久综合狠狠躁| 国产黄色高清内射熟女视频| 欧美二区视频在线观看| 中文人妻精品一区二区三区四区 | 麻豆tv传媒在线观看| 偷拍偷窥女厕一区二区视频| 91亚洲精品亚洲国产| 国产91麻豆精品成人区| 日韩精品日韩激情日韩综合| 在线视频免费看你懂的| 日韩无套内射免费精品| 国产高清一区二区不卡| 欧美黑人巨大一区二区三区| 九九热国产这里只有精品| 国产一区二区三区丝袜不卡| 免费午夜福利不卡片在线 视频| 日本加勒比在线播放一区| 欧美日韩亚洲国产综合网 | 久久99午夜福利视频| 国产一级特黄在线观看|