순수한 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); }
good ! 그러나 이는 단일 함수를 단순화하고 더 읽기 쉽게 만들 뿐이지만 여전히 여러 번 바인딩됩니다.
하지만 우리는 더 잘할 수 있습니다.
이 목록에 일부 버튼을 동적으로 추가한다고 가정해 보겠습니다. 그런 다음 이러한 동적 요소에 대한 이벤트 바인딩도 추가하고 제거합니다. 그런 다음 이러한 처리 기능과 현재 컨텍스트에서 사용되는 변수를 유지해야 하는데 이는 신뢰할 수 없는 것처럼 들립니다.
어쩌면 다른 방법이 있을 수도 있습니다.
먼저 이벤트가 어떻게 작동하고 DOM에서 어떻게 전달되는지 완전히 이해해 봅시다.
이벤트 작동 방식
HTML 예는 다음과 같습니다.
<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>
버튼 A를 클릭하면 이벤트 경로는 다음과 같습니다.
START | #document \ | HTML | | BODY } CAPTURE PHASE | UL | | LI#li_1 / | BUTTON <-- TARGET PHASE | LI#li_1 \ | UL | | BODY } BUBBLING PHASE | HTML | v #document / END
이는 이벤트 경로를 클릭하여 생성된 이벤트를 캡처할 수 있음을 의미합니다. 우리는 이 이벤트가 상위 요소인 ul 요소를 통과할 것이라고 확신합니다. 이벤트 처리를 상위 요소에 바인딩하고 솔루션을 단순화할 수 있습니다. 이를 이벤트 위임 및 프록시(위임된 이벤트)라고 합니다.
참고* 실제로 Flash/Silverlight/WPF에서 개발한 이벤트 메커니즘은 매우 유사합니다. Silverlight 3는 버블링 단계만 있는 이전 버전의 IE 이벤트 모델을 사용한다는 점을 제외하면 기본적으로 다음 세 단계로 구성됩니다. (이전 버전의 IE와 SL3의 이벤트 처리는 트리거 객체에서 루트 객체까지 버블링하는 과정만 있었는데, 이는 이벤트 처리 메커니즘을 단순화하기 위함일 수 있습니다.)
委托(代理)事件是那些被绑定到父级元素的事件,但是只有当满足一定匹配条件时才会被挪。
让我们看一个具体的例子,我们看看上文的那个工具栏的例子:
<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是非常低成本的一项选择。
以上就是理解JavaScript中的事件路由冒泡过程及委托代理机制的内容,更多相关内容请关注PHP中文网(www.php.cn)!