Skip to content

Latest commit

ย 

History

History
291 lines (242 loc) ยท 10.6 KB

event-propagation.md

File metadata and controls

291 lines (242 loc) ยท 10.6 KB

์ด๋ฒคํŠธ ์ „ํŒŒ

์ž‘์„ฑ์ž : ์ด์Šฌ๊ธฐ


1. ์ด๋ฒคํŠธ ์ „ํŒŒ๋ž€?

  • DOM ํŠธ๋ฆฌ ์ƒ์— ์กด์žฌํ•˜๋Š” DOM ์š”์†Œ ๋…ธ๋“œ์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋Š” DOM ํŠธ๋ฆฌ๋ฅผ ํ†ตํ•ด ์ „ํŒŒ๋œ๋‹ค. ์ด๋ฅผ ์ด๋ฒคํŠธ ์ „ํŒŒ๋ผ ํ•œ๋‹ค.
    <html>
      <body>
        <ul id="home">
          <li id="conan">Conan</li>
          <li id="mocha">Mocha</li>
          <li id="stuckyi">Stuckyi</li>
        </ul>
      </body>
    </html>
    • ์œ„์˜ ์˜ˆ์ œ์—์„œ ul์š”์†Œ์˜ ๋‘ ๋ฒˆ์งธ ์ž์‹ ์š”์†Œ์ธ li๋ฅผ ํด๋ฆญํ•˜๋ฉด ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด ๋•Œ ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ DOM์š”์†Œ์ธ ์ด๋ฒคํŠธ ํƒ€๊นƒ์„ ์ค‘์‹ฌ์œผ๋กœ DOM ํŠธ๋ฆฌ๋ฅผ ํ†ตํ•ด ์ „ํŒŒ๋œ๋‹ค.
    • ์ด๋ฒคํŠธ ์ „ํŒŒ๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ „ํŒŒ๋˜๋Š” ๋ฐฉํ–ฅ์— ๋”ฐ๋ผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด 3๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. ์ด๋ฒคํŠธ ์ „ํŒŒ
      • ์บก์ฒ˜๋ง ๋‹จ๊ณ„(capturing phase) : ์ด๋ฒคํŠธ๊ฐ€ ์ƒ์œ„ ์š”์†Œ์—์„œ ํ•˜์œ„ ์š”์†Œ ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ
      • ํƒ€๊นƒ ๋‹จ๊ณ„(target phase) : ์ด๋ฒคํŠธ๊ฐ€ ์ด๋ฒคํŠธ ํƒ€๊นƒ์— ๋„๋‹ฌ
      • ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„(bubbling phase) : ์ด๋ฒคํŠธ๊ฐ€ ํ•˜์œ„ ์š”์†Œ์—์„œ ์ƒ์œ„ ์š”์†Œ ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ

2. ์ด๋ฒคํŠธ ๋‹จ๊ณ„ ํ™•์ธ

  • ul ์š”์†Œ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๊ณ  ul ์š”์†Œ์˜ ํ•˜์œ„ ์š”์†Œ์ธ li์š”์†Œ๋ฅผ ํด๋ฆญํ•˜์—ฌ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ณด์ž.

    <html>
    <body>
        <ul id="home">
        <li id="conan">Conan</li>
        <li id="mocha">Mocha</li>
        <li id="stuckyi">Stuckyi</li>
        </ul>
    </body>
    
    <script>
        const home = document.getElementById("home");
    
        //home ์š”์†Œ์˜ ํ•˜์œ„ ์š”์†Œ์ธ li๋ฅผ ํด๋ฆญํ•œ ๊ฒฝ์šฐ
        home.addEventListener("click", (e) => {
        console.log(`์ด๋ฒคํŠธ ๋‹จ๊ณ„: ${e.eventPhase}`);
        //์ด๋ฒคํŠธ ๋‹จ๊ณ„: 3 : ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„
        console.log(`์ด๋ฒคํŠธ ํƒ€๊นƒ: ${e.target}`);
        //[object HTMLLIElement]
        console.log(`์ปค๋ŸฐํŠธ ํƒ€๊นƒ: ${e.currentTarget}`);
        //[object HTMLUListElement]
        });
    </script>
    </html>
    • li ์š”์†Œ๋ฅผ ํด๋ฆญํ•˜๋ฉด ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ํด๋ฆญ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ํด๋ฆญ๋œ li ์š”์†Œ๊ฐ€ ์ด๋ฒคํŠธ ํƒ€๊นƒ์ด ๋œ๋‹ค.
    • ์ด ๋•Œ ํด๋ฆญ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” window์—์„œ ์‹œ์ž‘ํ•ด์„œ ์ด๋ฒคํŠธ ํƒ€๊นƒ์œผ๋กœ ์ „ํŒŒ๋œ๋‹ค. (์บก์ณ๋ง ๋‹จ๊ณ„)
    • ์ด ํ›„ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ์ด๋ฒคํŠธ ํƒ€๊นƒ์— ๋„์ฐฉํ•œ๋‹ค. (ํƒ€๊นƒ ๋‹จ๊ณ„)
    • ์ด ํ›„ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ ํƒ€๊นƒ์—์„œ ์‹œ์ž‘ํ•ด์„œ window ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ๋œ๋‹ค. (๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„)
  • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ/ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹์œผ๋กœ ๋“ฑ๋กํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ํƒ€๊นƒ ๋‹จ๊ณ„์™€ ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„๋งŒ ์บ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, addEventListener ๋ฉ”์„œ๋“œ ๋ฐฉ์‹์œผ๋กœ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ํƒ€๊นƒ ๋‹จ๊ณ„์™€ ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„ ๋ฟ ์•„๋‹ˆ๋ผ ์บก์ณ๋ง ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋„ ์„ ๋ณ„์ ์œผ๋กœ ์บ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์บก์ฒ˜๋ง ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•˜์—ฌ๋ฉด addEventListener ๋ฉ”์„œ๋“œ์˜ 3๋ฒˆ์งธ ์ธ์ˆ˜๋กœ true์‘ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค. 3๋ฒˆ์งธ ์ธ์ˆ˜๋ฅผ ์ƒ๋žตํ•˜๊ฑฐ๋‚˜ false๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ํƒ€๊นƒ ๋‹จ๊ณ„์™€ ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋งŒ ์บ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

    <html>
        <body>
            <ul id="home">
            <li id="conan">Conan</li>
            <li id="mocha">Mocha</li>
            <li id="stuckyi">Stuckyi</li>
            </ul>
        </body>
      <script>
          const home = document.getElementById("home");
          const mocha = document.getElementById("mocha");
    
          //home ์š”์†Œ์˜ ํ•˜์œ„ ์š”์†Œ์ธ li๋ฅผ ํด๋ฆญํ•œ ๊ฒฝ์šฐ ์บก์ณ๋ง ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•œ๋‹ค.
          home.addEventListener("click", (e) => {
              console.log(`์ด๋ฒคํŠธ ๋‹จ๊ณ„: ${e.eventPhase}`);
              //์ด๋ฒคํŠธ ๋‹จ๊ณ„: 1 : ์บก์ณ๋ง ๋‹จ๊ณ„
              console.log(`์ด๋ฒคํŠธ ํƒ€๊นƒ: ${e.target}`);
              //์ด๋ฒคํŠธ ํƒ€๊นƒ: [object HTMLLIElement]
              console.log(`์ปค๋ŸฐํŠธ ํƒ€๊นƒ: ${e.currentTarget}`);
              //์ปค๋ŸฐํŠธ ํƒ€๊นƒ: [object HTMLUListElement]
          },true);
    
          //ํƒ€๊นƒ ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•œ๋‹ค.
          mocha.addEventListener("click", (e) => {
              console.log(`์ด๋ฒคํŠธ ๋‹จ๊ณ„: ${e.eventPhase}`);
              //์ด๋ฒคํŠธ ๋‹จ๊ณ„: 2 : ํƒ€๊นƒ ๋‹จ๊ณ„
              console.log(`์ด๋ฒคํŠธ ํƒ€๊นƒ: ${e.target}`);
              //์ด๋ฒคํŠธ ํƒ€๊นƒ: [object HTMLLIElement]
              console.log(`์ปค๋ŸฐํŠธ ํƒ€๊นƒ: ${e.currentTarget}`);
              //์ปค๋ŸฐํŠธ ํƒ€๊นƒ: [object HTMLLIElement]
          });
    
          //๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•œ๋‹ค.
          home.addEventListener("click", (e) => {
              console.log(`์ด๋ฒคํŠธ ๋‹จ๊ณ„: ${e.eventPhase}`);
              //์ด๋ฒคํŠธ ๋‹จ๊ณ„: 3 : ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„
              console.log(`์ด๋ฒคํŠธ ํƒ€๊นƒ: ${e.target}`);
              //์ด๋ฒคํŠธ ํƒ€๊นƒ: [object HTMLLIElement]
              console.log(`์ปค๋ŸฐํŠธ ํƒ€๊นƒ: ${e.currentTarget}`);
              //์ปค๋ŸฐํŠธ ํƒ€๊นƒ: [object HTMLUListElement]
          });
      </script>
    </html>
    • ์ด์ฒ˜๋Ÿผ ์ด๋ฒคํŠธ๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ์ด๋ฒคํŠธ ํƒ€๊นƒ์€ ๋ฌผ๋ก  ์ƒ์œ„ DOM์š”์†Œ์—์„œ๋„ ์บ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋Œ€๋ถ€๋ถ„์˜ ์ด๋ฒคํŠธ๋Š” ์บก์ฒ˜๋ง๊ณผ ๋ฒ„๋ธ”๋ง์„ ํ†ตํ•ด ์ „ํŒŒ๋œ๋‹ค. ํ•˜์ง€๋งŒ ๋ช‡๋ช‡ ์ด๋ฒคํŠธ๋Š” ๋ฒ„๋ธ”๋ง์„ ํ†ตํ•ด ์ „ํŒŒ๋˜์ง€ ์•Š๋Š”๋‹ค.

      ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜์™€ ๋ฒ„๋ธ”๋ง ์—ฌ๋ถ€

