순수한 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]) }
충돌은 발생하지 않지만 이것이 최선의 해결책은 아닙니다. 우리는 불필요한 기능을 많이 할당합니다. 잠재적으로 수천 건의 호출을 처리하기 위해 한 번만 연결하고 하나의 함수만 바인딩하도록 리팩토링해 보겠습니다.
당시 클릭한 객체를 저장하기 위해 버튼 변수를 닫는 대신 이벤트 객체를 사용하여 당시 클릭한 객체를 가져올 수 있습니다.
<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>
나쁘지 않아요! 그러나 이는 단일 함수를 단순화하고 더 읽기 쉽게 만들 뿐이지만 여전히 여러 번 바인딩됩니다.
하지만 우리는 더 잘할 수 있습니다.
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); }
버튼 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>
| UL | |
V #document/ 종료
에서 캡쳐 가능하니 참고하세요 클릭으로 생성된 이벤트가 상위 요소인 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是非常低成本的一项选择。