๐ŸŽฏ 40.1 ์ด๋ฒคํŠธ ๋“œ๋ฆฌ๋ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

๋ธŒ๋ผ์šฐ์ €๋Š” ์ฒ˜๋ฆฌํ•ด์•ผํ•  ํŠน์ • ์‚ฌ๊ฑด์ด ๋ฐœ์ƒํ•˜๋ฉด ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์ด๋ฒคํŠธ(event)๋ฅผ ๋ฐœ์ƒ(trigger)์‹œํ‚จ๋‹ค. ๋งŒ์•ฝ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํŠน์ • ํƒ€์ž…์˜ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ๋ฐ˜์‘ํ•˜์—ฌ ์–ด๋–ค ์ผ์„ ํ•˜๊ณ ์‹ถ๋‹ค๋ฉด ํ•ด๋‹นํ•˜๋Š” ํƒ€์ž…์˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ๋  ํ•จ์ˆ˜๋ฅผ ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ์•Œ๋ ค ํ˜ธ์ถœ์„ ์œ„์ž„ํ•œ๋‹ค. ์ด๋•Œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ๋  ํ•จ์ˆ˜๋ฅผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ(event handler)๋ผ ํ•˜๊ณ , ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ํ˜ธ์ถœ์„ ์œ„์ž„ํ•˜๋Š” ๊ฒƒ์„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก์ด๋ผ ํ•œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button>Click me!</button>
  <script>
    const $button = document.querySelector('button');

    // ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ์š”์ฒญ
    $button.onclick = () => { alert('button click'); };
  </script>
</body>
</html>

์œ„ ์˜ˆ์ œ์—์„œ ๋ฒ„ํŠผ์š”์†Œ $button์˜ onclick ํ”„๋กœํผํ‹ฐ์— ํ•จ์ˆ˜๋ฅผ ํ• ๋‹นํ–ˆ๋‹ค. ์ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ์— ํ•จ์ˆ˜๋ฅผ ํ• ๋‹นํ•˜๋ฉด ํ•ด๋‹น ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ• ๋‹นํ•œ ํ•จ์ˆ˜๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ํ˜ธ์ถœ๋œ๋‹ค.

์ด์ฒ˜๋Ÿผ ์ด๋ฒคํŠธ์™€ ๊ทธ์— ๋Œ€์‘ํ•˜๋Š” ํ•จ์ˆ˜ (์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ)๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์™€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ƒํ˜ธ์ž‘์šฉ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด์™€ ๊ฐ™์ด ํ”„๋กœ๊ทธ๋žจ์˜ ํ๋ฆ„์„ ์ด๋ฒคํŠธ ์ค‘์‹ฌ์œผ๋กœ ์ œ์–ดํ•˜๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์„ ์ด๋ฒคํŠธ ๋“œ๋ฆฌ๋ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(event-driven programming)์ด๋ผ ํ•œ๋‹ค.

๐ŸŽฏ 40.2 ์ด๋ฒคํŠธ ํƒ€์ž…

์ด๋ฒคํŠธ ํƒ€์ž…์€ ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜๋ฃฐ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด๋‹ค.

โœ” 40.2.1 ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ

โœ” 40.2.2 ํ‚ค๋ณด๋“œ ์ด๋ฒคํŠธ

โœ” 40.2.3 ํฌ์ปค์Šค ์ด๋ฒคํŠธ

โœ” 40.2.4 ํผ ์ด๋ฒคํŠธ

โœ” 40.2.5 ๊ฐ’ ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ

โœ” 40.2.6 DOM ๋ฎคํ…Œ์ด์…˜ ์ด๋ฒคํŠธ

โœ” 40.2.7 ๋ทฐ ์ด๋ฒคํŠธ

โœ” 40.2.8 ๋ฆฌ์†Œ์Šค ์ด๋ฒคํŠธ


๐ŸŽฏ 40.3 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ธŒ๋ผ์šฐ์ €์— ํ˜ธ์ถœ์„ ์œ„์ž„ํ•œ ํ•จ์ˆ˜๋‹ค. ๋‹ค์‹œ ๋งํ•ด, ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ํ˜ธ์ถœ๋  ํ•จ์ˆ˜๊ฐ€ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋‹ค. ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ํ˜ธ์ถœ์„ ์œ„์ž„ํ•˜๋Š” ๊ฒƒ์„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก์ด๋ผ ํ•œ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์€ 3๊ฐ€์ง€๋‹ค.

โœ” 40.3.1 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋ฐฉ์‹ (inline ๋ฐฉ์‹)

HTML ์š”์†Œ์˜ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ์ค‘์—๋Š” ์ด๋ฒคํŠธ์— ๋Œ€์‘ํ•˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ๊ฐ€ ์žˆ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ์˜ ์ด๋ฆ„์€ onclick๊ณผ ๊ฐ™์ด on ์ ‘๋‘์‚ฌ์™€ ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ด๋ฒคํŠธ ํƒ€์ž…์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๊ฐ’์œผ๋กœ ํ•จ์ˆ˜ ํ˜ธ์ถœ๋ฌธ ๋“ฑ์˜ ๋ฌธ์„ ํ• ๋‹นํ•˜๋ฉด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋“ฑ๋ก๋œ๋‹ค. ์ฆ‰, ์ด๋ฒคํŠธ ๋Œ€์ƒ์˜ ํƒœ๊ทธ ์†์„ฑ์œผ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button onclick="sayHi('Lee')">Click me!</button>
  <script>
    function sayHi(name) {
      console.log(`Hi! ${name}.`);
    }
  </script>
</body>
</html>

โœ” 40.3.2 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹

window ๊ฐ์ฒด์™€ Document, HTMLElement ํƒ€์ž…์˜ DOM ๋…ธ๋“œ ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ์— ๋Œ€์‘ํ•˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ์˜ ํ‚ค๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ onclick๊ณผ ๊ฐ™์ด on์ ‘๋‘์‚ฌ์™€ ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ด๋ฒคํŠธ ํƒ€์ž…์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ์— ํ•จ์ˆ˜๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๋ฉด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋“ฑ๋ก๋œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button>Click me!</button>
  <script>
    const $button = document.querySelector('button');

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ฐ”์ธ๋”ฉ
    $button.onclick = function () {
      console.log('button click');
    };
  </script>
</body>
</html>

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ๊ฐ์ฒด์ธ ์ด๋ฒคํƒ€ ํƒ€๊นƒ(event target)๊ณผ ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ธ ์ด๋ฒคํŠธ ํƒ€์ž…(event type) ๊ทธ๋ฆฌ๊ณ  ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ง€์ •ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฒ„ํŠผ ์š”์†Œ๊ฐ€ ํด๋ฆญ๋˜๋ฉด handleClick ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฒฝ์šฐ ์ด๋ฒคํŠธ ํƒ€๊นƒ์€ ๋ฒ„ํŠผ ์š”์†Œ์ด๊ณ  ์ด๋ฒคํŠธ ํƒ€์ž…์€ โ€˜clickโ€™์ด๋ฉฐ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” handleClick ํ•จ์ˆ˜๋‹ค.

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋Œ€๋ถ€๋ถ„ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ด๋ฒคํŠธ ํƒ€๊นƒ์— ๋ฐ”์ธ๋”ฉํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋ฐ˜๋“œ์‹œ ์ด๋ฒคํŠธ ํƒ€๊นƒ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ์ด๋ฒคํŠธ ํƒ€๊นƒ ๋˜๋Š” ์ „ํŒŒ๋œ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•  DOM ๋…ธ๋“œ ๊ฐ์ฒด์— ๋ฐ”์ธ๋”ฉํ•œ๋‹ค.

