When I implement this with pure CSS. I started using JavaScript and style classes to refine functionality.
Then, I have some ideas, I want to use delegated events (event delegate) but I don't want to have any dependencies, insert any library, including jquery. I need to implement the event delegate myself.
Let's take a look at what the event delegation is all about. How they work, how to implement this mechanism.
Well, what did it solve?
Let's look at a simple example first.
Let's say we have a set of buttons, I click one button at a time, and then I want to be set to "active" in the point. Cancel active when you click again.
Then, we can write some 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>
I can use some standard JavaScript events to handle the above logic:
var buttons = Document.queryselectorall (". toolbar. btn"); for (var i = 0; i < buttons.length; i++) {var button = button S[i]; Button.addeventlistener ("click", Function () {if (!button.classlist.contains ("active")) Button.classList.add ("Activ E "); else Button.classList.remove ("active"); });}
It looks good, but it can't actually work as you expect.
Traps for closures
If you have some JavaScript development experience, the problem is obvious.
For the layman, the button variable is closed, and each time a corresponding button is found ... But in fact there is only one button, and each cycle is reassigned.
First loop it points to the first button, followed by the second one. But when you click the button variable always points to the last button element, the problem is here.
What we need is a stable scope; let's refactor.
var buttons = Document.queryselectorall (". toolbar button"), var createtoolbarbuttonhandler = function (button) {return Fu Nction () {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]) );}
Note * The above code structure is somewhat complex, or it can be simply and directly using a closure, enclosing the current button variable as follows:
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.ad D ("active"); else Button.classList.remove ("active"); }); }) (Buttons[i])}
Now it's working properly. Point to always be the right button
So what's the problem with this program?
The scheme seems to work, but we can do better.
First, we created too many processing functions. For each matching. toolbar button binds an event listener and a callback handler. If there are only three buttons, this resource allocation can be ignored.
However, what if we have 1000 of them?
<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>
It doesn't crash, but it's not the best solution. We have allocated a large number of unnecessary functions. Let's refactor, append only one function, to handle the thousands of possible calls that can be made.
In relation to the Closed button variable to store the object we clicked on, we can use the event object to get the object that was clicked at that time.
The event object has some metadata, and in the case of multiple bindings, we can use Currenttarget to get the currently bound object, as the code in the example above can be changed to:
var buttons = Document.queryselectorall (". toolbar button"), var toolbarbuttonhandler = function (e) {var button = E.curren Ttarget; 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);}
Not bad! However, this simply simplifies a single function and makes it more readable, yet it is bound more than once.
But we can do it better.
Let's assume that we've dynamically added some buttons to this list. We will then add and remove event bindings for these dynamic elements as well. And then we're going to persist these processing functions and the variables we're going to use for the current context, which doesn't sound like a reliable thing.
There may be other ways.
Let's start with a full understanding of how events work and how they are delivered in the DOM.
How the event works
When the user clicks on an element, an event is generated to inform the user of the current behavior. There are three phases in the dispatch of an event:
- Capture phase: Capturing
- Trigger phase: Target
- Bubbling stage: bubbling
This event starts from the document and then all the way down to find the object that the current event is clicked on. When the event reaches the clicked object, it returns (bubbling) on its original path until it exits the entire DOM tree.
Here is an example of HTML:
When you click Button A, the path of the event passes to the following:
START | #document \ | HTML | | BODY} CAPTURE PHASE | UL | | Li#li_1/| BUTTON <--TARGET PHASE | Li#li_1 \ | UL | | BODY} bubbling PHASE | HTML | V #document/end
Note that this means that you can capture the events that you click on the path of the event, and we are very sure that this event will pass through their parent element, the UL element. We can bind our event handlers to the parent element and then simplify our solution, which is called the event's delegate and agent (delegated events).
Note * In fact, the event mechanism developed by FLASH/SILVERLIGHT/WPF is very approximate and there is a flowchart of their events. In addition to the bubbling-only event model used by Silverlight 3 with the older version of IE, there are basically three stages. (The event handling of older IE and SL3 only has a process of bubbling from the triggering object to the root object, possibly to simplify the handling of the event.) )
Event Delegate AgentDelegate (proxy) events are those that are bound to the parent element, but are only moved if a certain match condition is met.
Let's look at a specific example, let's take a look at the example of the toolbar above:
<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>
Because we know that clicking the button element bubbles to the Ul.toolbar element, let's put the event handling here and try it out. We need to tweak it a little bit:
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");});
This way we clean up a lot of code and never loop anymore. Note that we used the e.target instead of the previous e.currenttarget. This is because we are listening to events on a different level.
- E.target is the object that is currently triggering the event, which is the object that the user is actually clicking on.
- E.currenttarget is the object that is currently handling events, that is, an event-bound object.
In our case, E.currenttarget is Ul.toolbar.
Note * More than just an event mechanism, the implementation of flex (not flash)/silverlight/wpf/android on the entire UI architecture is very similar to the Web, using XML (HTML) to implement templates and element structures, Style (CSS) Implement control for display styles and UI, scripts (AS3,C#,JAVA,JS). But the web is more open than other platforms, but there are more problems left over from history. But almost all platforms support web standards, and embedded web rendering mechanisms like WebView are embedded, and using Web technology to implement the native app's front-end UI is a very low-cost option relative to the complex front-end UI framework and learning curve of each platform.
Original address: Codepen.io
Understanding event Routing bubbling process and principal-agent mechanism in JavaScript