Introduction to events

2018-05-15 17:26 更新
先決條件: 基本的計(jì)算機(jī)素養(yǎng),對(duì)HTML和CSS的基本了解, JavaScript第一步。
目的: 理解事件的基本理論,它們?nèi)绾卧跒g覽器中工作,以及事件在不同的編程環(huán)境中如何不同。

一系列幸運(yùn)事件

如上所述,事件是在您正在編程的系統(tǒng)中發(fā)生的操作或事件,系統(tǒng)將在事件發(fā)生時(shí)觸發(fā)某種信號(hào),并提供某種機(jī)制 可以自動(dòng)采取(例如一些代碼運(yùn)行)事件發(fā)生時(shí)。 例如,在機(jī)場,當(dāng)跑道對(duì)于飛機(jī)起飛是清楚的時(shí),信號(hào)被傳送到飛行員,并且因此他們開始飛行飛機(jī)。

在Web的情況下,事件在瀏覽器窗口內(nèi)觸發(fā),并且傾向于附加到駐留在其中的特定項(xiàng)目 - 這可以是單個(gè)元素,元素集合,當(dāng)前選項(xiàng)卡中加載的HTML文檔,或者 整個(gè)瀏覽器窗口。 有很多不同類型的事件可以發(fā)生,例如:

  • The user clicking the mouse over a certain element, or hovering the cursor over a certain element.
  • The user pressing a key on the keyboard.
  • The user resizing or closing the browser window.
  • A web page finishing loading.
  • A form being submitted.
  • A video being played, or paused, or finishing play.
  • An error occuring.

您將從此處收集(以及查看MDN 事件引用),有 >很多可以響應(yīng)的事件。

每個(gè)可用的事件都有一個(gè)事件處理程序,這是一個(gè)代碼塊,通常由開發(fā)人員定義,將在事件觸發(fā)時(shí)運(yùn)行。 當(dāng)這樣的代碼塊被定義為響應(yīng)事件觸發(fā)而運(yùn)行時(shí),我們說我們注冊了一個(gè)事件處理程序 請(qǐng)注意,事件處理程序有時(shí)稱為事件偵聽器 - 為了我們的目的,它們是可以互換的,雖然嚴(yán)格來說他們一起工作。 偵聽器偵聽事件發(fā)生,處理程序是響應(yīng)它發(fā)生運(yùn)行的代碼。

注意:請(qǐng)務(wù)必注意,網(wǎng)絡(luò)事件不是核心JavaScript語言的一部分 - 它們定義為瀏覽器中內(nèi)置的JavaScript API的一部分。 您可以通過檢出上面列出的事件引用來了解哪個(gè)API規(guī)范定義了每個(gè)事件。

一個(gè)簡單的例子

讓我們來看一個(gè)簡單的例子來解釋我們在這里的意思。 您已經(jīng)看過本課程中的許多示例中使用的事件和事件處理程序,但讓我們回顧一下,以鞏固我們的知識(shí)。 在下面的示例中,我們有一個(gè) "> < button> ,按下時(shí)會(huì)將背景更改為隨機(jī)顏色:

<button>Change color</button>

JavaScript看起來像這樣:

var btn = document.querySelector('button');

function random(number) {
  return Math.floor(Math.random()*number);
}

btn.onclick = function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

在這段代碼中,我們使用"返回與指定的選擇器組匹配的文檔內(nèi)的第一個(gè)元素(使用文檔標(biāo)記中的第一個(gè)元素的深度優(yōu)先預(yù)先遍歷文檔節(jié)點(diǎn)|并按順序節(jié)點(diǎn)迭代次序)。 Document.querySelector() function.\">> Document.querySelector() 函數(shù)。我們還定義一個(gè)返回隨機(jī)數(shù)的函數(shù)。代碼的第三部分是事件處理程序。 btn variable points to a <button> element, and this type of object has a number of events that can fire on it, and therefore, event handlers available.\"> btn 變量指向一個(gè)< button> 元素,這種類型的對(duì)象有許多事件可以觸發(fā)它,因此,事件處理程序可用。onclick event handler property to equal an anonymous function containing code that generated a random RGB color and sets the <body>\">我們正在偵聽點(diǎn)擊事件觸發(fā),通過將 onclick 事件處理程序?qū)傩栽O(shè)置為等于包含生成隨機(jī)RGB顏色的代碼的匿名函數(shù),并設(shè)置< body> 背景顏色等于它。