์•ž์„œ ์‚ดํŽด๋ณธ โ€œ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋ฐฉ์‹โ€๋„ ๊ฒฐ๊ตญ DOM ๋…ธ๋“œ ๊ฐ์ฒด์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ๋กœ ๋ณ€ํ™˜๋˜๋ฏ€๋กœ ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹๊ณผ ๋™์ผํ•˜๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. โ€œ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹โ€์€ โ€œ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋ฐฉ์‹โ€์˜ HTML๊ณผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋’ค์„ž์ด๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ์— ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋งŒ ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button>Click me!</button>
  <script>
    const $button = document.querySelector('button');

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹์€ ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ์— ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋งŒ์„ ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    // ์ฒซ ๋ฒˆ์งธ๋กœ ๋ฐ”์ธ๋”ฉ๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋‘ ๋ฒˆ์งธ ๋ฐ”์ธ๋”ฉ๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— ์˜ํ•ด ์žฌํ• ๋‹น๋˜์–ด
    // ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
    $button.onclick = function () {
      console.log('Button clicked 1');
    };

    // ๋‘ ๋ฒˆ์งธ๋กœ ๋ฐ”์ธ๋”ฉ๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
    $button.onclick = function () {
      console.log('Button clicked 2');
    };
  </script>
</body>
</html>

โœ” 40.3.3 addEventListener๋ฉ”์„œ๋“œ ๋ฐฉ์‹

addEventListener๋ฉ”์„œ๋“œ์˜ ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์—๋Š” ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ธ ์ด๋ฒคํŠธ ํƒ€์ž…,๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์—๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ(ํ•จ์ˆ˜), ๋งˆ์ง€๋ง‰ ๋งค๊ฐœ๋ณ€์ˆ˜์—๋Š” ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•  ์ „ํŒŒ ๋‹จ๊ณ„(์บก์ฒ˜๋ง ๋˜๋Š” ๋ฒ„๋ธ”๋ง)๋ฅผ ์ง€์ •ํ•œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button>Click me!</button>
  <script>
    const $button = document.querySelector('button');

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹
    // $button.onclick = function () {
    //   console.log('button click');
    // };

    // addEventListener ๋ฉ”์„œ๋“œ ๋ฐฉ์‹
    $button.addEventListener('click', function () {
      console.log('button click');
    });
  </script>
</body>
</html>

๋™์ผํ•œ HTML ์š”์†Œ์—์„œ ๋ฐœ์ƒํ•œ ๋™์ผํ•œ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹์€ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์—†์ง€๋งŒ addEventListener๋ฉ”์„œ๋“œ๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋“ฑ๋ก๋œ ์ˆœ์„œ๋Œ€๋กœ ํ˜ธ์ถœ๋œ๋‹ค. addEventListener๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ฐธ์กฐ๊ฐ€ ๋™์ผํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ค‘๋ณต ๋“ฑ๋กํ•˜๋ฉด ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋งŒ ๋“ฑ๋ก๋œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button>Click me!</button>
  <script>
    const $button = document.querySelector('button');
    const handleClick = () => console.log("button click");
    // addEventListener ๋ฉ”์„œ๋“œ๋Š” ๋™์ผํ•œ ์š”์†Œ์—์„œ ๋ฐœ์ƒํ•œ ๋™์ผํ•œ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด
    // ํ•˜๋‚˜ ์ด์ƒ์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.
    $button.addEventListener('click', function () {
      console.log('[1]button click');
    });

    $button.addEventListener('click', function () {
      console.log('[2]button click');
    });
    // ์ฐธ์กฐ๊ฐ€ ๋™์ผํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ค‘๋ณต ๋“ฑ๋กํ•˜๋ฉด ํ•˜๋‚˜์˜ ํ•ธ๋“ค๋Ÿฌ๋งŒ ๋“ฑ๋ก๋œ๋‹ค.
    $button.addEventListener('click', handleClick);
    $button.addEventListener('click', handleClick);
  </script>
</body>
</html>

๐ŸŽฏ 40.4 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์ œ๊ฑฐ

addEventListener๋ฉ”์„œ๋“œ๋กœ ๋“ฑ๋กํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ œ๊ฑฐํ•˜๋ ค๋ฉด EventTarget.prototype.removeEventListen ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. removeEventListener๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•  ์ธ์ˆ˜๋Š” addEventListener๋ฉ”์„œ๋“œ์™€ ๋™์ผํ•˜๋‹ค. ๋‹จ addEventListener๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•œ ์ธ์ˆ˜์™€ removeEventListener๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•œ ์ธ์ˆ˜๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ œ๊ฑฐ๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ๋ฌด๋ช… ํ•จ์ˆ˜๋ฅผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ๋“ฑ๋กํ•œ ๊ฒฝ์šฐ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button>Click me!</button>
  <script>
    const $button = document.querySelector('button');

    const handleClick = () => console.log('button click');

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก
    $button.addEventListener('click', handleClick);

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์ œ๊ฑฐ
    // addEventListener ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•œ ์ธ์ˆ˜์™€ removeEventListener ๋ฉ”์„œ๋“œ์—
    // ์ „๋‹ฌํ•œ ์ธ์ˆ˜๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ œ๊ฑฐ๋˜์ง€ ์•Š๋Š”๋‹ค.
    $button.removeEventListener('click', handleClick, true); // ์‹คํŒจ
    $button.removeEventListener('click', handleClick); // ์„ฑ๊ณต
  </script>
</body>
</html>

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹์œผ๋กœ ๋“ฑ๋กํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” removeEventListener ๋ฉ”์„œ๋“œ๋กœ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹์œผ๋กœ ๋“ฑ๋กํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ œ๊ฑฐํ•˜๋ ค๋ฉด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ์— null์„ ํ• ๋‹นํ•œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button>Click me!</button>
  <script>
    const $button = document.querySelector('button');

    const handleClick = () => console.log('button click');

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹์œผ๋กœ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก
    $button.onclick = handleClick;

    // removeEventListener ๋ฉ”์„œ๋“œ๋กœ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†๋‹ค.
    $button.removeEventListener('click', handleClick);

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ์— null์„ ํ• ๋‹นํ•˜์—ฌ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.
    $button.onclick = null;
  </script>
</body>
</html>

๐ŸŽฏ 40.5 ์ด๋ฒคํŠธ ๊ฐ์ฒด

