> 웹 프론트엔드 > JS 튜토리얼 > JavaScript의 이벤트 흐름 소개

JavaScript의 이벤트 흐름 소개

coldplay.xixi
풀어 주다: 2021-02-14 10:30:59
앞으로
2701명이 탐색했습니다.

ㅋㅋㅋ

JavaScript의 이벤트 흐름 소개이벤트 스트림이란 무엇인가요?

이벤트 버블링 vs 이벤트 캡처

DOM 이벤트 분류DOM0 이벤트DOM2 이벤트

결론

서문
  • 이 글을 읽기 전에 먼저 JavaScript 이벤트를 살펴보는 것이 좋습니다. 물론, 이미 알고 있는 사람들에게는 루프가 필요하지 않습니다. 이 기사에서는 js의 이벤트 흐름에 대해 설명합니다.
  • Text
    • 우리 모두는 클릭, 슬라이딩 등과 같은 웹 페이지에서 특정 유형의 작업을 수행할 때 일부 해당 이벤트가 트리거된다는 것을 알고 있습니다. 우리는 또한 전체 웹 페이지가 실제로 브라우저에 의해 DOM 트리로 구문 분석된다는 것을 알고 있습니다. 노드가 이벤트를 생성하면 이벤트는 노드와 루트 노드 사이에 일정한 순서로 전파되며, 이 전파 경로를 통과하는 모든 노드는 이벤트를 수신하게 됩니다. 이 전체 프로세스를
    • DOM 이벤트 흐름
    • 이라고 합니다.
    • 이벤트란 무엇인가요?
    • js와 html의 상호작용은 실제로 "이벤트"를 통해 구현됩니다. 웹 페이지의 모든 사용자 클릭, 선택, 슬라이드 등은 모두 js 세계의 이벤트입니다.
    • 이벤트의 경우 이벤트가 발생하면 응답이 있어야 합니다. JS에서는 소위 응답이 리스너입니다. 관찰자 패턴과 마찬가지로 이벤트가 우리의 주체이고, 이벤트가 발생하면 해당 이벤트(주체)에 해당하는 모든 리스너에게 알려야 해당 응답을 수행하게 됩니다.
  • 이벤트 흐름이란 무엇인가요?

이벤트 흐름은 페이지에서 이벤트가 수신되는 순서를 설명합니다. 크게 두 가지로 나누어진다.

IE의 이벤트 버블링

넷스케이프의 이벤트 캡쳐

이벤트 버블링 vs 이벤트 캡쳐

IE가 제안하는 이벤트 흐름 모델은 이벤트 버블링, 즉 아래에서 위로(Bottom to Top)이다. , 대상에 의해 트리거된 요소는 문서 개체까지 단계적으로 위쪽으로 전파됩니다.

넷스케이프가 제안하는 이벤트 흐름 모델은 이벤트 버블링과 달리 위에서 아래로, 즉 문서 개체에서 대상 개체로 단계적으로 전파됩니다.

위는 DOM0 표준에 따른 이벤트 흐름 메커니즘입니다. 나중에 ECMAScript는 DOM2의 이벤트 흐름을 더욱 표준화했습니다. DOM2에서는 이벤트에 포함된 이벤트 흐름을 다음 세 단계로 구분합니다.

이벤트 캡처 단계(capture)

대상 단계(target)

이벤트 버블링 단계(bubble)
DOM 이벤트 분류

DOM 노드에서 이벤트가 발생하면 당연히 이에 따라 처리해야 하며 DOM 이벤트는 다음과 같이 4가지 레벨로 구분됩니다.


그 중에서 더 중요한 것은 DOM0/DOM2이므로 다음은 이에 초점을 맞춘 것입니다.

DOM0 이벤트

DOM0 레벨 이벤트는 주로 두 가지 방식으로 구현됩니다. 첫 번째는 html 태그에서 이벤트 속성의 속성 값으로 함수 이름을 직접 사용하는 인라인 모델입니다. 다음과 같습니다:

// 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>
    로그인 후 복사
  1. 클릭하면 Hello World가 표시됩니다. 문제 없습니다. 그러나 스크립트 모델에도 단점이 있습니다. 위의 HTML 코드를 기반으로 다음과 같이 약간의 js를 추가합니다.
  2. // 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
    로그인 후 복사
  3. 이제 Hello World again을 클릭하면 나타나는 것을 확인했습니다. 따라서 스크립트 모델에서는 하나의 노드가 동일한 유형의 이벤트를 한 번만 추가하도록 허용하고 후속 노드는 이전 이벤트를 덮어씁니다.
  4. 마지막으로 흥미로운 예를 살펴보겠습니다.
    <!-- 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 수준 이벤트 핸들러가 있습니다. 두 가지 메소드가 정의됩니다.

  • addEventListener() 이벤트 리스너 추가
  • removeEventListener() 이벤트 리스너 제거

두 함수 모두 아래 표에 표시된 대로 세 개의 매개변수를 갖습니다.

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:csdn.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