每當(dāng)點(diǎn)擊事件在< button> 元素上觸發(fā)時(shí),即當(dāng)用戶點(diǎn)擊它時(shí),此代碼將運(yùn)行。

示例輸出如下:

它不只是網(wǎng)頁

在這一點(diǎn)上值得提及的另一件事是,事件不是JavaScript特有的 - 大多數(shù)編程語言都有某種類型的事件模型,它的工作方式通常不同于JavaScript的方式。 事實(shí)上,用于網(wǎng)頁的JavaScript中的事件模型與用于其他環(huán)境中的JavaScript的事件模型不同。

例如, Node.js 是一個(gè)非常受歡迎的JavaScript運(yùn)行時(shí),它允許開發(fā)人員使用JavaScript來構(gòu)建網(wǎng)絡(luò)和服務(wù)器端應(yīng)用程序。 Node.js事件模型依賴于偵聽器偵聽事件 和發(fā)射器周期性地發(fā)出事件 - 它聽起來不一樣,但代碼是完全不同的,使用 on()這樣的函數(shù)注冊一個(gè)事件監(jiān)聽器,并且 once 以注冊在運(yùn)行一次之后取消注冊的事件偵聽器。 HTTP連接事件文檔提供了一個(gè)很好的使用示例 。

作為另一個(gè)示例,您現(xiàn)在還可以使用JavaScript來開發(fā)跨瀏覽器插件 - 瀏覽器功能增強(qiáng) - 使用名為 Add-ons / WebExtensions"> WebExtensions 。 事件模型類似于web事件模型,但有點(diǎn)不同 - 事件監(jiān)聽器屬性是駝峰式的(例如 onMessage 而不是 onmessage ), 與 addListener 函數(shù)。 有關(guān)示例,請(qǐng)參見 runtime.onMessage頁面。

在學(xué)習(xí)的這個(gè)階段,你不需要了解其他這樣的環(huán)境; 我們只是想清楚地表明,事件可能在不同的編程環(huán)境中有所不同。

使用網(wǎng)絡(luò)事件的方式

有多種不同的方法可以向Web頁面添加事件偵聽器代碼,以便在關(guān)聯(lián)的事件觸發(fā)時(shí)運(yùn)行。 在本節(jié)中,我們將回顧不同的機(jī)制,并討論應(yīng)該使用哪些機(jī)制。

事件處理程序?qū)傩?

這些是存在的屬性,包含我們在課程中最常見的事件處理程序代碼。 回到上面的例子:

var btn = document.querySelector('button');

btn.onclick = function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

onclick 屬性是事件處理程序?qū)傩?用于這種情況。 它本質(zhì)上是按鈕上可用的任何其他屬性(例如 btn.textContent btn.style ),但它是一種特殊類型 - 當(dāng)您將其設(shè)置為等于某些代碼時(shí),該代碼將在事件觸發(fā)按鈕時(shí)運(yùn)行。

您還可以將處理程序?qū)傩栽O(shè)置為等于命名的函數(shù)名稱(就像我們在構(gòu)建自己的函數(shù)中看到的)。 以下將工作相同:

var btn = document.querySelector('button');

function bgChange() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

btn.onclick = bgChange;

有許多不同的事件處理程序?qū)傩钥捎谩?讓我們做一個(gè)實(shí)驗(yàn)。

首先,制作本地副本 "external"> random-color-eventhandlerproperty.html ,然后在瀏覽器中打開它。 它只是我們已經(jīng)在本文中已經(jīng)玩過的簡單隨機(jī)顏色示例的副本。 現(xiàn)在嘗試將 btn.onclick 依次改為以下不同的值,并觀察示例中的結(jié)果:

  • btn.onfocus and btn.onblur — The color will change when the button is focused and unfocused (try pressing tab to tab on to the button and off again). These are often used to display information about how to fill in form fields when they are focused, or display an error message if a form field has just been filled in with an incorrect value.
  • btn.ondblclick — The color will change only when it is double-clicked.
  • window.onkeypress, window.onkeydown, window.onkeyup — The color will change when a key is pressed on the keyboard. keypress refers to a general press (button down and then up), while keydown and keyup refer to just the key down and key up parts of the keystroke, respectively. Note that it doesn't work if you try to register this event handler on the button itself — we've had to register it on the window object, which represents the entire browser window.
  • btn.onmouseover and btn.onmouseout — The color will change when the mouse pointer is moved so it begins hovering over the button, or when it stops hover over the button and moves off it, respectively.