์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ด๋ฒคํŠธ์— ๊ด€๋ จํ•œ ๋‹ค์–‘ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <p>ํด๋ฆญํ•˜์„ธ์š”. ํด๋ฆญํ•œ ๊ณณ์˜ ์ขŒํ‘œ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</p>
  <em class="message"></em>
  <script>
    const $msg = document.querySelector('.message');

    // ํด๋ฆญ ์ด๋ฒคํŠธ์— ์˜ํ•ด ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ๋‹ค.
    function showCoords(e) {
      $msg.textContent = `clientX: ${e.clientX}, clientY: ${e.clientY}`;
    }

    document.onclick = showCoords;
  </script>
</body>
</html>

ํด๋ฆญ ์ด๋ฒคํŠธ์— ์˜ํ•ด ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋˜์–ด ๋งค๊ฐœ๋ณ€์ˆ˜ e์— ์•”๋ฌต์ ์œผ๋กœ ํ• ๋‹น๋œ๋‹ค.

<!DOCTYPE html>
<html>
<head>
  <style>
    html, body { height: 100%; }
  </style>
</head>
<!-- ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ event๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ
์ „๋‹ฌ๋ฐ›์ง€ ๋ชปํ•œ๋‹ค. -->
<body onclick="showCoords(event)">
  <p>ํด๋ฆญํ•˜์„ธ์š”. ํด๋ฆญํ•œ ๊ณณ์˜ ์ขŒํ‘œ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</p>
  <em class="message"></em>
  <script>
    const $msg = document.querySelector('.message');

    // ํด๋ฆญ ์ด๋ฒคํŠธ์— ์˜ํ•ด ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ๋‹ค.
    function showCoords(e) {
      $msg.textContent = `clientX: ${e.clientX}, clientY: ${e.clientY}`;
    }
  </script>
</body>
</html>

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌ๋ฐ›์œผ๋ ค๋ฉด ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„์ด ๋ฐ˜๋“œ์‹œ event์—ฌ์•ผ ํ•œ๋‹ค.

โœ” 40.5.1 ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ ์ƒ์† ๊ตฌ์กฐ


์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ด๋ฒคํŠธ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒ์† ๊ตฌ์กฐ๋ฅผ ๊ฐ–๋Š”๋‹ค. ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ƒ์„ฑ์ž ํ•จ์ˆ˜์™€ ๋”๋ถˆ์–ด ์ƒ์„ฑ๋˜๋Š” ํ”„๋กœํ† ํƒ€์ž…์œผ๋กœ ๊ตฌ์„ฑ๋œ ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์˜ ์ผ์›์ด ๋œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <script>
    // Event ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ foo ์ด๋ฒคํŠธ ํƒ€์ž…์˜ Event ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    let e = new Event('foo');
    console.log(e);
    // Event {isTrusted: false, type: "foo", target: null, ...}
    console.log(e.type); // "foo"
    console.log(e instanceof Event); // true
    console.log(e instanceof Object); // true

    // FocusEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ focus ์ด๋ฒคํŠธ ํƒ€์ž…์˜ FocusEvent ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    e = new FocusEvent('focus');
    console.log(e);
    // FocusEvent {isTrusted: false, relatedTarget: null, view: null, ...}

    // MouseEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ click ์ด๋ฒคํŠธ ํƒ€์ž…์˜ MouseEvent ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    e = new MouseEvent('click');
    console.log(e);
    // MouseEvent {isTrusted: false, screenX: 0, screenY: 0, clientX: 0, ... }

    // KeyboardEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ keyup ์ด๋ฒคํŠธ ํƒ€์ž…์˜ KeyboardEvent ๊ฐ์ฒด๋ฅผ
    // ์ƒ์„ฑํ•œ๋‹ค.
    e = new KeyboardEvent('keyup');
    console.log(e);
    // KeyboardEvent {isTrusted: false, key: "", code: "", ctrlKey: false, ...}

    // InputEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ change ์ด๋ฒคํŠธ ํƒ€์ž…์˜ InputEvent ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    e = new InputEvent('change');
    console.log(e);
    // InputEvent {isTrusted: false, data: null, inputType: "", ...}
  </script>
</body>
</html>

Event ์ธํ„ฐํŽ˜์ด์Šค๋Š” DOM ๋‚ด์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ์— ์˜ํ•ด ์ƒ์„ฑ๋˜๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. Event ์ธํ„ฐํŽ˜์ด์Šค์—๋Š” ๋ชจ๋“  ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ ๊ณตํ†ต ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๊ณ  FocusEvent, MouseEvent, KeyboardEvent, WheelEvent๊ฐ™์€ ํ•˜์œ„ ์ธํ„ฐํŽ˜์ด์Šค์—๋Š” ์ด๋ฒคํŠธ ํƒ€์ž…์— ๋”ฐ๋ผ ๊ณ ์œ ํ•œ ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋‹ค. ์ฆ‰, ๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ๋Š” ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ์˜ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค

<!DOCTYPE html>
<html>
<body>
  <input type="text">
  <input type="checkbox">
  <button>Click me!</button>
  <script>
    const $input = document.querySelector('input[type=text]');
    const $checkbox = document.querySelector('input[type=checkbox]');
    const $button = document.querySelector('button');

    // load ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด Event ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
    window.onload = console.log;

    // change ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด Event ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
    $checkbox.onchange = console.log;

    // focus ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด FocusEvent ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
    $input.onfocus = console.log;

    // input ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด InputEvent ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
    $input.oninput = console.log;

    // keyup ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด KeyboardEvent ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
    $input.onkeyup = console.log;

    // click ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด MouseEvent ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
    $button.onclick = console.log;
  </script>
</body>
</html>

โœ” 40.5.2 ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ ๊ณตํ†ต ํ”„๋กœํผํ‹ฐ

Event ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ด๋ฒคํŠธ ๊ด€๋ จ ํ”„๋กœํผํ‹ฐ๋Š” ๋ชจ๋“  ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์†๋ฐ›๋Š” ๊ณตํ†ต ํ”„๋กœํผํ‹ฐ์ด๋‹ค. ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ ๊ณตํ†ต ํ”„๋กœํผํ‹ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <input type="checkbox">
  <em class="message">off</em>
  <script>
    const $checkbox = document.querySelector('input[type=checkbox]');
    const $msg = document.querySelector('.message');

    // change ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด Event ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
    $checkbox.onchange = e => {
      console.log(Object.getPrototypeOf(e) === Event.prototype); // true

      // e.target์€ change ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ DOM ์š”์†Œ $checkbox๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ 
      // e.target.checked๋Š” ์ฒดํฌ๋ฐ•์Šค ์š”์†Œ์˜ ํ˜„์žฌ ์ฒดํฌ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
      $msg.textContent = e.target.checked ? 'on' : 'off';
    };
  </script>
</body>
</html>

โœ” 40.5.3 ๋งˆ์šฐ์Šค ์ •๋ณด ์ทจ๋“

click, dbclick, mousedown, mouseup, mousemove, mouseenter, mouseleave ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ƒ์„ฑ๋˜๋Š” MouseEvent ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณ ์œ ์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ–๋Š”๋‹ค.

  • ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ์˜ ์ขŒํ‘œ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ”„๋กœํผํ‹ฐ : screenX/screenY, clientX/clientY, pageX/pageY, offsetX/offsetY
  • ๋ฒ„ํŠผ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ”„๋กœํผํ‹ฐ : altKey, ctrlKey, shiftKey, button
