JavaScript의 addEventListener(라이브 예제 포함)

addEventListener는 요소에 문제가 발생하면 함수를 실행합니다. 아래: 이벤트가 페이지를 통해 이동하는 방법, 옵션 개체 및 리스너를 다시 끄는 방법.

영문 원문 보기

element.addEventListener(type, handler)는 해당 유형의 이벤트가 요소에서 발생할 때마다 handler를 실행하도록 브라우저에 지시합니다. 핸들러는 발생한 일을 설명하는 이벤트 객체를 수신합니다. 원하는 만큼 리스너를 연결하고 나중에 각각 제거할 수 있습니다.

const button = document.querySelector('#save');

button.addEventListener('click', (e) => {
  console.log('clicked', e.target);
});

이것이 대부분의 페이지에 대한 전체 API입니다. 이 가이드의 나머지 부분에서는 이벤트가 발생한 후 이벤트가 진행되는 위치와 옵션에 대해 설명합니다. 아래 상자를 클릭하여 시작하세요.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Event phases: capture, target, bubble</title>
<style>
  body { margin: 0; padding: 12px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .opts { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 13px; margin-bottom: 10px; }
  .box { border-radius: 10px; padding: 12px; font: 600 13px system-ui; cursor: pointer; user-select: none; }
  #outer { background: #dbeafe; }
  #middle { background: #bfdbfe; margin-top: 6px; }
  #inner { background: #fff; margin-top: 6px; text-align: center; padding: 16px; }
  #log { margin: 10px 0 0; padding: 10px; height: 170px; overflow: auto; background: #111827; color: #e5e7eb;
         border-radius: 10px; font: 12.5px/1.55 ui-monospace, Consolas, monospace; white-space: pre-wrap; }
  .cap { color: #93c5fd; } .tgt { color: #fde68a; } .bub { color: #86efac; } .stop { color: #fca5a5; }
</style>
</head>
<body>
<div class="opts">
  <label><input type="checkbox" id="useCapture"> Log capture listeners</label>
  <label><input type="checkbox" id="useStop"> middle calls stopPropagation()</label>
</div>

<div class="box" id="outer">outer
  <div class="box" id="middle">middle
    <div class="box" id="inner">inner: click me</div>
  </div>
</div>
<pre id="log">Click any box.</pre>

<script>
  const log = document.getElementById('log');
  const useCapture = document.getElementById('useCapture');
  const useStop = document.getElementById('useStop');
  const phaseName = { 1: 'capture', 2: 'target ', 3: 'bubble ' };
  const phaseClass = { 1: 'cap', 2: 'tgt', 3: 'bub' };

  function write(e) {
    const line = document.createElement('span');
    line.className = phaseClass[e.eventPhase];
    // currentTarget = the element this listener is on; target = what was clicked
    line.textContent = phaseName[e.eventPhase] + '  currentTarget: ' + e.currentTarget.id +
                       '  target: ' + e.target.id + '\n';
    log.append(line);
  }

  function maybeStop(e) {
    if (useStop.checked && e.currentTarget.id === 'middle') {
      e.stopPropagation();
      log.insertAdjacentHTML('beforeend', '<span class="stop">stopPropagation() at middle\n</span>');
    }
  }

  for (const id of ['outer', 'middle', 'inner']) {
    const el = document.getElementById(id);
    // capture listener: runs on the way down
    el.addEventListener('click', (e) => {
      if (!useCapture.checked) return;
      write(e);
      maybeStop(e);
    }, { capture: true });
    // normal (bubble) listener: runs on the way up
    el.addEventListener('click', (e) => {
      write(e);
      maybeStop(e);
    });
  }

  // clear the log at the start of each click (a capture listener on window runs first)
  window.addEventListener('click', (e) => {
    if (e.target.closest('.box')) log.textContent = '';
  }, { capture: true });
</script>
</body>
</html>
내부, 중간 또는 외부를 클릭합니다. 캡처 리스너를 기록하거나 중간에 이벤트를 중지하려면 상자를 선택하십시오.

addEventListener 대 onclick

클릭에 반응하는 방법에는 세 가지가 있습니다. 처음 두 개는 요소당 하나의 슬롯을 공유합니다. 세 번째는 목록을 유지합니다.

onclick="..." 속성 el.onclick = fn addEventListener
그것이 사는 곳 HTML에서 스크립트에서 스크립트에서
이벤트당 핸들러 하나 하나, 마지막 과제가 승리합니다 임의의 숫자, 추가된 순서대로 실행
옵션 없음 없음 once, passive, capture, signal
제거하세요 속성 삭제 el.onclick = null removeEventListener 또는 abort()

단일 슬롯이 함정입니다. 두 스크립트가 모두 button.onclick를 설정한 경우 두 번째 스크립트가 자동으로 첫 번째 스크립트를 대체합니다. addEventListener를 사용하면 둘 다 실행됩니다. 작은 페이지의 경우 이 속성은 괜찮습니다. 스크립트가 두 개 이상인 경우 리스너를 사용하세요.

이벤트 객체: target, currentTarget 및 this

핸들러의 첫 번째 인수는 이벤트입니다. 클릭 시 수정자 키가 눌려진 포인터 위치와 혼합하기 쉬운 두 가지 속성이 유지됩니다.

  • **e.target**는 이벤트가 시작된 위치, 즉 포인터 아래의 가장 안쪽 요소입니다. 이는 <span>일 수도 있고 버튼 내부의 아이콘일 수도 있습니다.
  • **e.currentTarget**는 현재 리스너가 실행 중인 요소입니다. 항상 addEventListener라고 부르는 요소입니다.

위의 예에서 내부를 클릭하면 모든 줄에 target: inner가 기록되고, 이벤트가 각 상자를 통과할 때 currentTarget가 변경됩니다. 버튼을 원하고 해당 아이콘이 있으면 e.currentTarget 또는 e.target.closest('button')를 사용하세요.

이것은 화살표 기능과 일반 기능에서

일반 function로 작성된 리스너 내에서 this는 e.currentTarget와 마찬가지로 리스너가 있는 요소입니다. 화살표 함수 내에서 this는 대신 주변 코드(종종 undefined 또는 window)에서 나옵니다.

button.addEventListener('click', function () {
  this.classList.add('done');   // this === button
});

button.addEventListener('click', (e) => {
  e.currentTarget.classList.add('done');  // arrow: use currentTarget
});

e.currentTarget를 사용하면 두 가지 스타일 모두에서 작동하므로 더 안전한 습관입니다.

일반적인 이벤트

이벤트 객체가 보유하는 다른 내용은 유형에 따라 다릅니다. keydown 이벤트는 e.key를 전달합니다. 포인터 이벤트는 e.clientX 및 e.clientY를 전달합니다.

이벤트 다음과 같은 경우에 발생합니다. 메모
click 버튼이나 요소가 활성화되었습니다. 또한 초점이 맞춰진 버튼에서 Enter 또는 Space를 실행하는 경우에도 실행됩니다.
input 키를 누를 때마다 필드 값이 변경됩니다. 실시간 미리보기 및 카운터용
change 값이 커밋되었습니다. 텍스트 필드: 필드를 떠날 때. 체크박스 및 선택: 한 번에
submit 양식이 전송되었습니다. 버튼이 아닌 <form>에서 들어보세요
keydown 키를 눌렀습니다. e.key, e.ctrlKey, e.metaKey, e.shiftKey 읽기
pointerdown, pointermove, pointerup 마우스, 펜 또는 손가락 세 가지 모두에 대한 하나의 코드 세트
scroll 요소 또는 페이지가 스크롤됩니다. 취소할 수 없습니다
DOMContentLoaded HTML이 구문 분석되었습니다. document에서 들어보세요

버블링 및 캡처: 이벤트가 이동하는 곳

한 요소에서만 클릭이 실행되지 않습니다. 세 단계로 이동합니다.

창 아래에서 캡처가 실행되고 대상이 발사된 다음 버블링이 다시 실행됩니다.
창 아래에서 캡처가 실행되고 대상이 발사된 다음 버블링이 다시 실행됩니다.
  1. 캡처: window에서 클릭한 요소의 상위 요소까지. { capture: true }로 추가된 리스너만 여기에서 실행됩니다.
  2. 대상: 클릭한 요소 자체.
  3. 버블: 모든 상위 항목을 통해 window에 백업합니다. 일반 청취자가 여기에서 실행됩니다.

click, input, change, submit 및 keydown를 포함한 대부분의 이벤트가 버블링됩니다. focus, blur, mouseenter 및 load와 같은 일부는 그렇지 않습니다. 초점을 맞추기 위해 버블링 버전은 focusin 및 focusout입니다.

버블링을 통해 이벤트 위임이 작동합니다. 목록의 하나의 리스너는 나중에 추가된 항목을 포함하여 모든 항목에 대한 클릭을 처리합니다. HTML 및 JavaScript의 DOM은 패턴을 단계별로 설명하고 아래 완성된 예에서는 이를 사용합니다.

PreventDefault 대 stopPropagation

이 두 가지 방법은 비슷하게 들리지만 관련이 없는 작업을 수행합니다. 첫 번째 예에서 stopPropagation을 선택하면 두 번째 예가 로그를 짧게 자르는 것을 볼 수 있습니다.

PreventDefault는 브라우저 자체 작업을 취소합니다. stopPropagation은 이벤트가 더 많은 요소에 도달하는 것을 중지합니다.
PreventDefault는 브라우저 자체 작업을 취소합니다. stopPropagation은 이벤트가 더 많은 요소에 도달하는 것을 중지합니다.
link.addEventListener('click', (e) => {
  e.preventDefault();     // don't follow the link
  openPreview(link.href);
});

링크가 탐색되지 않도록 하거나 양식이 페이지를 다시 로드하지 못하게 하려면 preventDefault()를 사용하십시오. HTML로 제출된 양식은 제출 사례 전체를 보여줍니다.

stopPropagation()를 거의 사용하지 마십시오. 또한 위임된 리스너와 외부 클릭 시 메뉴를 닫는 코드로부터 이벤트를 숨겨 버그를 추적하기 어렵게 만듭니다. 일반적으로 상위 항목에서 e.target를 확인하는 것이 더 깔끔하게 수정됩니다.

옵션 개체: 한 번, 수동, 캡처, 신호

세 번째 인수는 옵션 개체입니다. 각각은 청취자의 행동 방식을 변경합니다.

옵션 기능 다음 용도로 사용하세요.
once: true 첫 번째 실행 후 리스너를 제거합니다. 환영 메시지, 첫 번째 클릭 설정
passive: true preventDefault()를 호출하지 않겠다고 약속합니다. 전화가 걸려오면 무시됩니다 읽기 전용 touchstart, touchmove 및 wheel 리스너
capture: true 캡처 단계에서 리스너를 실행합니다. 아이들이 사건을 다루기 전에 사건을 보는 것
signal AbortController가 중단되면 리스너를 제거합니다. 한 번의 호출로 많은 리스너 제거

passive는 preventDefault()를 호출하는 경우 터치 및 휠 리스너가 완료될 때까지 브라우저가 스크롤을 시작할 수 없기 때문에 존재합니다. 수동으로 표시하면 스크롤이 즉시 시작됩니다. 일부 브라우저는 window, document 또는 body에 연결될 때 기본적으로 이러한 리스너를 수동으로 만듭니다.

signal는 깔끔하게 정리하는 방법입니다. 동일한 신호를 여러 리스너에 전달한 후 abort()를 한 번 호출합니다.

const controller = new AbortController();
const { signal } = controller;

window.addEventListener('resize', onResize, { signal });
document.addEventListener('keydown', onKey, { signal });

controller.abort();  // both listeners are gone

RemoveEventListener에는 동일한 기능이 필요합니다.

removeEventListener는 해당 유형, 캡처 설정 및 함수 객체를 기준으로 제거할 리스너를 찾습니다. 함수의 코드가 아니라 객체 자체입니다.

동일한 텍스트를 가진 두 개의 화살표 기능은 여전히 두 가지 다른 기능입니다.
동일한 텍스트를 가진 두 개의 화살표 기능은 여전히 두 가지 다른 기능입니다.

JavaScript는 () => save()를 평가할 때마다 새로운 함수를 생성합니다. 따라서 removeEventListener에 전달된 인라인 화살표는 추가한 화살표와 절대 일치하지 않습니다. 호출에서는 아무것도 찾지 못하며 오류도 발생하지 않습니다. 아래 예에서 두 가지를 모두 시도해 보세요.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Adding and removing listeners</title>
<style>
  body { margin: 0; padding: 12px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  #target { display: block; width: 100%; padding: 14px; font: 700 16px system-ui; border: 0; border-radius: 10px;
            background: #2563eb; color: #fff; cursor: pointer; }
  .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin: 10px 0; }
  .grid button { padding: 8px 6px; font: 13px system-ui; color: #1d2330; border: 1px solid #cfd4dc; border-radius: 8px; background: #fff; cursor: pointer; }
  .grid .bad { border-color: #f3c3b3; background: #fff7f5; }
  .grid .good { border-color: #b9e2c6; background: #f4fbf6; }
  #log { margin: 0; padding: 10px; height: 190px; overflow: auto; background: #111827; color: #e5e7eb;
         border-radius: 10px; font: 12.5px/1.55 ui-monospace, Consolas, monospace; white-space: pre-wrap; }
  .fired { color: #fde68a; } .note { color: #9ca3af; }
</style>
</head>
<body>
<button id="target">Click me</button>

<div class="grid">
  <button class="bad" id="addAnon">Add anonymous</button>
  <button class="bad" id="removeAnon">Remove anonymous</button>
  <button class="good" id="addNamed">Add named</button>
  <button class="good" id="removeNamed">Remove named</button>
  <button id="addOnce">Add once: true</button>
  <button id="addSignal">Add 3 with a signal</button>
  <button id="abort">controller.abort()</button>
  <button id="clear">Clear log</button>
</div>
<pre id="log"></pre>

<script>
  const target = document.getElementById('target');
  const log = document.getElementById('log');
  const say = (text, cls) => {
    log.insertAdjacentHTML('beforeend', '<span class="' + cls + '">' + text + '\n</span>');
    log.scrollTop = log.scrollHeight;
  };
  const on = (id, fn) => document.getElementById(id).addEventListener('click', fn);

  // 1. Anonymous: each arrow is a new function, so remove never finds a match
  on('addAnon', () => {
    target.addEventListener('click', () => say('anonymous handler ran', 'fired'));
    say('added an anonymous function', 'note');
  });
  on('removeAnon', () => {
    target.removeEventListener('click', () => say('anonymous handler ran', 'fired'));
    say('removeEventListener with a new arrow: nothing removed', 'note');
  });

  // 2. Named: the same reference can be removed, and adding it twice is ignored
  function handleClick() { say('named handler ran', 'fired'); }
  on('addNamed', () => {
    target.addEventListener('click', handleClick);
    say('added handleClick (a second add is ignored)', 'note');
  });
  on('removeNamed', () => {
    target.removeEventListener('click', handleClick);
    say('removed handleClick', 'note');
  });

  // 3. once: the listener removes itself after its first run
  on('addOnce', () => {
    target.addEventListener('click', () => say('once handler ran, now gone', 'fired'), { once: true });
    say('added a once listener', 'note');
  });

  // 4. signal: one abort() removes every listener added with that signal
  let controller = new AbortController();
  on('addSignal', () => {
    for (const n of [1, 2, 3]) {
      target.addEventListener('click', () => say('signal handler ' + n + ' ran', 'fired'),
                              { signal: controller.signal });
    }
    say('added 3 listeners with one signal', 'note');
  });
  on('abort', () => {
    controller.abort();
    controller = new AbortController();  // an aborted signal cannot be reused
    say('abort(): all signal listeners removed', 'note');
  });

  on('clear', () => { log.textContent = ''; });
  target.addEventListener('click', () => say('-- click --', 'note'), { capture: true });
</script>
</body>
</html>
리스너를 추가한 다음 파란색 버튼을 클릭하세요. 익명 함수를 제거해도 아무 일도 일어나지 않습니다. 이름이 붙은 것이 나옵니다.

동일한 규칙은 우호적인 부작용을 설명합니다. 동일한 캡처 설정으로 동일한 이름의 함수를 두 번 추가하는 것은 무시되므로 여전히 한 번 실행됩니다. 인라인 화살표를 두 번 추가하면 두 개의 리스너가 제공됩니다.

완성된 예: 바로가기 및 위임된 목록

이 예에서는 조각들을 하나로 합칩니다. document의 하나의 keydown 리스너는 바로가기를 처리하고, 목록의 하나의 click 리스너는 추가한 행을 포함하여 모든 행을 처리합니다.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Keyboard shortcuts and a delegated list</title>
<style>
  body { margin: 0; padding: 12px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  form { display: flex; gap: 6px; }
  #text { flex: 1; min-width: 0; padding: 9px 10px; font: 15px system-ui; border: 1px solid #cfd4dc; border-radius: 8px; }
  form button { padding: 9px 14px; font: 600 14px system-ui; border: 0; border-radius: 8px; background: #2563eb; color: #fff; }
  ul { list-style: none; margin: 10px 0; padding: 0; }
  li { display: flex; align-items: center; gap: 8px; padding: 8px 10px; margin-bottom: 5px;
       background: #fff; border: 2px solid transparent; border-radius: 8px; font-size: 14px; }
  li.selected { border-color: #2563eb; }
  li.done span { text-decoration: line-through; color: #9aa3b2; }
  li span { flex: 1; }
  li button { padding: 4px 8px; font: 12px system-ui; color: #1d2330; border: 1px solid #cfd4dc; border-radius: 6px; background: #fff; cursor: pointer; }
  .keys { font-size: 12.5px; color: #4b5563; line-height: 1.8; }
  kbd { font: 600 11.5px ui-monospace, Consolas, monospace; background: #fff; border: 1px solid #cfd4dc; border-radius: 4px; padding: 0 4px; }
  #status { font-size: 12.5px; color: #0f5132; min-height: 1.4em; margin-top: 4px; }
</style>
</head>
<body>
<form id="add">
  <input id="text" placeholder="New task, then Enter" autocomplete="off">
  <button>Add</button>
</form>
<ul id="list">
  <li><span>Write the brief</span><button data-action="done">Done</button><button data-action="delete">Delete</button></li>
  <li><span>Send the link</span><button data-action="done">Done</button><button data-action="delete">Delete</button></li>
  <li><span>Check it on a phone</span><button data-action="done">Done</button><button data-action="delete">Delete</button></li>
</ul>
<div class="keys">
  <kbd>/</kbd> type a task &nbsp; <kbd>j</kbd> <kbd>k</kbd> move &nbsp; <kbd>x</kbd> done &nbsp;
  <kbd>Shift</kbd>+<kbd>D</kbd> delete &nbsp; <kbd>Esc</kbd> leave the box
</div>
<div id="status"></div>

<script>
  const form = document.getElementById('add');
  const text = document.getElementById('text');
  const list = document.getElementById('list');
  const status = document.getElementById('status');
  let selected = 0;

  const items = () => [...list.children];
  function select(i) {
    const all = items();
    selected = Math.max(0, Math.min(i, all.length - 1));
    all.forEach((li, n) => li.classList.toggle('selected', n === selected));
  }

  // Adding: submit fires for the button and for Enter in the box
  form.addEventListener('submit', (e) => {
    e.preventDefault();  // stay on the page
    if (!text.value.trim()) return;
    const li = document.createElement('li');
    li.innerHTML = '<span></span><button data-action="done">Done</button><button data-action="delete">Delete</button>';
    li.querySelector('span').textContent = text.value.trim();
    list.append(li);  // no listener to attach: the list handles it
    text.value = '';
    status.textContent = 'Added. Its buttons already work.';
  });

  // One listener on the list handles every row, old and new
  list.addEventListener('click', (e) => {
    const li = e.target.closest('li');
    if (!li) return;
    select(items().indexOf(li));
    const btn = e.target.closest('button[data-action]');
    if (!btn) return;
    if (btn.dataset.action === 'done') li.classList.toggle('done');
    if (btn.dataset.action === 'delete') { li.remove(); select(selected); }
  });

  // Keyboard shortcuts for the whole page
  document.addEventListener('keydown', (e) => {
    if (e.target.closest('input, textarea, select, [contenteditable]')) {
      if (e.key === 'Escape') e.target.blur();  // the only key we take while typing
      return;
    }
    if (e.ctrlKey || e.metaKey || e.altKey) return;  // leave browser shortcuts alone

    const li = items()[selected];
    if (e.key === '/') { e.preventDefault(); text.focus(); }  // don't type the "/"
    else if (e.key === 'j') select(selected + 1);
    else if (e.key === 'k') select(selected - 1);
    else if (e.key === 'x' && li) li.classList.toggle('done');
    else if (e.key === 'D' && e.shiftKey && li) { li.remove(); select(selected); }
    else return;
    status.textContent = 'Shortcut: ' + e.key;
  });

  select(0);
</script>
</body>
</html>
/를 눌러 입력하고, j와 k를 눌러 이동하고, x를 눌러 완료로 표시하고, Shift+D를 눌러 삭제하세요. 전화기에서는 버튼을 탭합니다.

각 부분의 역할:

  • 입력 무시: e.target가 입력일 때 keydown 리스너가 일찍 반환되므로 상자에서 j를 누르면 j가 입력됩니다.
  • 브라우저 단축키는 그대로 둡니다. Ctrl, Cmd 또는 Alt를 누르고 있으면 리스너는 아무 작업도 수행하지 않습니다.
  • 수정자 키: e.shiftKey와 e.key === 'D'를 함께 사용하면 Shift+D가 일반 d와 다릅니다.
  • 위임: 새 행에는 자체 리스너가 필요하지 않습니다. e.target.closest('button[data-action]')는 클릭된 버튼을 찾습니다.

처음에 요소를 선택하기 위해 querySelector는 선택기와 closest()를 포함합니다.

작동하지 않을 때

보이는 것(크롬 문구) 원인 수정
Cannot read properties of null (reading 'addEventListener') 요소가 존재하기 전에 스크립트가 실행되었거나 선택기와 일치하는 항목이 없습니다. 스크립트를 body 끝으로 이동하고 defer를 추가한 후 선택기를 확인하세요.
핸들러는 페이지 로드 시 한 번 실행되고 클릭 시에는 실행되지 않습니다. handler()가 전달되었으므로 반환 값이 리스너가 되었습니다. 괄호 없이 handler를 전달하세요.
removeEventListener는 아무것도 하지 않습니다 리스너는 인라인 익명 함수였습니다. 명명된 참조를 유지하거나 signal를 사용하세요.
preventDefault()가 무시되고 페이지가 계속 스크롤됩니다. 리스너는 선택적으로 또는 기본적으로 수동적입니다. 해당 리스너에 { passive: false }를 추가하세요.
이벤트가 두 번 발생합니다. 리스너가 두 번 추가되었습니다. 종종 다시 실행되는 설정 코드에 의해 추가되었습니다. 한 번 추가하거나 이름이 지정된 함수를 사용하여 반복 추가가 무시되도록 합니다.
버튼 안의 아이콘을 클릭하면 잘못된 요소가 표시됩니다. e.target는 아이콘입니다 e.currentTarget 또는 closest('button')를 사용하세요.

전혀 실행되지 않는 스크립트의 경우 HTML JavaScript가 작동하지 않음에서 원인을 하나씩 살펴봅니다.

링크로 공유하세요

이벤트 리스너는 누군가가 클릭할 때만 그 가치를 보여줍니다. 바로가기 패널의 스크린샷을 시도할 수 없으며 .html 첨부 파일이 전화기에서 일반 코드로 열릴 수 있습니다.

작업 버전을 보내려면 페이지를 NOS 문서에 붙여넣고 공유 링크 만들기를 선택하세요. 링크할 HTML이 이를 안내합니다.

페이지는 작성된 대로 렌더링되고 해당 스크립트가 실행되므로 페이지를 받는 사람은 키를 누르고 버튼을 직접 클릭할 수 있습니다. 나중에 코드를 변경하면 동일한 링크에 새 버전이 표시됩니다.

자주 묻는 질문

addEventListener와 onclick의 차이점은 무엇입니까?

onclick은 단일 슬롯입니다. 새 기능을 할당하거나 onclick 속성을 설정하면 이전 기능이 대체됩니다. addEventListener는 목록에 추가되므로 여러 리스너가 동일한 클릭에 반응할 수 있으며 1회, 수동, 캡처 및 신호와 같은 옵션을 허용합니다.

RemoveEventListener가 작동하지 않는 이유는 무엇입니까?

동일한 유형, 동일한 함수 개체 및 추가하는 데 사용된 동일한 캡처 설정을 전달할 때만 리스너를 제거합니다. 인라인으로 작성된 익명 또는 화살표 함수는 다시 작성하면 새 함수가 생성되므로 일치할 수 없습니다. 명명된 참조를 유지하거나 AbortController 신호를 사용하십시오.

e.target과 e.currentTarget의 차이점은 무엇입니까?

e.target은 이벤트가 시작된 요소입니다(예: 버튼 내부의 아이콘). e.currentTarget은 현재 리스너가 실행 중인 요소입니다. 목록의 위임된 리스너에서 target은 클릭된 하위 항목이고 currentTarget은 목록입니다.

addEventListener가 HTML 속성에서 작동합니까?

아니요. HTML에서는 onclick="save()"와 같은 인라인 핸들러를 작성합니다. addEventListener는 스크립트에서 선택한 요소(예: querySelector)에서 호출되는 JavaScript 메소드입니다.

내 이벤트가 두 번 실행되는 이유는 무엇입니까?

일반적으로 리스너를 추가하는 코드는 두 번 실행되며 각 실행은 새로운 익명 함수를 전달하므로 둘 다 유지됩니다. 동일한 이름의 함수를 두 번 추가하면 무시됩니다. 또 다른 원인은 자식에 대한 하나의 리스너와 부모에 대한 하나의 리스너가 모두 동일한 버블링 이벤트에 반응하기 때문입니다.

함께 읽어 보기