<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          要小心 JavaScript 的事件代理

          共 2401字,需瀏覽 5分鐘

           ·

          2021-04-14 09:08

          我們知道,如果給 form 里面的 button 元素綁定事件,需要考慮它是否會觸發(fā) form 的 submit 行為。除此之外,其它場合給 button 元素綁定事件,你幾乎不用擔心這個事件會有什么非預期的附加效果,很自然地會這樣寫事件處理代碼:

          var button = document.querySelector('button')
          button.addEventListener('click', function (e) {
          console.log('點擊了按鈕')
          })

          你之所以放心這么寫,是因為這個 button 元素沒有使用事件代理,即沒有代理任何子元素的事件。

          事件代理的意思是,你要為一個元素綁定事件,但你不是直接把事件綁定到這個元素自己身上,而是綁定到這個元素的父元素上。當子元素的某個事件(比如點擊事件)觸發(fā)時,它的父元素相同的事件也會觸發(fā)(我們常說的事件冒泡),此時我們說父元素代理了子元素的事件。

          舉個例子,比如一個 button 元素中包含一個齒輪圖標:

          <button>
          <svg>
          <use xlink:href="#gear"></use>
          </svg>
          </button>

          當用戶點擊齒輪圖標,必然要觸發(fā) click 事件,但你并不會直接綁定事件到 svg 或 use 元素上,而是綁定到它們的父元素 button 上。即:

          document.querySelector('button').addEventListener('click', function (e) {
          console.log('點擊了按鈕')
          })

          這種情況,我們可以說,button 元素代理了它的所有子元素的 click 事件。

          但是,出現(xiàn)這種事件代理的情況時,我們就得小心了。

          為了更直觀地說明問題,我們把“父”元素上升到頂層的 document 元素:

          document.documentElement.addEventListener('click', function (e) {
          console.log('我被點擊了')
          })

          只要網(wǎng)頁中任意一個位置被點擊了,都會觸發(fā)綁定在 document 元素上的點擊事件。想要知道事件具體是發(fā)生在哪個元素上面,可以通過事件對象提供的 target 屬性來判斷。

          document.documentElement.addEventListener('click', function (e) {
          console.log(e.target)
          })

          我們很容易知道事件具體是發(fā)生在哪個元素身上的。于是在上面的示例中,如果父元素 document 想在按鈕被點擊時做點什么事情,我們很自然地會這么寫:

          document.documentElement.addEventListener('click', function (e) {
          if (e.target.tagName === 'BUTTON') {
          console.log('按鈕被點擊了')
          }
          })

          這時問題就出現(xiàn)了,按鈕即使被點擊了 if 條件也不一定成立,即也不一定會輸出“按鈕被點擊了”。因為用戶在按鈕上的某個位置點擊了,根據(jù)用戶點擊的位置,e.target 可能是下面三種情況:

          • BUTTON 元素

          • SVG 元素

          • USE 元素

          實際的情況是這樣的:

          我們真正的意圖是,只要點擊是發(fā)生在按鈕上面,不論是按鈕的哪個位置,我們都應視為按鈕被點擊了。嗯,簡單,我們再改一下,這樣寫:

          document.documentElement.addEventListener('click', function (e) {
          if (['BUTTON', 'SVG', 'USE'].includes(e.target.tagName)) {
          // 點擊的是按鈕
          }
          })

          這樣似乎沒什么問題,也確實可以達到目的,但看上去總是有些別扭。因為這種情況對于最上層的 document 來說,得知道每個子元素的情況,本來我只需要關心離我最近的 button 元素就可以了。

          根據(jù) OOP 對內(nèi)封裝的思想,button 元素內(nèi)部的事情應該在內(nèi)部消化掉,其子元素對外不可見,應該只暴露 button 元素本身。依據(jù)這個思想和事件冒泡的特點,我們就有了比較好的解決辦法:只需要禁止 button 內(nèi)部元素的事件響應(包括事件冒泡)而只允許 button 元素本身的事件發(fā)生就行。有兩種方式可以實現(xiàn)這個目的。

          一種是使用 CSS 禁止 button 內(nèi)部元素的事件響應:

          button > * {
          pointer-events: none;
          }

          另一種是使用 JS 來阻止 button 內(nèi)部元素的事件響應(包括事件冒泡):

          document.querySelector('button > svg').addEventListener('click', function (e) {
          e.stopPropagation()
          e.preventDefault()
          })

          document.querySelector('button').addEventListener('click', function (e) {
          console.log(e.target.tagName)
          })

          這兩種方式都能達到我們預期的效果:

          綜上,針對特定元素進行事件處理時,如果該元素有事件代理的情況,就要小心處理它所代理的子元素。

          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉手机电影网 | 思久久精品 | 亚洲欧美精品性爱 | 影音先锋中文资源网 | 日韩精品网站 |