<!DOCTYPE html>
<html>
<head>
  <style>
    .box {
      width: 100px;
      height: 100px;
      background-color: #fff700;
      border: 5px solid orange;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="box"></div>
  <script>
    // ๋“œ๋ž˜๊ทธ ๋Œ€์ƒ ์š”์†Œ
    const $box = document.querySelector('.box');

    // ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ ์‹œ์ ์˜ ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ ์œ„์น˜
    const initialMousePos = { x: 0, y: 0 };
    // ์˜คํ”„์…‹: ์ด๋™ํ•  ๊ฑฐ๋ฆฌ
    const offset = { x: 0, y: 0 };

    // mousemove ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
    const move = e => {
      // ์˜คํ”„์…‹ = ํ˜„์žฌ(๋“œ๋ž˜๊ทธํ•˜๊ณ  ์žˆ๋Š” ์‹œ์ ) ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ ์œ„์น˜ - ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ ์‹œ์ ์˜ ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ ์œ„์น˜
      offset.x = e.clientX - initialMousePos.x;
      offset.y = e.clientY - initialMousePos.y;

      // translate3d๋Š” GPU๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ absolute์˜ top, left๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋น ๋ฅด๋‹ค.
      // top, left๋Š” ๋ ˆ์ด์•„์›ƒ์— ์˜ํ–ฅ์„ ์ค€๋‹ค.
      $box.style.transform = `translate3d(${offset.x}px, ${offset.y}px, 0)`;
    };

    // mousedown ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ ์‹œ์ ์˜ ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ ์ขŒํ‘œ๋ฅผ ์ €์žฅํ•œ๋‹ค.
    $box.addEventListener('mousedown', e => {
      // ์ด๋™ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด mousedown ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ(๋“œ๋ž˜๊ทธ๋ฅผ ์‹œ์ž‘)ํ•˜๋ฉด
      // ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ ์‹œ์ ์˜ ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ ์ขŒํ‘œ(e.clientX/e.clientY: ๋ทฐํฌํŠธ ์ƒ์—์„œ ํ˜„์žฌ
      // ๋งˆ์šฐ์Šค์˜ ํฌ์ธํ„ฐ ์ขŒํ‘œ)๋ฅผ ์ €์žฅํ•ด ๋‘”๋‹ค. ํ•œ๋ฒˆ ์ด์ƒ ๋“œ๋ž˜๊ทธ๋กœ ์ด๋™ํ•œ ๊ฒฝ์šฐ move์—์„œ
      // translate3d(${offset.x}px, ${offset.y}px, 0)์œผ๋กœ ์ด๋™ํ•œ ์ƒํƒœ์ด๋ฏ€๋กœ
      // offset.x์™€ offset.y๋ฅผ ๋นผ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
      initialMousePos.x = e.clientX - offset.x;
      initialMousePos.y = e.clientY - offset.y;

      // mousedown ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ƒํƒœ์—์„œ mousemove ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด
      // box ์š”์†Œ๋ฅผ ์ด๋™์‹œํ‚จ๋‹ค.
      document.addEventListener('mousemove', move);
    });

    // mouseup ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด mousemove ์ด๋ฒคํŠธ๋ฅผ ์ œ๊ฑฐํ•ด ์ด๋™์„ ๋ฉˆ์ถ˜๋‹ค.
    document.addEventListener('mouseup', () => {
      document.removeEventListener('mousemove', move);
    });
  </script>
</body>
</html>

โœ” 40.5.4 ํ‚ค๋ณด๋“œ ์ •๋ณด ์ทจ๋“

keydown, keyup, keypress ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ƒ์„ฑ๋˜๋Š” KeyboardEvent ํƒ€์ž…์˜ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” altKey, ctrlKey, shiftKey, metaKey, key, keyCode ๊ฐ™์€ ๊ณ ์œ ์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ–๋Š”๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <input type="text" />
  <em class="message"></em>
  <script>
    const $input = document.querySelector('input[type=text]');
    const $msg = document.querySelector('.message');

    $input.onkeyup = e => {
      // e.key๋Š” ์ž…๋ ฅํ•œ ํ‚ค ๊ฐ’์„ ๋ฌธ์ž์—ด๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
      // ์ž…๋ ฅํ•œ ํ‚ค๊ฐ€ 'Enter', ์ฆ‰ ์—”ํ„ฐ ํ‚ค๊ฐ€ ์•„๋‹ˆ๋ฉด ๋ฌด์‹œํ•œ๋‹ค.
      if (e.key !== 'Enter') return;

      // ์—”ํ„ฐํ‚ค๊ฐ€ ์ž…๋ ฅ๋˜๋ฉด ํ˜„์žฌ๊นŒ์ง€ ์ž…๋ ฅ ํ•„๋“œ์— ์ž…๋ ฅ๋œ ๊ฐ’์„ ์ถœ๋ ฅํ•œ๋‹ค.
      $msg.textContent = e.target.value;
      e.target.value = '';
    };
  </script>
</body>
</html>

๐ŸŽฏ 40.6 ์ด๋ฒคํŠธ ์ „ํŒŒ

DOM ํŠธ๋ฆฌ ์ƒ์— ์กด์žฌํ•˜๋Š” DOM ์š”์†Œ ๋…ธ๋“œ์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋Š” DOM ํŠธ๋ฆฌ๋ฅผ ํ†ตํ•ด ์ „ํŒŒ๋œ๋‹ค. ์ด๋ฅผ ์ด๋ฒคํŠธ ์ „ํŒŒ(event propagation)๋ผ๊ณ  ํ•œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <ul id="fruits">
    <li id="apple">Apple</li>
    <li id="banana">Banana</li>
    <li id="orange">Orange</li>
  </ul>
</body>
</html>

์ด๋ฒคํŠธ ์ „ํŒŒ๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ์ „ํŒŒ๋˜๋Š” ๋ฐฉํ–ฅ์— ๋”ฐ๋ผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด 3๋‹จ๊ณ„๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์บก์ณ๋ง ๋‹จ๊ณ„(capturing phase) : ์ด๋ฒคํŠธ๊ฐ€ ์ƒ์œ„ ์š”์†Œ์—์„œ ํ•˜์œ„ ์š”์†Œ ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ
  • ํƒ€๊นƒ ๋‹จ๊ณ„(target) : ์ด๋ฒคํŠธ๊ฐ€ ์ด๋ฒคํŠธ ํƒ€๊นƒ์— ๋„๋‹ฌ
  • ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„(bubbling phase) : ์ด๋ฒคํŠธ๊ฐ€ ํ•˜์œ„ ์š”์†Œ์—์„œ ์•„์œ„ ์š”์†Œ ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํŒŒ
<!DOCTYPE html>
<html>
<body>
  <ul id="fruits">
    <li id="apple">Apple</li>
    <li id="banana">Banana</li>
    <li id="orange">Orange</li>
  </ul>
  <script>
    const $fruits = document.getElementById('fruits');
    const $banana = document.getElementById('banana');

    // #fruits ์š”์†Œ์˜ ํ•˜์œ„ ์š”์†Œ์ธ li ์š”์†Œ๋ฅผ ํด๋ฆญํ•œ ๊ฒฝ์šฐ
    // ์บก์ฒ˜๋ง ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•œ๋‹ค.
    $fruits.addEventListener('click', e => {
      console.log(`์ด๋ฒคํŠธ ๋‹จ๊ณ„: ${e.eventPhase}`); // 1: ์บก์ฒ˜๋ง ๋‹จ๊ณ„
      console.log(`์ด๋ฒคํŠธ ํƒ€๊นƒ: ${e.target}`); // [object HTMLLIElement]
      console.log(`์ปค๋ŸฐํŠธ ํƒ€๊นƒ: ${e.currentTarget}`); // [object HTMLUListElement]
    }, true);

    // ํƒ€๊นƒ ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•œ๋‹ค.
    $banana.addEventListener('click', e => {
      console.log(`์ด๋ฒคํŠธ ๋‹จ๊ณ„: ${e.eventPhase}`); // 2: ํƒ€๊นƒ ๋‹จ๊ณ„
      console.log(`์ด๋ฒคํŠธ ํƒ€๊นƒ: ${e.target}`); // [object HTMLLIElement]
      console.log(`์ปค๋ŸฐํŠธ ํƒ€๊นƒ: ${e.currentTarget}`); // [object HTMLLIElement]
    });

    // ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•œ๋‹ค.
    $fruits.addEventListener('click', e => {
      console.log(`์ด๋ฒคํŠธ ๋‹จ๊ณ„: ${e.eventPhase}`); // 3: ๋ฒ„๋ธ”๋ง ๋‹จ๊ณ„
      console.log(`์ด๋ฒคํŠธ ํƒ€๊นƒ: ${e.target}`); // [object HTMLLIElement]
      console.log(`์ปค๋ŸฐํŠธ ํƒ€๊นƒ: ${e.currentTarget}`); // [object HTMLUListElement]
    });
  </script>
</body>
</html>

์œ„์ฒ˜๋Ÿผ ์ด๋ฒคํŠธ๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ํƒ€๊ฒŸ์€ ๋ฌผ๋ก  ์ƒ์œ„ DOM ์š”์†Œ์—์„œ๋„ ์บ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐ŸŽฏ 40.7 ์ด๋ฒคํŠธ ์œ„์ž„

<!DOCTYPE html>
<html>
<head>
  <style>
    #fruits {
      display: flex;
      list-style-type: none;
      padding: 0;
    }

    #fruits li {
      width: 100px;
      cursor: pointer;
    }

    #fruits .active {
      color: red;
      text-decoration: underline;
    }
  </style>
