JavaScript의 이벤트 라우팅 버블링 프로세스 및 위임 메커니즘

伊谢尔伦
풀어 주다: 2016-11-23 10:11:45
원래의
928명이 탐색했습니다.

순수한 CSS를 사용하여 구현할 때. 저는 기능을 완성하기 위해 JavaScript와 스타일 클래스를 사용하기 시작했습니다.

그런 다음 몇 가지 아이디어가 있습니다. 위임된 이벤트(이벤트 위임)를 사용하고 싶지만 종속성을 갖고 싶지 않고 jQuery를 포함한 모든 라이브러리를 연결하고 싶습니다. 이벤트 위임을 직접 구현해야 합니다.

먼저 이벤트 위임이 무엇인지 살펴볼까요? 작동 방식과 이 메커니즘을 구현하는 방법입니다.

좋습니다. 어떤 문제가 해결되나요?

먼저 간단한 예를 살펴보겠습니다.

한 번에 하나의 버튼을 클릭하고 클릭한 상태를 "활성"으로 설정하려는 버튼 세트가 있다고 가정해 보겠습니다. 다시 클릭하면 활성화가 취소됩니다.

그런 다음 몇 가지 HTML을 작성할 수 있습니다.

<ul class="toolbar">
  <li><button class="btn">Pencil</button></li>
  <li><button class="btn">Pen</button></li>
  <li><button class="btn">Eraser</button></li>
</ul>
로그인 후 복사
로그인 후 복사

일부 표준 Javascript 이벤트를 사용하여 위 논리를 처리할 수 있습니다.

var buttons = document.querySelectorAll(".toolbar .btn");
for(var i = 0; i < buttons.length; i++) {
  var button = buttons[i];
  button.addEventListener("click", function() {
    if(!button.classList.contains("active"))
      button.classList.add("active");
    else
      button.classList.remove("active");
  });
}
로그인 후 복사

보기에는 좋지만, 실제로는 예상한 대로 작동하지 않습니다.

클로저의 함정

JavaScript 개발 경험이 있다면 이 문제는 분명할 것입니다. <…

첫 번째 루프는 첫 번째 버튼을 가리키고 그 다음에는 두 번째 버튼을 가리킵니다. 그러나 클릭하면 버튼 변수가 항상 마지막 버튼 요소를 가리킵니다. 이것이 문제입니다.

우리에게 필요한 것은 안정적인 범위입니다.

참고* 위 코드의 구조는 약간 복잡합니다. 아래와 같이 간단히 클로저를 사용하여 현재 버튼 변수를 닫고 저장할 수도 있습니다. > 이제 이제 잘 작동합니다. 올바른 버튼을 가리키는 것은 항상
var buttons = document.querySelectorAll(".toolbar button");
var createToolbarButtonHandler = function(button) {
  return function() {
    if(!button.classList.contains("active"))
      button.classList.add("active");
    else
      button.classList.remove("active");
  };
};
for(var i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener("click", createToolBarButtonHandler(buttons[i]));
}
로그인 후 복사

입니다. 그렇다면 이 해결 방법에는 무엇이 문제일까요?

var buttons = document.querySelectorAll(".toolbar .btn");
for(var i = 0; i < buttons.length; i++) {
  (function(button) {
    button.addEventListener("click", function() {
      if(!button.classList.contains("active"))
        button.classList.add("active");
      else
        button.classList.remove("active");
    });
  })(buttons[i])
}
로그인 후 복사
이 솔루션은 괜찮아 보이지만 실제로는 더 나은 방법이 있습니다.

먼저 처리 기능을 너무 많이 만들었습니다. 이벤트 리스너와 콜백 핸들러는 일치하는 각 .toolbar 버튼에 바인딩됩니다. 버튼이 3개만 있는 경우 이 리소스 할당을 무시할 수 있습니다.

그런데 1,000개면 어떨까요?

충돌은 발생하지 않지만 이것이 최선의 해결책은 아닙니다. 우리는 불필요한 기능을 많이 할당합니다. 잠재적으로 수천 건의 호출을 처리하기 위해 한 번만 연결하고 하나의 함수만 바인딩하도록 리팩토링해 보겠습니다.

당시 클릭한 객체를 저장하기 위해 버튼 변수를 닫는 대신 이벤트 객체를 사용하여 당시 클릭한 객체를 가져올 수 있습니다.

<ul class="toolbar">
  <li><button id="button_0001">Foo</button></li>
  <li><button id="button_0002">Bar</button></li>
  // ... 997 more elements ...
  <li><button id="button_1000">baz</button></li>
</ul>
로그인 후 복사
이벤트 개체에는 여러 개의 바인딩이 있는 경우 currentTarget을 사용하여 현재 바인딩된 개체를 가져올 수 있습니다. 위 예제의 코드는

나쁘지 않아요! 그러나 이는 단일 함수를 단순화하고 더 읽기 쉽게 만들 뿐이지만 여전히 여러 번 바인딩됩니다.