3. ์ด๋ฒคํŠธ ์œ„์ž„

  • ์—ฌ๋Ÿฌ๊ฐœ์˜ ํ•˜์œ„ DOM ์š”์†Œ์— ๊ฐ๊ฐ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋Œ€์‹  ํ•˜๋‚˜์˜ ์ƒ์œ„ DOM ์š”์†Œ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•œ๋‹ค.

  • ์•„๋ž˜์˜ ์˜ˆ์ œ๋ฅผ ๋ณด์ž.

    <!DOCTYPE html>
    <html>
    <head>
        <style>
        #home {
            display: flex;
            list-style-type: none;
            padding: 0;
        }
        #home li {
            width: 100px;
            cursor: pointer;
        }
        #home .active {
            color: aqua;
            text-decoration: underline;
        }
        </style>
    </head>
    <body>
        <ul id="home">
        <li id="conan">Conan</li>
        <li id="mocha">Mocha</li>
        <li id="stuckyi">Stuckyi</li>
        </ul>
    
        <nav>์„ ํƒ๋œ ๊ฐ€์กฑ : <em class="msg">Conan</em></nav>
    </body>
    
    <script>
        const home = document.getElementById("home");
        const msg = document.querySelector(".msg");
    
        //์‚ฌ์šฉ์ž ํด๋ฆญ์— ์˜ํ•ด ์„ ํƒ๋œ ๊ฐ€์กฑ (li์š”์†Œ)์— active ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ 
        //๊ทธ ์™ธ์˜ ๋ชจ๋“  ๊ตฌ์„ฑ์›์˜ active ํด๋ž˜์Šค๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.
        function activate({ target }) {
          [...home.children].forEach((home) => {
              home.classList.toggle("active", home === target);
              msg.textContent = target.id;
          });
        }
    
        //๋ชจ๋“  ๊ตฌ์„ฑ์› (li์š”์†Œ)์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
        document.getElementById("conan").onclick = activate;
        document.getElementById("mocha").onclick = activate;
        document.getElementById("stuckyi").onclick = activate;
    </script>
    </html>
    • ๋ชจ๋“  li ์š”์†Œ๊ฐ€ ํด๋ฆญ ์ด๋ฒคํŠธ์— ๋ฐ˜์‘ํ•˜๋„๋ก ๋ชจ๋“  li ์š”์†Œ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์ธ activate๋ฅผ ๋“ฑ๋กํ–‡๋‹ค.

    • ๋งŒ์•ฝ li ์š”์†Œ๊ฐ€ 1000๊ฐœ๋ผ๋ฉด ?

    • ์ด ๊ฒฝ์šฐ ๋งŽ์€ DOM ์š”์†Œ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ ์ €ํ•˜์˜ ์›์ธ์ด ๋  ๋ฟ๋”๋Ÿฌ ์œ ์ง€๋ณด์ˆ˜์—๋„ ๋ถ€์ ํ•ฉํ•œ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ฒŒ ๋œ๋‹ค.

    • ์ด๋ฒคํŠธ ์œ„์ž„์„ ์‹œ์šฉํ•˜์—ฌ ์ˆ˜์ •ํ•ด ๋ณด์ž.

      <!DOCTYPE html>
      <html>
      <head>
          <style>
          #home {
              display: flex;
              list-style-type: none;
              padding: 0;
          }
          #home li {
              width: 100px;
              cursor: pointer;
          }
          #home .active {
              color: aqua;
              text-decoration: underline;
          }
          </style>
      </head>
      <body>
          <ul id="home">
          <li id="conan">Conan</li>
          <li id="mocha">Mocha</li>
          <li id="stuckyi">Stuckyi</li>
          </ul>
      
          <nav>์„ ํƒ๋œ ๊ฐ€์กฑ : <em class="msg">Conan</em></nav>
      </body>
      
      <script>
          const home = document.getElementById("home");
          const msg = document.querySelector(".msg");
      
          //์‚ฌ์šฉ์ž ํด๋ฆญ์— ์˜ํ•ด ์„ ํƒ๋œ ๊ฐ€์กฑ (li์š”์†Œ)์— active ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ 
          //๊ทธ ์™ธ์˜ ๋ชจ๋“  ๊ตฌ์„ฑ์›์˜ active ํด๋ž˜์Šค๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.
          function activate({ target }) {
              //์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ๊ธฐํ‚จ ์š”์†Œ (target)๊ฐ€ ul#home์˜ ์ž์‹ ์š”์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋ฌด์‹œํ•œ๋‹ค.
              if (!target.matches("#home > li")) return;
      
              [...home.children].forEach((home) => {
                  home.classList.toggle("active", home === target);
                  msg.textContent = target.id;
              });
          }
      
          //์ด๋ฒคํŠธ ์œ„์ž„ : ์ƒ์œ„ ์š”์†Œ (ul#home)๋Š” ํ•˜์œ„ ์š”์†Œ์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
          home.onclick = activate;
      </script>
      </html>

      Element.prototype.matches : ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ ์„ ํƒ์ž์— ์˜ํ•ด ํŠน์ • ๋…ธ๋“œ๋ฅผ ํƒ์ƒ‰ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•œ๋‹ค.

      • ์ด๋ฒคํŠธ ์œ„์ž„์„ ํ†ตํ•ด ํ•˜์œ„ DOM ์š”์†Œ์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ์€ ์ƒ์œ„ ์š”์†Œ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒคํŠธ ํƒ€๊นƒ, ์ฆ‰ ์‹ค์ œ๋กœ ๋ฐœ์ƒ์‹œํ‚จ DOM ์š”์†Œ๊ฐ€ ์šฐ๋ฆฌ๊ฐ€ ๊ธฐ๋Œ€ํ•œ DOM์š”์†Œ๊ฐ€ ์•„๋‹ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
      • ๋”ฐ๋ผ์„œ ์ด๋ฒคํŠธ์— ๋ฐ˜์‘์ด ํ•„์š”ํ•œ DOM ์š”์†Œ (#home > li)์— ํ•œ์ •ํ•˜์—ฌ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ์ด๋ฒคํŠธ ํƒ€๊นƒ์„ ๊ฒ€์‚ฌํ•ด์•ผ ํ•œ๋‹ค.

4. DOM ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘ ์กฐ์ž‘

4.1. preventDefault

  • DOM ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ค‘์ง€ ์‹œํ‚จ๋‹ค.

        document.querySelector("a").onclick = e => {
            //a ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ค‘์ง€ํ•œ๋‹ค.
            e.preventDefault();
        }
    
        document.querySelector("input[type=checkbox]").onclick = e => {
            //checkbox ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ค‘์ง€ํ•œ๋‹ค.
            e.preventDefault();
        }

4.2. stopPropagation

  • ์ด๋ฒคํŠธ ์ „ํŒŒ๋ฅผ ์ค‘์ง€์‹œํ‚จ๋‹ค.

      <!DOCTYPE html>
      <html>
      <body>
          <div class="container">
          <button class="btn1">btn1</button>
          <button class="btn2">btn2</button>
          <button class="btn3">btn3</button>
          </div>
    
          <script>
          //์ด๋ฒคํŠธ ์œ„์ž„. ํด๋ฆญ๋œ ํ•˜์œ„ ์š”์†Œ์˜ color๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.
          document.querySelector(".container").onclick = ({ target }) => {
              if (!target.matches(".container > button")) return;
              target.style.color = "blue";
          };
    
          //.btn2 ์š”์†Œ๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ „ํŒŒํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ƒ์œ„ ์š”์†Œ์—์„œ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•  ์ˆ˜ ์—†๋‹ค.
          document.querySelector(".btn2").onclick = (e) => {
              e.stopPropagation();
              e.target.style.color = "red";
          };
          </script>
      </body>
      </html>


์ฐธ๊ณ : ๋ชจ๋˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ deep dive