</head>
<body>
  <nav>
    <ul id="fruits">
      <li id="apple" class="active">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
    </ul>
  </nav>
  <div>์„ ํƒ๋œ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ: <em class="msg">apple</em></div>
  <script>
    const $fruits = document.getElementById('fruits');
    const $msg = document.querySelector('.msg');

    // ์‚ฌ์šฉ์ž ํด๋ฆญ์— ์˜ํ•ด ์„ ํƒ๋œ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ(li ์š”์†Œ)์— active ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ 
    // ๊ทธ ์™ธ์˜ ๋ชจ๋“  ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ์˜ active ํด๋ž˜์Šค๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.
    function activate({ target }) {
      [...$fruits.children].forEach($fruit => {
        $fruit.classList.toggle('active', $fruit === target);
        $msg.textContent = target.id;
      });
    }

    // ๋ชจ๋“  ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ(li ์š”์†Œ)์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
    document.getElementById('apple').onclick = activate;
    document.getElementById('banana').onclick = activate;
    document.getElementById('orange').onclick = activate;
  </script>
</body>
</html>

์œ„์˜ ์˜ˆ์ œ๋Š” ๋ชจ๋“  ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ(li ์š”์†Œ)์ด ํด๋ฆญ ์ด๋ฒคํŠธ์— ๋ฐ˜์‘ํ•˜๋„๋ก ๋ชจ๋“  ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์ธ activate๋ฅผ ๋“ฑ๋กํ–ˆ๋‹ค. ๋งŒ์ผ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ์ด 100๊ฐœ๋ผ๋ฉด 100๊ฐœ์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ด๋Š” ์„ฑ๋Šฅ ์ €ํ•˜์˜ ์›์ธ์ด ๋ ๋ฟ๋”๋Ÿฌ ์œ ์ง€๋ณด์ˆ˜์—๋„ ๋ถ€์ ํ•ฉํ•œ ์ฝ”๋“œ๋ฅผ ์ƒ์‚ฐํ•˜๊ฒŒ ํ•œ๋‹ค.

<!DOCTYPE html>
<html>
<head>
  <style>
    #fruits {
      display: flex;
      list-style-type: none;
      padding: 0;
    }

    #fruits li {
      width: 100px;
      cursor: pointer;
    }

    #fruits .active {
      color: red;
      text-decoration: underline;
    }
  </style>
</head>
<body>
  <nav>
    <ul id="fruits">
      <li id="apple" class="active">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
    </ul>
  </nav>
  <div>์„ ํƒ๋œ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ: <em class="msg">apple</em></div>
  <script>
    const $fruits = document.getElementById('fruits');
    const $msg = document.querySelector('.msg');

    // ์‚ฌ์šฉ์ž ํด๋ฆญ์— ์˜ํ•ด ์„ ํƒ๋œ ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ(li ์š”์†Œ)์— active ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ 
    // ๊ทธ ์™ธ์˜ ๋ชจ๋“  ๋‚ด๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ์˜ active ํด๋ž˜์Šค๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.
    function activate({ target }) {
      // ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ์š”์†Œ(target)๊ฐ€ ul#fruits์˜ ์ž์‹ ์š”์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋ฌด์‹œํ•œ๋‹ค.
      if (!target.matches('#fruits > li')) return;

      [...$fruits.children].forEach($fruit => {
        $fruit.classList.toggle('active', $fruit === target);
        $msg.textContent = target.id;
      });
    }

    // ์ด๋ฒคํŠธ ์œ„์ž„: ์ƒ์œ„ ์š”์†Œ(ul#fruits)๋Š” ํ•˜์œ„ ์š”์†Œ์˜ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
    $fruits.onclick = activate;
  </script>
</body>
</html>

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

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

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