하지만 우리는 더 잘할 수 있습니다.

var buttons = document.querySelectorAll(".toolbar button");
var toolbarButtonHandler = function(e) {
  var button = e.currentTarget;
  if(!button.classList.contains("active"))
    button.classList.add("active");
  else
    button.classList.remove("active");
};
for(var i = 0; i < buttons.length; i++) {
  button.addEventListener("click", toolbarButtonHandler);
}
로그인 후 복사
이 목록에 일부 버튼을 동적으로 추가한다고 가정해 보겠습니다. 그런 다음 이러한 동적 요소에 대한 이벤트 바인딩도 추가하고 제거합니다. 그런 다음 이러한 처리 기능과 현재 컨텍스트에서 사용되는 변수를 유지해야 하는데 이는 신뢰할 수 없는 것처럼 들립니다.

어쩌면 다른 방법이 있을 수도 있습니다.

먼저 이벤트가 작동하는 방식과 DOM에서 전달되는 방식을 완전히 이해해 보겠습니다.

이벤트 작동 방식

사용자가 요소를 클릭하면 사용자에게 현재 동작을 알리는 이벤트가 생성됩니다. 이벤트가 전달되는 단계는 세 가지 단계가 있습니다.

캡처 단계: 캡처

트리거 단계: 대상

버블 단계: Bubbling

이 이벤트가 시작됩니다. 문서 앞에서부터 아래로 작업하여 현재 이벤트에서 클릭한 개체를 찾습니다. 이벤트가 클릭한 객체에 도달하면 전체 DOM 트리를 종료할 때까지 원래 경로(버블링 프로세스)를 따라 반환됩니다.

다음은 HTML 예입니다.

버튼 A를 클릭하면 이벤트 경로가 다음과 같이 표시됩니다.

START

|
<html>
<body>
  <ul>
    <li id="li_1"><button id="button_1">Button A</button></li>
    <li id="li_2"><button id="button_2">Button B</button></li>
    <li id="li_3"><button id="button_3">Button C</button></li>
  </ul>
</body>
</html>
로그인 후 복사
| HTML                                                  

| UL | |

V #document/

종료

에서 캡쳐 가능하니 참고하세요 클릭으로 생성된 이벤트가 상위 요소인 ul 요소를 통과할 것이라고 확신합니다. 이벤트 처리를 상위 요소에 바인딩하고 솔루션을 단순화할 수 있습니다. 이를 이벤트 위임 및 프록시(위임된 이벤트)라고 합니다.

  注* 其实Flash/Silverlight/WPF开发的事件机制是非常近似的,这里有一张他们的事件流程图。 除了Silverlight 3使用了旧版IE的仅有冒泡阶段的事件模型外,基本上也都有这三个阶段。(旧版IE和SL3的事件处理只有一个从触发对象冒泡到根对象的过程,可能是为了简化事件的处理机制。)

JavaScript의 이벤트 라우팅 버블링 프로세스 및 위임 메커니즘

事件委托代理

  委托(代理)事件是那些被绑定到父级元素的事件,但是只有当满足一定匹配条件时才会被挪。

  让我们看一个具体的例子,我们看看上文的那个工具栏的例子:

<ul class="toolbar">
  <li><button class="btn">Pencil</button></li>
  <li><button class="btn">Pen</button></li>
  <li><button class="btn">Eraser</button></li>
</ul>
로그인 후 복사
로그인 후 복사

  因为我们知道单击button元素会冒泡到UL.toolbar元素,让我们将事件处理放到这里试试。我们需要稍微调整一下:

var toolbar = document.querySelector(".toolbar");
toolbar.addEventListener("click", function(e) {
  var button = e.target;
  if(!button.classList.contains("active"))
    button.classList.add("active");
  else
    button.classList.remove("active");
});
로그인 후 복사

  这样我们清理了大量的代码,再也没有循环了。注意我们使用了e.target代替了之前的e.currentTarget。这是因为我们在一个不同的层次上面进行了事件侦听。

e.target 是当前触发事件的对象,即用户真正单击到的对象。

e.currentTarget 是当前处理事件的对象,即事件绑定的对象。

  在我们的例子中e.currentTarget就是UL.toolbar。

  注* 其实不止事件机制,在整个UI构架上FLEX(不是Flash) /Silverlight /WPF /Android的实现跟WEB也非常相似,都使用XML(HTML)实现模板及元素结构组织,Style(CSS)实现显示样式及UI,脚本(AS3,C#,Java,JS)实现控制。不过Web相对其他平台更加开放,不过历史遗留问题也更多。但是几乎所有的平台都支持Web标准,都内嵌有类似WebView这样的内嵌Web渲染机制,相对各大平台复杂的前端UI框架和学习曲线来说,使用Web技术实现Native APP的前端UI是非常低成本的一项选择。


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