一些事件非常通用,幾乎可以在任何地方使用(例如 onclick 處理程序可以在幾乎任何元素上注冊),而一些事件更具體,只在某些情況下有用(例如使用 僅 onplay 僅適用于特定元素,例如 ://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/video"title ="使用HTML視頻元素將視頻內(nèi)容嵌入到文檔中。"> < video> 代碼> )。

內(nèi)聯(lián)事件處理程序 - 不要使用這些

您可能還會(huì)在代碼中看到這樣的模式:

<button onclick="bgChange()">Press me</button>
function bgChange() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

注冊在Web上發(fā)現(xiàn)的事件處理程序的最早方法涉及事件處理程序HTML屬性(又稱內(nèi)聯(lián)事件處理程序),如上所示 - 屬性值字面上是JavaScript代碼 你想在事件發(fā)生時(shí)運(yùn)行。 上述示例調(diào)用在 在同一頁面上引用可執(zhí)行腳本"> < script> 元素,但也可以直接在屬性內(nèi)插入JavaScript,例如:

<button onclick="alert('Hello, this is my old-fashioned event handler!');">Press me</button>

你會(huì)發(fā)現(xiàn)許多事件處理程序?qū)傩缘腍TML屬性等價(jià)物; 然而,你不應(yīng)該使用這些 - 它們被認(rèn)為是不良做法。 看起來很容易使用事件處理程序?qū)傩裕绻阒皇亲鲆恍┛焖俚氖虑?,但他們很快就變得難以管理和低效率。

首先,將HTML和JavaScript混合起來并不是一個(gè)好主意,因?yàn)樗茈y解析 - 將JavaScript集中在一個(gè)位置更好; 如果它在一個(gè)單獨(dú)的文件中,您可以將其應(yīng)用于多個(gè)HTML文檔。

即使在單個(gè)文件中,內(nèi)聯(lián)事件處理程序也不是一個(gè)好主意。 一個(gè)按鈕是OK,但如果你有100個(gè)按鈕怎么辦? 您必須向文件中添加100個(gè)屬性; 它很快就變成了維修噩夢。 使用JavaScript,您可以輕松地添加一個(gè)事件處理函數(shù)到頁面上的所有按鈕,無論有多少,使用這樣的東西:

var buttons = document.querySelectorAll('button');

for (var i = 0; i < buttons.length; i++) {
  buttons[i].onclick = bgChange;
}

注意:將您的編程邏輯與您的內(nèi)容分開也可以使您的網(wǎng)站對(duì)搜索引擎更加友好。

addEventListener()和removeEventListener()

最新類型的事件機(jī)制在文檔對(duì)象模型(DOM)級(jí)別2中定義 活動(dòng)規(guī)格,為瀏覽器提供新功能 - addEventListener( ) 。 這個(gè)函數(shù)與事件處理程序?qū)傩缘姆绞筋愃疲Z法明顯不同。 我們可以重寫我們的隨機(jī)顏色示例,看起來像這樣:

var btn = document.querySelector('button');

function bgChange() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}   

btn.addEventListener('click', bgChange);

因此,在 addEventListener()函數(shù)中,我們指定兩個(gè)參數(shù) - 我們想要注冊此處理程序的事件的名稱,以及包含我們要響應(yīng)它運(yùn)行的處理程序函數(shù)的代碼。 請(qǐng)注意,將所有代碼放在 addEventListener()函數(shù)內(nèi),這是一個(gè)匿名函數(shù),如下所示:

btn.addEventListener('click', function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
});

這種機(jī)制比前面討論的舊機(jī)制具有一些優(yōu)點(diǎn)。 對(duì)于開始,有一個(gè)對(duì)應(yīng)的函數(shù), removeEventListener() ,它刪除以前添加的偵聽器。 例如,這將刪除本節(jié)中第一個(gè)代碼塊中的偵聽器集:

btn.removeEventListener('click', bgChange);

這對(duì)于簡單的小程序來說并不重要,但是對(duì)于更大,更復(fù)雜的程序,它可以提高清理舊未使用的事件處理程序的效率,此外還允許您在不同的情況下使用相同的按鈕執(zhí)行不同的操作 - 我們需要做的是根據(jù)需要添加/刪除事件處理程序。

其次,您還可以為同一個(gè)偵聽器注冊多個(gè)處理程序。 將不應(yīng)用以下兩個(gè)處理程序:

myElement.onclick = functionA;
myElement.onclick = functionB;

因?yàn)榈诙袑⒏采w onclick 設(shè)置的第一個(gè)值。 然而,這將工作:

myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);

這兩個(gè)函數(shù)現(xiàn)在將在單擊元素時(shí)運(yùn)行。

此外,此事件機(jī)制還提供了許多其他強(qiáng)大的功能和選項(xiàng)。 這些內(nèi)容有點(diǎn)超出了本文的范圍,但如果您想閱讀它們,請(qǐng)查看 Web / API / EventTarget / addEventListener"> addEventListener() / removeEventListener"> removeEventListener() 參考頁。

我應(yīng)該使用什么機(jī)制?

在這三種機(jī)制中,您絕對(duì)不應(yīng)該使用HTML事件處理程序?qū)傩?- 這些屬性已經(jīng)過時(shí),而且是上面提到的壞做法。

其他兩個(gè)是相對(duì)可互換的,至少對(duì)于簡單的用途:

  • Event handler properties have less power and options, but better cross browser compatibility (being supported as far back as Internet Explorer 8). You should probably start with these as you are learning.
  • DOM Level 2 Events (addEventListener(), etc.) are more powerful, but can also become more complex and are less well supported (supported as far back as Internet Explorer 9). You should also experiment with these, and aim to use them where possible.

第三種機(jī)制的主要優(yōu)點(diǎn)是,如果需要,可以使用 removeEventListener()刪除事件處理程序代碼,如果需要,您可以向元素添加多個(gè)相同類型的偵聽器。 例如,您可以多次在元素上調(diào)用 addEventListener(\'click\',function(){...}),并在第二個(gè)參數(shù)中指定不同的函數(shù)。 這對(duì)于事件處理程序?qū)傩允遣豢赡艿?,因?yàn)樵O(shè)置屬性的任何后續(xù)嘗試都將覆蓋先前的屬性,例如:

element.onclick = function1;
element.onclick = function2;
etc.

注意:如果您被要求支持早于Internet Explorer 8的瀏覽器,您可能會(huì)遇到困難,因?yàn)檫@些古老的瀏覽器在新的瀏覽器中使用不同的事件模型。 但不要害怕,大多數(shù)JavaScript庫(例如 jQuery )內(nèi)置了抽象出跨瀏覽器差異的函數(shù)。 在你的學(xué)習(xí)之旅中,不要擔(dān)心這一點(diǎn)。

其他事件概念

在本節(jié)中,我們將簡要介紹一些與事件相關(guān)的高級(jí)概念。 在這一點(diǎn)上完全理解這些并不重要,但它可能會(huì)解釋你可能會(huì)不時(shí)遇到的一些代碼模式。

事件對(duì)象

有時(shí)在一個(gè)事件處理程序函數(shù)中,你可能會(huì)看到一個(gè)以 event , evt 或簡單的 e 這稱為事件對(duì)象,它會(huì)自動(dòng)傳遞給事件處理程序,以提供額外的功能和信息。 例如,讓我們再次重寫我們的隨機(jī)顏色示例:

function bgChange(e) {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  e.target.style.backgroundColor = rndCol;
  console.log(e);
}  

btn.addEventListener('click', bgChange);

在這里,您可以看到我們在函數(shù)中包含一個(gè)事件對(duì)象 e ,并且在 e.target 上設(shè)置背景顏色樣式的函數(shù) - 這是按鈕 本身。 事件對(duì)象的 target 屬性始終是對(duì)事件剛剛發(fā)生的元素的引用。 所以在這個(gè)例子中,我們在按鈕上設(shè)置一個(gè)隨機(jī)的背景顏色,而不是頁面。

注意:您可以為事件對(duì)象使用任何您喜歡的名稱 - 您只需要選擇一個(gè)名稱,然后您可以在事件處理函數(shù)中引用它。 e / evt / event 是開發(fā)人員最常用的,因?yàn)樗鼈兒喍桃子洝?堅(jiān)持標(biāo)準(zhǔn)總是好的。

注意:您可以為事件對(duì)象使用任何您喜歡的名稱 - 您只需要選擇一個(gè)名稱,然后您可以在事件處理函數(shù)中引用它。 e / evt / event 是開發(fā)人員最常用的,因?yàn)樗鼈兒喍桃子洝?堅(jiān)持標(biāo)準(zhǔn)總是好的。

var divs = document.querySelectorAll('div');

for (var i = 0; i < divs.length; i++) {
  divs[i].onclick = function(e) {
    e.target.style.backgroundColor = bgChange();
  }
}

輸出如下(嘗試點(diǎn)擊它 - 有樂趣):

您將遇到的大多數(shù)事件處理程序只有一組屬性和函數(shù)(方法)可用于事件對(duì)象(請(qǐng)參閱API / Event"title ="Event接口表示在DOM中發(fā)生的任何事件;一些是用戶生成的(例如鼠標(biāo)或鍵盤事件),而其他是由API生成的(例如指示動(dòng)畫已完成的事件\">運(yùn)行,視頻已暫停,等等)。有很多類型的事件,其中一些使用的是基于主Event接口的其他接口。事件本身包含所有事件通用的屬性和方法。"> Event object reference for a full list).\"> Event 對(duì)象引用完整列表)。然而,一些更高級(jí)的處理程序添加專家屬性包含他們需要運(yùn)行的額外數(shù)據(jù)。Media Recorder API for example has a dataavailable event,\">例如,媒體記錄器API 具有 dataavailable 事件,當(dāng)一些音頻或視頻已經(jīng)被記錄并且可用于做某事(例如,保存它或者回放)時(shí)觸發(fā)。ondataavailable handler\'s event object has a data\">相應(yīng)的 ondataavailable 處理程序的事件對(duì)象具有 data 屬性可用包含錄制的音頻或視頻數(shù)據(jù),以允許您訪問它,并做一些與它。

防止默認(rèn)行為

有時(shí)候,你會(huì)遇到一個(gè)情況,你想要停止一個(gè)事件做默認(rèn)情況下做什么。 最常見的示例是Web表單,例如自定義注冊表單。 當(dāng)您填寫詳細(xì)信息并按提交按鈕時(shí),自然的行為是將數(shù)據(jù)提交到服務(wù)器上的指定頁面進(jìn)行處理,并將瀏覽器重定向到某種類型的"成功消息"頁面(或 同一頁,如果沒有指定另一個(gè)。)

麻煩來了,當(dāng)用戶沒有正確地提交數(shù)據(jù) - 作為開發(fā)人員,你會(huì)想要停止提交到服務(wù)器,并給他們一個(gè)錯(cuò)誤消息告訴他們什么是錯(cuò)誤,需要做什么,以妥善處理。 某些瀏覽器支持自動(dòng)表單數(shù)據(jù)驗(yàn)證功能,但由于許多瀏覽器不支持,因此建議您不要依賴這些功能,并實(shí)施自己的驗(yàn)證檢查。 讓我們來看一個(gè)簡單的例子。

首先,一個(gè)簡單的HTML表單,要求您輸入您的姓名:

<form>
  <div>
    <label for="fname">First name: </label>
    <input id="fname" type="text">
  </div>
  <div>
    <label for="lname">Last name: </label>
    <input id="lname" type="text">
  </div>
  <div>
     <input id="submit" type="submit">
  </div>
</form>
<p></p>

現(xiàn)在一些JavaScript - 這里我們在 onsubmit 事件處理程序中實(shí)現(xiàn)非常簡單的檢查 (提交事件在表單提交時(shí)觸發(fā)),測試文本字段是否為空。 如果是,我們調(diào)用 preventDefault() >對(duì)事件對(duì)象的函數(shù) - 這將停止提交表單 - 然后在我們的表單下面的段落中顯示一條錯(cuò)誤消息,告訴用戶出現(xiàn)了什么問題:

var form = document.querySelector('form');
var fname = document.getElementById('fname');
var lname = document.getElementById('lname');
var submit = document.getElementById('submit');
var para = document.querySelector('p');

form.onsubmit = function(e) {
  if (fname.value === '' || lname.value === '') {
    e.preventDefault();
    para.textContent = 'You need to fill in both names!';
  }
}

顯然,這是相當(dāng)弱的表單驗(yàn)證 - 它不會(huì)阻止用戶驗(yàn)證表單與空格或數(shù)字輸入到字段,例如 - 但它是確定為示例的目的。 輸出如下:

注意:有關(guān)完整的源代碼,請(qǐng)參閱 .html"class ="external"> preventdefault-validation.html (另見 -validation.html"class ="external">運(yùn)行)。

事件冒泡和捕獲

這里涵蓋的最后一個(gè)主題是你不會(huì)經(jīng)常碰到的,但如果你不明白它可能是一個(gè)真正的痛苦。 事件冒泡和捕獲是描述當(dāng)在一個(gè)元素上激活相同事件類型的兩個(gè)處理程序時(shí)發(fā)生什么的兩種機(jī)制。 讓我們來看一個(gè)例子,讓這更容易 - 打開 "external"> show-video-box.html example in a new tab(and the block / events / show-video-box.html"class ="external">源代碼)。它也可以在下面提供:

這是一個(gè)非常簡單的示例,用于顯示和隱藏 容器用于流內(nèi)容,并不固有地表示任何內(nèi)容。使用它來為元素分組,例如樣式(使用類或id屬性),用不同語言標(biāo)記文檔的一部分(使用lang屬性),等等 。"> < div> ="使用HTML視頻元素將視頻內(nèi)容嵌入到文檔中。"> < video> 元素:

<button>Display video</button>

<div class="hidden">
  <video>
    <source src="rabbit320.mp4" type="video/mp4">
    <source src="rabbit320.webm" type="video/webm">
    <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
  </video>
</div>

< 按鈕> ,通過將< div> 隱藏的類屬性更改為 (示例的CSS包含這兩個(gè)類,分別將框定位在屏幕和屏幕上):

btn.onclick = function() {
  videoBox.setAttribute('class', 'showing');
}

然后我們再添加一些 onclick 事件處理程序 - 第一個(gè)到< div> ,第二個(gè)到< video> 。 這種想法是,當(dāng)視頻之外的< div> 區(qū)域被點(diǎn)擊時(shí),框應(yīng)該再次隱藏; 當(dāng)視頻本身被點(diǎn)擊時(shí),視頻應(yīng)該開始播放。

videoBox.onclick = function() {
  videoBox.setAttribute('class', 'hidden');
};

video.onclick = function() {
  video.play();
};

但有一個(gè)問題 - 目前,當(dāng)您點(diǎn)擊開始播放的視頻,但它會(huì)導(dǎo)致< div> 也同時(shí)隱藏。 這是因?yàn)橐曨l位于< div> - 它是它的一部分 - 所以點(diǎn)擊視頻實(shí)際上運(yùn)行 以上事件處理程序。

Bubbling and capturing explained

當(dāng)對(duì)具有父元素的元素觸發(fā)事件時(shí)(例如 視頻元素在文檔中嵌入視頻內(nèi)容。"> < video> ),現(xiàn)代瀏覽器運(yùn)行兩個(gè)不同的階段 - 捕獲階段和冒泡階段。 在捕獲階段:

  • The browser checks to see if the element's outer-most ancestor (<html>) has an onclick event handler registered on it in the capturing phase, and runs it if so.
  • Then it moves on to the next element inside <html> and does the same thing, then the next one, and so on until it reaches the element that was actually clicked on.

在起泡階段,發(fā)生完全相反的情況:

  • The browser checks to see if the element that was actually clicked on has an onclick event handler registered on it in the bubbling phase, and runs it if so.
  • Then it moves on to the next immediate ancestor element and does the same thing, then the next one, and so on until it reaches the <html> element.

(點(diǎn)擊圖片查看大圖)

在現(xiàn)代瀏覽器中,默認(rèn)情況下,所有事件處理程序都在冒泡階段注冊。 因此,在我們當(dāng)前的示例中,當(dāng)您點(diǎn)擊視頻時(shí),點(diǎn)擊事件氣泡從< video> 元素向外到< html> 元素。 一路上:

  • It finds the video.onclick... handler and runs it, so the video first starts playing.
  • It then finds the videoBox.onclick... handler and runs it, so the video is hidden as well.

Fixing the problem with stopPropagation()

這是惱人的行為,但有一種方法來解決它! 標(biāo)準(zhǔn)事件對(duì)象具有可用的函數(shù),其名稱為 stopPropagation() > ,當(dāng)在處理程序的事件對(duì)象上調(diào)用時(shí),它使得處理程序運(yùn)行,但事件不會(huì)在鏈上冒泡,因此不會(huì)運(yùn)行更多的處理程序。

因此,我們可以通過將前面代碼塊中的第二個(gè)處理函數(shù)改為:

video.onclick = function(e) {
  e.stopPropagation();
  video.play();
};

您可以嘗試制作本地副本 "external"> show-video-box.html源代碼,并自己修復(fù)它,或查看固定的結(jié)果 區(qū)域/ javascript / building-blocks / events / show-video-box-fixed.html"class ="external"> show-video-box-fixed.html (另請(qǐng)參閱 //github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box-fixed.html"class ="external">源代碼)。

注意:為什么要同時(shí)捕獲和冒泡? 好吧,在糟糕的舊時(shí)代,瀏覽器交叉兼容性比現(xiàn)在少得多,Netscape只使用事件捕獲,而Internet Explorer只使用事件冒泡。 當(dāng)W3C決定嘗試標(biāo)準(zhǔn)化行為并達(dá)成共識(shí)時(shí),他們最終得到了包括兩者的系統(tǒng),這是一個(gè)現(xiàn)代瀏覽器實(shí)現(xiàn)的。

注意:如上所述,默認(rèn)情況下,所有事件處理程序都在冒泡階段注冊,這在大多數(shù)時(shí)間更有意義。 如果您真的想要在捕獲階段注冊一個(gè)事件,您可以使用 / EventTarget / addEventListener"> addEventListener() ,并將可選的第三個(gè)屬性設(shè)置為 true 。

Event delegation

冒泡也允許我們利用事件委托 - 這個(gè)概念依賴于這樣一個(gè)事實(shí),如果你想要一些代碼運(yùn)行時(shí),你點(diǎn)擊任何一個(gè)大量的子元素,你可以設(shè)置 事件監(jiān)聽器,并且對(duì)每個(gè)孩子具有事件監(jiān)聽器泡泡的效果,而不必對(duì)每個(gè)孩子單獨(dú)設(shè)置事件監(jiān)聽器。

一個(gè)很好的例子是一系列列表項(xiàng) - 如果你想讓每個(gè)人在點(diǎn)擊時(shí)彈出一個(gè)消息,你可以在父代碼< ul>上設(shè)置 click 事件監(jiān)聽器。 ,它會(huì)彈出列表項(xiàng)。

David Walsh的博客中進(jìn)一步解釋了這一概念,其中包含多個(gè)示例 - 請(qǐng)參見 JavaScript事件委托的工作原理。

結(jié)論

在這個(gè)早期階段,您現(xiàn)在應(yīng)該知道所有您需要了解的Web事件。 如上所述,事件不是核心JavaScript的一部分 - 它們是在瀏覽器Web API中定義的。

此外,重要的是要了解JavaScript使用的不同上下文往往有不同的事件模型 - 從Web API到其他領(lǐng)域,如瀏覽器WebExtensions和Node.js(服務(wù)器端JavaScript)。 我們不希望你現(xiàn)在了解所有這些領(lǐng)域,但它肯定有助于了解事件的基礎(chǔ)知識(shí),因?yàn)槟阍陂_發(fā)學(xué)習(xí)Web開發(fā)。

如果您有任何不明白的地方,請(qǐng)隨時(shí)閱讀本文,或與我們聯(lián)系以尋求幫助。

也可以看看

  • Event order (discussion of capturing and bubbling) — an excellently detailed piece by Peter-Paul Koch.
  • Event accessing (discussing of the event object) — another excellently detailed piece by Peter-Paul Koch.
  • Event reference

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)