function activate({ target }) {
  // ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ์š”์†Œ(target)์ด ul#fruits์˜ ์ž์‹ ์š”์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋ฌด์‹œํ•œ๋‹ค.
  if (!target.matches('#fruits > li')) return;
  ...

๐ŸŽฏ 40.8 DOM ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘ ์กฐ์ž‘

โœ”40.8.1 DOM ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘ ์ค‘๋‹จ

์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ **preventDefault** ๋ฉ”์„œ๋“œ๋Š” ์ด๋Ÿฌํ•œ DOM ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ค‘๋‹จ์‹œํ‚จ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <a href="https://www.google.com">go</a>
  <input type="checkbox">
  <script>
    document.querySelector('a').onclick = e => {
      // a ์š”์†Œ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ค‘๋‹จํ•œ๋‹ค.
      e.preventDefault();
    };

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

โœ”40.8.2 ์ด๋ฒคํŠธ ์ „ํŒŒ ๋ฐฉ์ง€

์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ **stopPropagation** ๋ฉ”์„œ๋“œ๋Š” ์ด๋ฒคํŠธ ์ „ํŒŒ๋ฅผ ์ค‘์ง€์‹œํ‚จ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <div class="container">
    <button class="btn1">Button 1</button>
    <button class="btn2">Button 2</button>
    <button class="btn3">Button 3</button>
  </div>
  <script>
    // ์ด๋ฒคํŠธ ์œ„์ž„. ํด๋ฆญ๋œ ํ•˜์œ„ ๋ฒ„ํŠผ ์š”์†Œ์˜ color๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.
    document.querySelector('.container').onclick = ({ target }) => {
      if (!target.matches('.container > button')) return;
      target.style.color = 'red';
    };

    // .btn2 ์š”์†Œ๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ „ํŒŒํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ƒ์œ„ ์š”์†Œ์—์„œ ์ด๋ฒคํŠธ๋ฅผ ์บ์น˜ํ•  ์ˆ˜ ์—†๋‹ค.
    document.querySelector('.btn2').onclick = e => {
      e.stopPropagation(); // ์ด๋ฒคํŠธ ์ „ํŒŒ ์ค‘๋‹จ
      e.target.style.color = 'blue';
    };
  </script>
</body>
</html>

์œ„ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ์ƒ์œ„ DOM ์š”์†Œ์ธ container ์š”์†Œ์— ์ด๋ฒคํŠธ๋ฅผ ์œ„์ž„ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•˜์œ„ DOM ์š”์†Œ์—์„œ ๋ฐœ์ƒํ•œ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์œ„ DOM ์š”์†Œ์ธ container ์š”์†Œ๊ฐ€ ์บ์น˜ํ•˜์—ฌ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค. ํ•˜์ง€๋งŒ ํ•˜์œ„ ์š”์†Œ์ค‘์—์„œ btn2 ์š”์†Œ๋Š” ์ž์ฒด์ ์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์ด๋•Œ btn2 ์š”์†Œ๋Š” ์ž์‹ ์ด ๋ฐœ์ƒ์‹œํ‚จ ์ด๋ฒคํŠธ๊ฐ€ ์ „ํŒŒ๋˜๋Š”๊ฒƒ์„ ์ค‘๋‹จํ•˜์—ฌ ์ž์‹ ์—๊ฒŒ ๋ฐ”์ธ๋”ฉ๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋งŒ ์‹คํ–‰๋˜๋„๋ก ํ•œ๋‹ค.

๐ŸŽฏ 40.9 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์˜ this

โœ”40.9.1 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋ฐฉ์‹

<!DOCTYPE html>
<html>
<body>
  <button onclick="handleClick()">Click me</button>
  <script>
    function handleClick() {
      console.log(this); // window
    }
  </script>
</body>
</html>

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๊ฐ’์œผ๋กœ ์ง€์ •ํ•œ ๋ฌธ์ž์—ด์€ ์‚ฌ์‹ค ์•”๋ฌต์ ์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๋ฌธ์ด๊ธฐ ๋•Œ๋ฌธ์— ์œ„์˜ ์˜ˆ์ œ์—์„œ handleClick ํ•จ์ˆ˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— ์˜ํ•ด ์ผ๋ฐ˜ ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœ๋œ๋‹ค. ์ผ๋ฐ˜ ํ•จ์ˆ˜๋กœ์„œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ this๋Š” ์ „์—ญ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฅดํ‚ค๊ธฐ ๋•Œ๋ฌธ์— handleClick ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ this๋Š” ์ „์—ญ๊ฐ์ฒด window๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button onclick="handleClick(this)">Click me</button>
  <script>
    function handleClick(button) {
      console.log(button); // ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•œ button ์š”์†Œ
      console.log(this);   // window
    }
  </script>
</body>
</html>

์œ„ ์˜ˆ์ œ์—์„œ handleClick ํ•จ์ˆ˜์— ์ „๋‹ฌ๋œ this๋Š” ์•”๋ฌต์ ์œผ๋กœ ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์˜ this๋‹ค. ์ฆ‰, ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋ฐฉ์‹์— ์˜ํ•ด ์•”๋ฌต์ ์œผ๋กœ ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์˜ this๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•œ DOM ์š”์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ์ด๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹๊ณผ ๋™์ผํ•˜๋‹ค.

โœ” 40.9.2 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹๊ณผ addEvenetListener๋ฉ”์„œ๋“œ ๋ฐฉ์‹

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹๊ณผ addEventListener๋ฉ”์„œ๋“œ ๋ฐฉ์‹ ๋ชจ๋‘ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์˜ this๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•œ DOM ์š”์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ์ฆ‰ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์˜ this๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ currentTarget ํ”„๋กœํผํ‹ฐ์™€ ๊ฐ™๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button class="btn1">0</button>
  <button class="btn2">0</button>
  <script>
    const $button1 = document.querySelector('.btn1');
    const $button2 = document.querySelector('.btn2');

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹
    $button1.onclick = function (e) {
      // this๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•œ DOM ์š”์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
      console.log(this); // $button1
      console.log(e.currentTarget); // $button1
      console.log(this === e.currentTarget); // true

      // $button1์˜ textContent๋ฅผ 1 ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.
      ++this.textContent;
    };

    // addEventListener ๋ฉ”์„œ๋“œ ๋ฐฉ์‹
    $button2.addEventListener('click', function (e) {
      // this๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•œ DOM ์š”์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
      console.log(this); // $button2
      console.log(e.currentTarget); // $button2
      console.log(this === e.currentTarget); // true

      // $button2์˜ textContent๋ฅผ 1 ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.
      ++this.textContent;
    });
  </script>
</body>
</html>

ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ์ •์˜ํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์˜ this๋Š” ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ this๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜ ์ž์ฒด์˜ this ๋ฐ”์ธ๋”ฉ์„ ๊ฐ–์ง€ ์•Š๋Š”๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button class="btn1">0</button>
  <button class="btn2">0</button>
  <script>
    const $button1 = document.querySelector('.btn1');
    const $button2 = document.querySelector('.btn2');

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹
    $button1.onclick = e => {
      // ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ this๋Š” ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ this๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
      console.log(this); // window
      console.log(e.currentTarget); // $button1
      console.log(this === e.currentTarget); // false

      // this๋Š” window๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ window.textContent์— NaN(undefined + 1)์„ ํ• ๋‹นํ•œ๋‹ค.
      ++this.textContent;
    };

    // addEventListener ๋ฉ”์„œ๋“œ ๋ฐฉ์‹
    $button2.addEventListener('click', e => {
      // ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ this๋Š” ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ this๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
      console.log(this); // window
      console.log(e.currentTarget); // $button2
      console.log(this === e.currentTarget); // false

      // this๋Š” window๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ window.textContent์— NaN(undefined + 1)์„ ํ• ๋‹นํ•œ๋‹ค.
      ++this.textContent;
    });
  </script>
</body>
</html>

ํด๋ž˜์Šค์—์„œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒฝ์šฐ this์— ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋‚˜ addEventListener๋ฉ”์„œ๋“œ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์™€ ๋™์ผํ•˜๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button class="btn">0</button>
  <script>
    class App {
      constructor() {
        this.$button = document.querySelector('.btn');
        this.count = 0;

        // increase ๋ฉ”์„œ๋“œ๋ฅผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ๋“ฑ๋ก
        this.$button.onclick = this.increase;
      }

      increase() {
        // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ increase ๋‚ด๋ถ€์˜ this๋Š” DOM ์š”์†Œ(this.$button)๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
        // ๋”ฐ๋ผ์„œ this.$button์€ this.$button.$button๊ณผ ๊ฐ™๋‹ค.
        this.$button.textContent = ++this.count;
        // -> TypeError: Cannot set property 'textContent' of undefined
      }
    }

    new App();
  </script>
</body>
</html>

์œ„ ์˜ˆ์ œ์—์„œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์˜ this๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•œ DOM ์š”์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ธฐ ๋•Œ๋ฌธ์— increase ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์˜ this๋Š” this.$button์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ๋”ฐ๋ผ์„œ increase ๋ฉ”์„œ๋“œ๋ฅผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ๋ฐ”์ธ๋”ฉํ• ๋•Œ bind ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด this๋ฅผ ์ „๋‹ฌํ•˜์—ฌ increase ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์˜ this๊ฐ€ ํด๋ž˜์Šค๊ฐ€ ์ƒ์„ฑํ•  ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button class="btn">0</button>
  <script>
    class App {
      constructor() {
        this.$button = document.querySelector('.btn');
        this.count = 0;

        // increase ๋ฉ”์„œ๋“œ๋ฅผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ๋“ฑ๋ก
        // this.$button.onclick = this.increase;

        // increase ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์˜ this๊ฐ€ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ํ•œ๋‹ค.
        this.$button.onclick = this.increase.bind(this);
      }

      increase() {
        this.$button.textContent = ++this.count;
      }
    }

    new App();
  </script>
</body>
</html>

๋˜๋Š” ํด๋ž˜์Šค ํ•„๋“œ์— ํ• ๋‹นํ•œ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ๋“ฑ๋กํ•˜์—ฌ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์˜ this๊ฐ€ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ๋‹ค๋งŒ ์ด๋•Œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ increase๋Š” ํ”„๋กœํ† ํƒ€์ž… ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋‹Œ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๊ฐ€ ๋œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button class="btn">0</button>
  <script>
    class App {
      constructor() {
        this.$button = document.querySelector('.btn');
        this.count = 0;

        // ํ™”์‚ดํ‘œ ํ•จ์ˆ˜์ธ increase๋ฅผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ๋“ฑ๋ก
        this.$button.onclick = this.increase;
      }

      // ํด๋ž˜์Šค ํ•„๋“œ ์ •์˜
      // increase๋Š” ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ์ด๋ฉฐ ๋‚ด๋ถ€์˜ this๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
      increase = () => this.$button.textContent = ++this.count;
    }
    new App();
  </script>
</body>
</html>

๐ŸŽฏ 40.10 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— ์ธ์ˆ˜ ์ „๋‹ฌ

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ๋ฐฉ์‹์€ ํ•จ์ˆ˜ ํ˜ธ์ถœ๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ”„๋กœํผํ‹ฐ ๋ฐฉ์‹๊ณผ addEventListener ๋ฉ”์„œ๋“œ ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜ ํ˜ธ์ถœ๋ฌธ์ด ์•„๋‹Œ ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋ ค๋ฉด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์—์„œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <label>User name <input type='text'></label>
  <em class="message"></em>
  <script>
    const MIN_USER_NAME_LENGTH = 5; // ์ด๋ฆ„ ์ตœ์†Œ ๊ธธ์ด
    const $input = document.querySelector('input[type=text]');
    const $msg = document.querySelector('.message');

    const checkUserNameLength = min => {
      $msg.textContent
        = $input.value.length < min ? `์ด๋ฆ„์€ ${min}์ž ์ด์ƒ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”` : '';
    };

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์—์„œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
    $input.onblur = () => {
      checkUserNameLength(MIN_USER_NAME_LENGTH);
    };
  </script>
</body>
</html>

๋˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <label>User name <input type='text'></label>
  <em class="message"></em>
  <script>
    const MIN_USER_NAME_LENGTH = 5; // ์ด๋ฆ„ ์ตœ์†Œ ๊ธธ์ด
    const $input = document.querySelector('input[type=text]');
    const $msg = document.querySelector('.message');

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
    const checkUserNameLength = min => e => {
      $msg.textContent
        = $input.value.length < min ? `์ด๋ฆ„์€ ${min}์ž ์ด์ƒ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”` : '';
    };

    // ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
    $input.onblur = checkUserNameLength(MIN_USER_NAME_LENGTH);
  </script>
</body>
</html>

checkUserNameLengthํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ $input.onblur์—๋Š” ๊ฒฐ๊ตญ checkUserNameLength

ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฐ”์ธ๋”ฉ๋œ๋‹ค.


๐ŸŽฏ 40.11 ์ปค์Šคํ…€ ์ด๋ฒคํŠธ

โœ”40.11.1 ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ์ƒ์„ฑ

์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์•”๋ฌต์ ์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ํƒ€์ž…์ด ๊ฒฐ์ •๋œ๋‹ค. ํ•˜์ง€๋งŒ Event, UIEvent, MouseEvent ๊ฐ™์€ ์ด๋ฒคํŠธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ช…์‹œ์ ์œผ๋กœ ์ƒ์„ฑํ•œ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ์ž„์˜์˜ ์ด๋ฒคํŠธ ํƒ€์ž…์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด์ฒ˜๋Ÿผ ๊ฐœ๋ฐœ์ž์˜ ์˜๋„๋กœ ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ๋ฅผ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ผ๊ณ  ํ•œ๋‹ค.

์ด๋ฒคํŠธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋Š” ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ด๋ฒคํŠธ ํƒ€์ž…์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์„ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค. ์ด๋•Œ ์ด๋ฒคํŠธ ํƒ€์ž…์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์€ ๊ธฐ์กด ์ด๋ฒคํŠธํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ž„์˜์˜ ๋ฌธ์ž์—ด์„ ์‚ฌ์šฉํ•œ๋‹ค.

// KeyboardEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ keyup ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
const keyboardEvent = new KeyboardEvent('keyup');
console.log(keyboardEvent.type); // keyup

// CustomEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ foo ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
const customEvent = new CustomEvent('foo');
console.log(customEvent.type); // foo

์ƒ์„ฑ๋œ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ๋ฒ„๋ธ”๋ง๋˜์ง€ ์•Š์œผ๋ฉฐ preventDefault ๋ฉ”์„œ๋“œ๋กœ ์ทจ์†Œํ•  ์ˆ˜๋„ ์—†๋‹ค. ์ฆ‰, ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” bubbles์™€ cancelable ํ”„๋กœํผํ‹ฐ ๊ฐ’์ด false๋กœ ๊ธฐ๋ณธ ์„ค์ •๋œ๋‹ค.

// MouseEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ click ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
const customEvent = new MouseEvent('click');
console.log(customEvent.type); // click
console.log(customEvent.bubbles); // false
console.log(customEvent.cancelable); // false

์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ bubbles ๋˜๋Š” cancelable ํ”„๋กœํผํ‹ฐ๋ฅผ true๋กœ ์„ค์ •ํ•˜๋ ค๋ฉด ์ด๋ฒคํŠธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ bubbles ๋˜๋Š” cancelabla ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ–๋Š” ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

// MouseEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ click ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
const customEvent = new MouseEvent('click', {
  bubbles: true,
  cancelable: true
});

console.log(customEvent.bubbles); // true
console.log(customEvent.cancelable); // true

์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด์—๋Š” bubbles ๋˜๋Š” cancelable ํ”„๋กœํผํ‹ฐ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ด๋ฒคํŠธ ํƒ€์ž…์— ๋”ฐ๋ผ ๊ฐ€์ง€๋Š” ์ด๋ฒคํŠธ ๊ณ ์œ ์˜ ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, MouseEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ ์ƒ์„ฑํ•œ ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ๊ฐ์ฒด๋Š” ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ์˜ ์ขŒํ‘œ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ๊ฐ์ฒด ๊ณ ์œ ์˜ ํ”„๋กœํผํ‹ฐ screenX /screenY, clientX/clientY, pageX/pageY, offsetX/offsetY์™€ ๋ฒ„ํŠผ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ”„๋กœํผํ‹ฐ altKey, ctrlKey, shiftKey, button์„ ๊ฐ–๋Š”๋‹ค. ์ด๋Ÿฌํ•œ ์ด๋ฒคํŠธ ๊ฐ์ฒด ๊ณ ์œ ์˜ ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ์ง€์ •ํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ด๋ฒคํŠธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

// MouseEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ click ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
const mouseEvent = new MouseEvent('click', {
  bubbles: true,
  cancelable: true,
  clientX: 50,
  clientY: 100
});

console.log(mouseEvent.clientX); // 50
console.log(mouseEvent.clientY); // 100

// KeyboardEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ keyup ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
const keyboardEvent = new KeyboardEvent('keyup', { key: 'Enter' });

console.log(keyboardEvent.key); // Enter

์ด๋ฒคํŠธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ ์ƒ์„ฑํ•œ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋Š” isTrusted ํ”„๋กœํผํ‹ฐ ๊ฐ’์ด ์–ธ์ œ๋‚˜ false๋‹ค. ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๊ฐ€ ์•„๋‹Œ ์‚ฌ์šฉ์ž์˜ ํ–‰์œ„์— ์˜ํ•ด ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ isTrusted ํ”„๋กœํผํ‹ฐ ๊ฐ’์€ ์–ธ์ œ๋‚˜ true๋‹ค.

// InputEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ foo ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
const customEvent = new InputEvent('foo');
console.log(customEvent.isTrusted); // false

โœ”40.11.2 ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๋””์ŠคํŒจ์น˜

์ƒ์„ฑ๋œ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋Š” **dispatchEvent**๋ฉ”์„œ๋“œ๋กœ ๋””์ŠคํŒจ์น˜(dispatch)(์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ํ–‰์œ„)๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค. **dispatchEvent**๋ฉ”์„œ๋“œ์— ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•˜๋ฉด์„œ ํ˜ธ์ถœํ•˜๋ฉด ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•œ ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button class="btn">Click me</button>
  <script>
    const $button = document.querySelector('.btn');

    // ๋ฒ„ํŠผ ์š”์†Œ์— click ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋ก
    // ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ฅผ ๋””์ŠคํŒจ์น˜ํ•˜๊ธฐ ์ด์ „์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.
    $button.addEventListener('click', e => {
      console.log(e); // MouseEvent {isTrusted: false, screenX: 0, ...}
      alert(`${e} Clicked!`);
    });

    // ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ์ƒ์„ฑ
    const customEvent = new MouseEvent('click');

    // ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๋””์ŠคํŒจ์น˜(๋™๊ธฐ ์ฒ˜๋ฆฌ). click ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
    $button.dispatchEvent(customEvent);
  </script>
</body>
</html>

์ผ๋ฐ˜์ ์œผ๋กœ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋น„๋™๊ธฐ(asynchronous) ์ฒ˜๋ฆฌ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜์ง€๋งŒ **dispatchEvent** ๋ฉ”์„œ๋“œ๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋™๊ธฐ(synchronous)๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค. ๋‹ค์‹œ ๋งํ•ด **dispatchEvent**๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ปค์Šคํ…€์ด๋ฒคํŠธ์— ๋ฐ”์ธ๋”ฉ๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค. ๋”ฐ๋ผ์„œ dispatchEvent ๋ฉ”์„œ๋“œ๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋””์ŠคํŒจ์น˜ ํ•˜๊ธฐ ์ด์ „์— ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.

// CustomEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ foo ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
const customEvent = new CustomEvent('foo');
console.log(customEvent.type); // foo

์ด๋•Œ CustonEvent ์ด๋ฒคํŠธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜์—๋Š” ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ด๋ฒคํŠธ์™€ ํ•จ๊ป˜ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์€ ์ •๋ณด๋ฅผ ๋‹ด์€ detail ํ”„๋กœํผํ‹ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์ •๋ณด๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ detail ํ”„๋กœํผํ‹ฐ์— ๋‹ด๊ฒจ ์ „๋‹ฌ๋œ๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <button class="btn">Click me</button>
  <script>
    const $button = document.querySelector('.btn');

    // ๋ฒ„ํŠผ ์š”์†Œ์— foo ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋ก
    // ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ฅผ ๋””์ŠคํŒจ์น˜ํ•˜๊ธฐ ์ด์ „์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.
    $button.addEventListener('foo', e => {
      // e.detail์—๋Š” CustomEvent ํ•จ์ˆ˜์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•œ ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ ์žˆ๋‹ค.
      alert(e.detail.message);
    });

    // CustomEvent ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ foo ์ด๋ฒคํŠธ ํƒ€์ž…์˜ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
    const customEvent = new CustomEvent('foo', {
      detail: { message: 'Hello' } // ์ด๋ฒคํŠธ์™€ ํ•จ๊ป˜ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์€ ์ •๋ณด
    });

    // ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๋””์ŠคํŒจ์น˜
    $button.dispatchEvent(customEvent);
  </script>
</body>
</html>

๊ธฐ์กด ์ด๋ฒคํŠธ ํƒ€์ž…์ด ์•„๋‹Œ ์ž„์˜์˜ ์ด๋ฒคํŠธ ํƒ€์ž…์„ ์ง€์ •ํ•˜์—ฌ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ๊ฒฝ์šฐ ๋ฐ˜๋“œ์‹œ addEventListener ๋ฉ”์„œ๋“œ ๋ฐฉ์‹์œผ๋กœ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ/ํ”„๋กœํผํ‹ฐ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ด์œ ๋Š” โ€œon + ์ด๋ฒคํŠธ ํƒ€์ž…โ€์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ/ํ”„๋กœํผํ‹ฐ๊ฐ€ ์š”์†Œ ๋…ธ๋“œ์— ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

profile
์ด์‚ฌ์ค‘์ž…๋‹ˆ๋‹ค!๐ŸŒŸhttps://velog.io/@devkyoung2

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด