ㅋㅋㅋ
이벤트 스트림이란 무엇인가요?
이벤트 버블링 vs 이벤트 캡처결론DOM 이벤트 분류DOM0 이벤트DOM2 이벤트
서문
이벤트 흐름은 페이지에서 이벤트가 수신되는 순서를 설명합니다. 크게 두 가지로 나누어진다.
IE의 이벤트 버블링넷스케이프의 이벤트 캡쳐이벤트 버블링 vs 이벤트 캡쳐
IE가 제안하는 이벤트 흐름 모델은 이벤트 버블링, 즉 아래에서 위로(Bottom to Top)이다. , 대상에 의해 트리거된 요소는 문서 개체까지 단계적으로 위쪽으로 전파됩니다.
넷스케이프가 제안하는 이벤트 흐름 모델은 이벤트 버블링과 달리 위에서 아래로, 즉 문서 개체에서 대상 개체로 단계적으로 전파됩니다.
위는 DOM0 표준에 따른 이벤트 흐름 메커니즘입니다. 나중에 ECMAScript는 DOM2의 이벤트 흐름을 더욱 표준화했습니다. DOM2에서는 이벤트에 포함된 이벤트 흐름을 다음 세 단계로 구분합니다.
이벤트 캡처 단계(capture)
대상 단계(target) 이벤트 버블링 단계(bubble)DOM 노드에서 이벤트가 발생하면 당연히 이에 따라 처리해야 하며 DOM 이벤트는 다음과 같이 4가지 레벨로 구분됩니다.
그 중에서 더 중요한 것은 DOM0/DOM2이므로 다음은 이에 초점을 맞춘 것입니다.
DOM0 이벤트
// js code// eventDOM.jsfunction btnClick() { console.log('Hello World')}
<!-- html code --><html> <head> <title>eventDOM demo</title> </head> <body> <p onclick="btnClick()" style="width: 30px; height: 30px; background-color:black"></p> <script type="text/javascript" src="eventDOM.js"></script> </body></html>그러나 인라인 모델에는 명백한 단점이 있습니다. 즉, 콘텐츠(html)와 동작(js) 분리에 대한 W3C 요구 사항을 위반한다는 것입니다. 따라서 두 번째 유형인 스크립트 모델(동적 바인딩 모델)이 있습니다. 구체적인 방법은 js 스크립트를 통해 특정 DOM 노드를 선택한 후 해당 노드에 이벤트 속성과 속성값을 추가하는 것이다. 다음과 같이
// js code// eventDOM.jslet btn = document.getElementById('btn')let btnClick = function() { console.log('Hello World')}btn.onclick = btnClick
<!-- html code --><html> <head> <title>eventDOM demo</title> </head> <body> <p id="btn" style="width: 30px; height: 30px; background-color:black"></p> <script type="text/javascript" src="eventDOM.js"></script> </body></html>
Hello World
가 표시됩니다. 문제 없습니다. 그러나 스크립트 모델에도 단점이 있습니다. 위의 HTML 코드를 기반으로 다음과 같이 약간의 js를 추가합니다. // js code// eventDOM.jslet btn = document.getElementById('btn')let btnClick = function() { console.log('Hello World')}let btnClick2 = function() { console.log('Hello World again')}btn.onclick = btnClick btn.onclick = btnClick2
Hello World again
을 클릭하면 나타나는 것을 확인했습니다. 따라서 스크립트 모델에서는 하나의 노드가 동일한 유형의 이벤트를 한 번만 추가하도록 허용하고 후속 노드는 이전 이벤트를 덮어씁니다. <!-- html code --><html> <head> <title>eventDOM demo</title> </head> <body> <p id="btn3" style="width: 400px; height: 400px; background-color:pink"> btn3 <p id="btn2" style="width: 300px; height: 300px; background-color:skyblue"> btn2 <p id="btn1" style="width: 200px; height: 200px; background-color:lightgreen"> btn1 </p> </p> </p> <script type="text/javascript" src="eventDOM.js"></script> </body></html>
// js code// eventDOM.jslet btn1 = document.getElementById('btn1')let btn2 = document.getElementById('btn2')let btn3 = document.getElementById('btn3')btn1.onclick = function() { console.log('1')}btn2.onclick = function() { console.log('2')}btn3.onclick = function() { console.log('3')}
btn3을 클릭하면 출력은 다음과 같습니다.
예상대로인데 btn1을 클릭하면 어떻게 될까요? 출력은 다음과 같습니다.
확실히 btn1에 대해 하나의 리스너만 추가한 것은 좀 이상합니다. , 왜 btn2와 btn3이 합쳐진 것처럼 느껴지나요? 그 이유는 DOM0에는 IE에서 제안한 이벤트 버블링과 Netscape에서 제안한 이벤트 캡처라는 두 가지 모델이 있지만 실제로 DOM0은 이벤트 버블링만 지원하기 때문입니다. 따라서 btn1을 클릭하는 이벤트 흐름은 다음과 같습니다.
즉, 이벤트 흐름도 btn2와 btn3을 통과하므로 이들의 이벤트 처리가 시작됩니다. 그러나 분명히 이것은 우리가 원하는 결과가 아닙니다.
DOM2 events
더 자세히 설명하면 DOM2 수준 이벤트 핸들러가 있습니다. 두 가지 메소드가 정의됩니다.
두 함수 모두 아래 표에 표시된 대로 세 개의 매개변수를 갖습니다.
Parameters | type | Description |
---|---|---|
event | String | '클릭'과 같은 모니터링되는 이벤트의 이름입니다. 여기서는 "on"할 필요가 없습니다 |
callback | function | 이벤트를 트리거하기 위해 실행할 콜백 함수 |
useCapture | Boolean(기본값:false) | 이벤트가 실행되는지 여부 캡처 단계에서 처리되었습니다 |
DOM2 中就可以對同一個節點綁定兩個以上的同類型事件監聽器了,看看下面例子:
<!-- html code --><html> <head> <title>eventDOM demo</title> </head> <body> <p id="btn" style="width: 200px; height: 200px; background-color:lightgreen"></p> <script type="text/javascript" src="eventDOM.js"></script> </body></html>
// js code// eventDOM.jslet btn = document.getElementById('btn')function hello() { console.log("Hello World")}function helloAgain() { console.log("Hello World again")}btn.addEventListener('click', hello, false)btn.addEventListener('click', helloAgain, false)
輸出如下:
解決了 DOM0 只能綁定一個同類型事件監聽器的缺點啦!值得注意的是,如果綁定同樣的監聽器兩次以上,仍然只會觸發一次。
再回到上面三個 p 的那個例子進行改寫如下:
<!-- html code --><html> <head> <title>eventDOM demo</title> </head> <body> <p id="btn3" style="width: 400px; height: 400px; background-color:pink"> btn3 <p id="btn2" style="width: 300px; height: 300px; background-color:skyblue"> btn2 <p id="btn1" style="width: 200px; height: 200px; background-color:lightgreen"> btn1 </p> </p> </p> <script type="text/javascript" src="eventDOM.js"></script> </body></html>
// js code// eventDOM.jslet btn1 = document.getElementById('btn1')let btn2 = document.getElementById('btn2')let btn3 = document.getElementById('btn3')btn1.addEventListener('click', function() { console.log('1')}, true)btn2.addEventListener('click', function() { console.log('2')}, true)btn3.addEventListener('click', function() { console.log('3')}, true)
注意,這邊我們把 addEventListener
的第三個參數設為 true,也就是事件會在捕獲階段觸發。點擊 btn1 輸出如下:
看到順序與 DOM0 的順序反過來了。首先最外層(btn3)的節點先被觸發了,而因為第三個參數被設為 true,事件會在捕獲階段就被處理,所以輸出才會是 3,2,1。如果都是 false,就會是 1,2,3。
可見 DOM2 的事件處理機制有了更彈性的操作空間。我們也可以在不同階段綁定事件監聽器,看看下面例子:
<!-- 沿用上一段 html 代碼 -->
// js code// eventDOM.jsbtn1.addEventListener('click',function() { console.log('btn1 capture')}, true)btn1.addEventListener('click',function() { console.log('btn1 bubble')}, false)btn2.addEventListener('click',function() { console.log('btn2 capture')}, true)btn2.addEventListener('click',function() { console.log('btn2 bubble')}, false)btn3.addEventListener('click',function() { console.log('btn3 capture')}, true)btn3.addEventListener('click',function() { console.log('btn3 bubble')}, false)
點擊 btn1 輸出如下:
改變一下順序,如下:
<!-- 沿用上一段 html 代碼 -->
// js code// eventDOM.jsbtn1.addEventListener('click',function() { console.log('btn1 bubble')}, false)btn1.addEventListener('click',function() { console.log('btn1 capture')}, true)btn2.addEventListener('click',function() { console.log('btn2 bubble')}, false)btn2.addEventListener('click',function() { console.log('btn2 capture')}, true)btn3.addEventListener('click',function() { console.log('btn3 bubble')}, false)btn3.addEventListener('click',function() { console.log('btn3 capture')}, true)
點擊 btn1 輸出如下:
注意 btn1 的輸出。雖然捕獲階段先發生了,但是因為 btn1 本身就是目標節點,所以在這種情況下,總結出規律:在目標元素上不區分冒泡還是捕獲,是根據腳本中的順序來執行。
有時候,我們希望對於某節點,不要再經過冒泡階段了,DOM2 也提供了相應函數,stopPropagation
。
<!-- 沿用上一段 html 代碼 -->
// js code// eventDOM.jsbtn1.addEventListener('click',function() { console.log('btn1 capture')}, true)btn1.addEventListener('click',function() { console.log('btn1 bubble')}, false)btn2.addEventListener('click',function(e) { e.stopPropagation() console.log('btn2 capture')}, true)btn2.addEventListener('click',function() { console.log('btn2 bubble')}, false)btn3.addEventListener('click',function() { console.log('btn3 capture')}, true)btn3.addEventListener('click',function() { console.log('btn3 bubble')}, false)
點擊 btn1 輸出如下:
可以看到,因為我們在 btn2 的捕獲階段就阻止了 btn2 的冒泡階段,所以 btn2 在捕獲後就不再繼續執行下去,確保不會冒泡,事件流如下:
加強一下前面提到的知識點,如果是在 btn1 阻止冒泡,會變成怎樣呢?
<!-- 沿用上一段 html 代碼 -->
// js code// eventDOM.jsbtn1.addEventListener('click',function(e) { e.stopPropagation() console.log('btn1 capture')}, true)btn1.addEventListener('click',function() { console.log('btn1 bubble')}, false)btn2.addEventListener('click',function() { console.log('btn2 capture')}, true)btn2.addEventListener('click',function() { console.log('btn2 bubble')}, false)btn3.addEventListener('click',function() { console.log('btn3 capture')}, true)btn3.addEventListener('click',function() { console.log('btn3 bubble')}, false)
點擊 btn1 輸出如下:
雖然我們對 btn1 阻止了冒泡,但是為什麼還是輸出了 btn bubble
呢?原因就是前面提到了,目標節點不區分 捕獲/冒泡 階段,但是後面也就不會繼續冒泡了,算是個比較特殊的情況,可以稍微留意下。
結語
本篇大致介紹了 js 的事件流的各種模型以及階段上的工作任務,個人認為應該還算詳細。雖然個人感覺好像對編程本身沒有太明顯的幫助,但是還是算是 js 的一個重要的知識點,學習下也沒甚麼不好。若內容有誤,還歡迎指點!
相关免费学习推荐:javascript(视频)
위 내용은 JavaScript의 이벤트 흐름 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!