<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 事件模型 ## 監聽函數 瀏覽器的事件模型,就是通過監聽函數(listener)對事件做出反應。事件發生后,瀏覽器監聽到了這個事件,就會執行對應的監聽函數。這是事件驅動編程模式(event-driven)的主要編程方式。 JavaScript 有三種方法,可以為事件綁定監聽函數。 ### HTML 的 on- 屬性 HTML 語言允許在元素的屬性中,直接定義某些事件的監聽代碼。 ```html <body onload="doSomething()"> <div onclick="console.log('觸發事件')"> ``` 上面代碼為`body`節點的`load`事件、`div`節點的`click`事件,指定了監聽代碼。一旦事件發生,就會執行這段代碼。 元素的事件監聽屬性,都是`on`加上事件名,比如`onload`就是`on + load`,表示`load`事件的監聽代碼。 注意,這些屬性的值是將會執行的代碼,而不是一個函數。 ```html <!-- 正確 --> <body onload="doSomething()"> <!-- 錯誤 --> <body onload="doSomething"> ``` 一旦指定的事件發生,`on-`屬性的值是原樣傳入 JavaScript 引擎執行。因此如果要執行函數,不要忘記加上一對圓括號。 使用這個方法指定的監聽代碼,只會在冒泡階段觸發。 ```html <div onclick="console.log(2)"> <button onclick="console.log(1)">點擊</button> </div> ``` 上面代碼中,`<button>`是`<div>`的子元素。`<button>`的`click`事件,也會觸發`<div>`的`click`事件。由于`on-`屬性的監聽代碼,只在冒泡階段觸發,所以點擊結果是先輸出`1`,再輸出`2`,即事件從子元素開始冒泡到父元素。 直接設置`on-`屬性,與通過元素節點的`setAttribute`方法設置`on-`屬性,效果是一樣的。 ```javascript el.setAttribute('onclick', 'doSomething()'); // 等同于 // <Element onclick="doSomething()"> ``` ### 元素節點的事件屬性 元素節點對象的事件屬性,同樣可以指定監聽函數。 ```javascript window.onload = doSomething; div.onclick = function (event) { console.log('觸發事件'); }; ``` 使用這個方法指定的監聽函數,也是只會在冒泡階段觸發。 注意,這種方法與 HTML 的`on-`屬性的差異是,它的值是函數名(`doSomething`),而不像后者,必須給出完整的監聽代碼(`doSomething()`)。 ### EventTarget.addEventListener() 所有 DOM 節點實例都有`addEventListener`方法,用來為該節點定義事件的監聽函數。 ```javascript window.addEventListener('load', doSomething, false); ``` `addEventListener`方法的詳細介紹,參見`EventTarget`章節。 ### 小結 上面三種方法,第一種“HTML 的 on- 屬性”,違反了 HTML 與 JavaScript 代碼相分離的原則,將兩者寫在一起,不利于代碼分工,因此不推薦使用。 第二種“元素節點的事件屬性”的缺點在于,同一個事件只能定義一個監聽函數,也就是說,如果定義兩次`onclick`屬性,后一次定義會覆蓋前一次。因此,也不推薦使用。 第三種`EventTarget.addEventListener`是推薦的指定監聽函數的方法。它有如下優點: - 同一個事件可以添加多個監聽函數。 - 能夠指定在哪個階段(捕獲階段還是冒泡階段)觸發監聽函數。 - 除了 DOM 節點,其他對象(比如`window`、`XMLHttpRequest`等)也有這個接口,它等于是整個 JavaScript 統一的監聽函數接口。 ## this 的指向 監聽函數內部的`this`指向觸發事件的那個元素節點。 ```html <button id="btn" onclick="console.log(this.id)">點擊</button> ``` 執行上面代碼,點擊后會輸出`btn`。 其他兩種監聽函數的寫法,`this`的指向也是如此。 ```javascript // HTML 代碼如下 // <button id="btn">點擊</button> var btn = document.getElementById('btn'); // 寫法一 btn.onclick = function () { console.log(this.id); }; // 寫法二 btn.addEventListener( 'click', function (e) { console.log(this.id); }, false ); ``` 上面兩種寫法,點擊按鈕以后也是輸出`btn`。 ## 事件的傳播 一個事件發生后,會在子元素和父元素之間傳播(propagation)。這種傳播分成三個階段。 - **第一階段**:從`window`對象傳導到目標節點(上層傳到底層),稱為“捕獲階段”(capture phase)。 - **第二階段**:在目標節點上觸發,稱為“目標階段”(target phase)。 - **第三階段**:從目標節點傳導回`window`對象(從底層傳回上層),稱為“冒泡階段”(bubbling phase)。 這種三階段的傳播模型,使得同一個事件會在多個節點上觸發。 ```html <div> <p>點擊</p> </div> ``` 上面代碼中,`<div>`節點之中有一個`<p>`節點。 如果對這兩個節點,都設置`click`事件的監聽函數(每個節點的捕獲階段和冒泡階段,各設置一個監聽函數),共計設置四個監聽函數。然后,對`<p>`點擊,`click`事件會觸發四次。 ```javascript var phases = { 1: 'capture', 2: 'target', 3: 'bubble' }; var div = document.querySelector('div'); var p = document.querySelector('p'); div.addEventListener('click', callback, true); p.addEventListener('click', callback, true); div.addEventListener('click', callback, false); p.addEventListener('click', callback, false); function callback(event) { var tag = event.currentTarget.tagName; var phase = phases[event.eventPhase]; console.log("Tag: '" + tag + "'. EventPhase: '" + phase + "'"); } // 點擊以后的結果 // Tag: 'DIV'. EventPhase: 'capture' // Tag: 'P'. EventPhase: 'target' // Tag: 'P'. EventPhase: 'target' // Tag: 'DIV'. EventPhase: 'bubble' ``` 上面代碼表示,`click`事件被觸發了四次:`<div>`節點的捕獲階段和冒泡階段各1次,`<p>`節點的目標階段觸發了2次。 1. 捕獲階段:事件從`<div>`向`<p>`傳播時,觸發`<div>`的`click`事件; 2. 目標階段:事件從`<div>`到達`<p>`時,觸發`<p>`的`click`事件; 3. 冒泡階段:事件從`<p>`傳回`<div>`時,再次觸發`<div>`的`click`事件。 其中,`<p>`節點有兩個監聽函數(`addEventListener`方法第三個參數的不同,會導致綁定兩個監聽函數),因此它們都會因為`click`事件觸發一次。所以,`<p>`會在`target`階段有兩次輸出。 注意,瀏覽器總是假定`click`事件的目標節點,就是點擊位置嵌套最深的那個節點(本例是`<div>`節點里面的`<p>`節點)。所以,`<p>`節點的捕獲階段和冒泡階段,都會顯示為`target`階段。 事件傳播的最上層對象是`window`,接著依次是`document`,`html`(`document.documentElement`)和`body`(`document.body`)。也就是說,上例的事件傳播順序,在捕獲階段依次為`window`、`document`、`html`、`body`、`div`、`p`,在冒泡階段依次為`p`、`div`、`body`、`html`、`document`、`window`。 ## 事件的代理 由于事件會在冒泡階段向上傳播到父節點,因此可以把子節點的監聽函數定義在父節點上,由父節點的監聽函數統一處理多個子元素的事件。這種方法叫做事件的代理(delegation)。 ```javascript var ul = document.querySelector('ul'); ul.addEventListener('click', function (event) { if (event.target.tagName.toLowerCase() === 'li') { // some code } }); ``` 上面代碼中,`click`事件的監聽函數定義在`<ul>`節點,但是實際上,它處理的是子節點`<li>`的`click`事件。這樣做的好處是,只要定義一個監聽函數,就能處理多個子節點的事件,而不用在每個`<li>`節點上定義監聽函數。而且以后再添加子節點,監聽函數依然有效。 如果希望事件到某個節點為止,不再傳播,可以使用事件對象的`stopPropagation`方法。 ```javascript // 事件傳播到 p 元素后,就不再向下傳播了 p.addEventListener('click', function (event) { event.stopPropagation(); }, true); // 事件冒泡到 p 元素后,就不再向上冒泡了 p.addEventListener('click', function (event) { event.stopPropagation(); }, false); ``` 上面代碼中,`stopPropagation`方法分別在捕獲階段和冒泡階段,阻止了事件的傳播。 但是,`stopPropagation`方法只會阻止事件的傳播,不會阻止該事件觸發`<p>`節點的其他`click`事件的監聽函數。也就是說,不是徹底取消`click`事件。 ```javascript p.addEventListener('click', function (event) { event.stopPropagation(); console.log(1); }); p.addEventListener('click', function(event) { // 會觸發 console.log(2); }); ``` 上面代碼中,`p`元素綁定了兩個`click`事件的監聽函數。`stopPropagation`方法只能阻止這個事件的傳播,不能取消這個事件,因此,第二個監聽函數會觸發。輸出結果會先是1,然后是2。 如果想要徹底取消該事件,不再觸發后面所有`click`的監聽函數,可以使用`stopImmediatePropagation`方法。 ```javascript p.addEventListener('click', function (event) { event.stopImmediatePropagation(); console.log(1); }); p.addEventListener('click', function(event) { // 不會被觸發 console.log(2); }); ``` 上面代碼中,`stopImmediatePropagation`方法可以徹底取消這個事件,使得后面綁定的所有`click`監聽函數都不再觸發。所以,只會輸出1,不會輸出2。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看