Real world example where event capturing is necessary / preferred? - javascript

The addEventListener DOM method supports a third optional, boolean parameter (useCapture) to indicate whether the function should use event bubbling or event capturing as propagation method. In this article the difference is nicely shown (click on the examples & view code).
From other questions on SO and blog posts, I concluded event bubbling was preferred mostly because IE8- didn't support it.
Suppose I'm only required to support IE9+, in what situation would event capturing be necessary or preferred over event bubbling? In other words, in what situation would it be better to let the events execute on the outermost elements first, and then the innermost elements? I'm looking for a simple, real world example to demonstrate the use of event capturing...

Event capturing used to be the only option outside of the Internet Explorer browser:
One of the major differences of the back then two important browsers was how they handled events. Microsoft worked with the bubbling phase - meaning the event first hit on the target element and then traverse the whole DOM upwards hitting on the parent nodes whereas Netscape did it the exact other way - meaning the event first passes the parent elements and then runs down to the target element - capturing. This caused developers in the early days a lot of trouble and the W3C finally specified an approach where both kind of works and can be used at free will.
Event capturing is useful in event delegation when bubbling is not supported. For example:
Some events, such as focus, don't bubble but can be captured.
The inline handler on the target element triggers before capture handlers for the target element.
Many newly specified events in the web platform (such as the media events) do not bubble, which is a problem for frameworks like Ember that rely on event delegation. However, the capture API, which was added in IE9, is invoked properly for all events, and does not require a normalization layer. Not only would supporting the capture API allow us to drop the jQuery dependency, but it would allow us to properly handle these non-bubbling events. This would allow you to use events like playing in your components without having to manually set up event listeners.
Custom events and bubbling have the following issues:
Currently, Ember relies on jQuery for event handling, doing so comes with several costs:
jQuery silently changes inline handlers to bubble handlers.
This changes expected invocation order
This can cause automated tests to fail
Events triggered via jQuery.trigger trigger handlers in a different order than events triggered by a user.
This changes expected invocation order
This leads to difficult to reason about and debug aberrations in behavior
This often causes automated tests to fail
Events must flow down and bubble back up through the entire DOM before being detected by the Ember application, and then must undergo an expensive delegation process that is effectively re-bubbling to find the right handler.
Handlers attached directly within components or by non-ember plugins take precedent over handlers attached by Ember, whether this was desired or not.
This causes component handlers to have far-reaching side-effects
This leads to difficult to reason about and debug aberrations in behavior
This often causes automated tests to fail
A media player focus=>play preprocess/postprocess event flow would be a simple use case.
The mechanics of the capturing phase make it ideal for preparing or preventing behavior that will later be applied by event delegation during the bubbling phase. And that’s how we’re going to use it here—to initialize the sortable in response to a mouse click, but just before the event starts bubbling and other handlers have a chance to deal with it.
To make use of capturing, we have to go down to the metal. jQuery’s event methods only work for bubbling and don’t let us tap into the capturing phase. The capturing handler looks like:
document.addEventListener("mousedown", function(event) {
if ($(event.target).closest(".sortable_handle").length) {
$("article.todolist, section.todolists").sortable();
}
}, true);
References
Domina Github Repo: Readme - Event Propagation
EmberJS RFC: Capture Based Eventing
EmberJS RFC: Internet Explorer
MDN: Event.eventPhase
Using event capturing to improve Basecamp page load times – Signal v. Noise
Bubbling, foreign events and Firefox: Index
Event capture and bubbling

Related

Can I remove event listeners with a Chrome extension?

In Chrome's dev tools, there's a lovely interface where you can see all the event listeners attached to a given DOM element, and remove any of them as you see fit. Here's a screenshot (arrow added for emphasis):
I'd like to write a Chrome extension that automatically removes event listeners from any web page (I'm trying to write a Chrome extension to disable smooth scrolling on any website that tries to force it upon you -- I figure removing the 'wheel' listener from <body> is the most direct route to do this). Is there any JavaScript API available for accessing and modifying this list of event listeners from a Chrome extension, or is it limited to the dev tools GUI?
To be clear, I'm aware of removeEventListener(), but that method requires that you have a reference to the original listener object -- I have no such reference, so that method won't suit my purposes.
eholder0's answer unfortunately doesn't help when the event listener is registered on window (like in your question) or document. For that, one way is that most code and libraries usually registers event listeners on the bubbling phase, by passing in false for the third useCapture parameter of addEventListener (or just not passing it in at all). Since the capturing phase happens first, you can prevent event listeners from being invoked by registering a capturing phase listener that stops further propagation.
So for your case, you can do the following in the extension's content script:
document.addEventListener("wheel", event => event.stopPropagation(), true);
... or more explicitly:
document.addEventListener("wheel", event => event.stopPropagation(), { capture: true });
Notice the third parameter is true to register a capturing phase event listener. Also notice that it does not call event.preventDefault(), so the browser's built-in scrolling function is retained.
An addendum based on the comments: In the case where the handler you want to suppress is on a specific element rather than document, you can also register the capturing handler on that element, so that events in the rest of the document are not impacted.
Unfortunately because of how event listeners are implemented, this isn't trivially possible. There are some libraries you can use which record calls to add event listeners, thereby giving you a reference to remove. Short of using those or rolling your own, the isn't a simple tool to remove them anonymously.
You can however do something which will effectively remove all listeners, which is to clone the element and replace it with the clone. Cloning does not preserve any listeners on the element or its children, though it does otherwise preserve all attributes. Here's an example of how to do that:
var elem = document.getElementById('foo'),
clone = elem.cloneNode(true);
elem.parentNode.replaceChild(clone, elem);

Are JavaScript events always executed even if there is no one listening?

Are events in JavaScript always fired even if there are no listeners attached?
Lets say "mousemove", I move the mouse but there are no listeners in the whole app, will the browser still construct a new Event and fire it, or will it optimize and consider the fact that if there are no event listeners, just ignore the data.
I assume that each browser works differently and I'm assuming they use patterns like observer and what not, but is there a spec around that states how it should be?
Feel free to downvote this if you feel this is not correct but from my understanding and according to the DOM Level 2 Events Spec there is a sense that events are always constructed and executed but listeners need to be there, of course, to actually register them.
The reason I say "there is a sense that events are always constructed and executed" is because the Spec mentions that
This method allows the registration of event listeners on the event
target. If an EventListener is added to an EventTarget while it is
processing an event, it will not be triggered by the current actions
but may be triggered during a later stage of event flow, such as the
bubbling phase. If multiple identical EventListeners are registered on
the same EventTarget with the same parameters the duplicate instances
are discarded. They do not cause the EventListener to be called twice
and since they are discarded they do not need to be removed with the
removeEventListener method.
So if event listeners are dynamically added, there needs to be a way for the the page to know to register and listen to them. How each browser handles this is probably different as #JAAulde mentioned above but I do not think browsers would optimize for the fact that an event listener exists or not or at least nothing drastic.

Closing on HTML click pattern, across large project

I figure other developers have run into this before. We have a large code base with many components following this pattern:
$('#elm').on('click',function($e){
$e.stopPropagation();
//... do stuff (i.e. Open something);
});
$('html').on('click',function($e){
//... do oposite of stuff (i.e. Close something);
}
Our issue is, all the stopPropagation's across the site are stopping closing of other components. What we really want is a mechanism to only block the click handler for this component but not for others.
I'm looking for a solution which is the easiest to implement right now to fix our bugs and for our Multi-developer team to follow in the future.
The .live() method handles events once they have propagated to the top of the document, it is not possible to stop propagation using live events this allows you to do hack and take advantage of this because the .live() method binds a handler to the $(document), and identifies which element triggered the event up at the top of the hierarchy using the event.target property.
The stopPropagation stops the propagation from bubbling up the DOM hierarchy, but since the handler is at the document level there is no upper place to propogate to.
On the other hand note that events handled by .delegate() will bubble up to the elements to which they are binded to; event handlers bound on any elements below it in the DOM tree will already have been executed by the time the delegated event handler is called. These handlers, therefore, may prevent the delegated handler from triggering by calling event.stopPropagation()
See this example: http://jsfiddle.net/knr3v/2/
Therefore you can use the live and delegate method instead of click, bind, on method to do exactly what you are explaining.

Can I attach capture and bubble phase event handler on the same element?

Can I attach capture and bubble phase event handler (could be different or same function) on the same element?
I tried it and it's working fine.
Is it permitted as per W3C?
I don't see any limitation or restriction mentioned in the DOM3 Event specification.
Could someone please clarify it?
var divList = document.getElementsByTagName('div');
var eventHandler = function(event){
console.log(event.currentTarget);
}
for(var index=0; index < divList.length; index++){
divList[index].addEventListener('click',eventHandler,true);
divList[index].addEventListener('click',eventHandler,false);
}
Yes, binding events to both phases is allowed. Here are a few instances where it is very useful:
Programmatic filling of form fields
Programmatic queueing of form submit events
Programmatic syncing of multiple select elements
Some events, such as focus, don't bubble but can be captured.
The inline handler on the target element triggers before capture handlers for the target element.
Many newly specified events in the web platform (such as the media events) do not bubble, which is a problem for frameworks like Ember that rely on event delegation. However, the capture API, which was added in IE9, is invoked properly for all events, and does not require a normalization layer. Not only would supporting the capture API allow us to drop the jQuery dependency, but it would allow us to properly handle these non-bubbling events. This would allow you to use events like playing in your components without having to manually set up event listeners.
References
Domina Github Repo: Readme - Event Propagation
EmberJS RFC: Capture Based Eventing
EmberJS RFC: Internet Explorer
MDN: Event.eventPhase
Yes but it should be discouraged, you should handle logic to trigger other events/ call functions in the callback.
It's rare to have a use case where an element requires more than one of the same event type.

How does event handling work internally within JavaScript?

Specifically Spidermonkey.
I know you write functions and attach them to events to handle them.
Where is the onClick handler defined and how does the JS engine know to fire onClick events when the user clicks?
Any keywords, design patterns, links, etc are appreciated.
UPDATE
Aggregating links I find useful here:
http://www.w3.org/TR/DOM-Level-2-Events/events.html
https://github.com/joyent/node/blob/master/src/node_events.cc
http://mxr.mozilla.org/mozilla/source/dom/src/events/nsJSEventListener.cpp
SpiderMonkey itself doesn't have anything involving event handling. Events are purely a DOM thing.
The click event is fired by the browser code (the thing embedding SpiderMonkey), not by SpiderMonkey itself. See http://hg.mozilla.org/mozilla-central/file/e60b8be7a97b/content/events/src/nsEventStateManager.cpp for the code that's responsible for dispatching things like click.
The browser is also what defines setter methods that take an assignment to the onclick property and turn it into an event listener registration. See http://hg.mozilla.org/mozilla-central/file/e60b8be7a97b/dom/base/nsDOMClassInfo.cpp#l7624 which is called from nsEventReceiverSH::SetProperty and handles properties whose name (id in this code) passes the IsEventName test.
When event listeners are registered and an event is fired, the event dispatcher manages calls to the listeners; the nsJSEventListener link you found is the glue that converts a C++ HandleEvent call into a call to a JS function.
So in your case, you want some sort of registration/unregistration mechanism for listeners and then your implementation will fire events and dispatch them to listeners. How you do this last part is pretty open-ended; the Gecko implementation has a lot of constraints due to needing to implement the DOM Events specification, but you should be able to do something much simpler.
HTML uses sink/bubble event propagation schema: http://catcode.com/domcontent/events/capture.html
There are "physical" events (mouse, keyboard) and logical/synthesized ones (focus,click, value_changed, etc.)
onClick is a logical event - generated as a result of mouse, touch and/or keyboard events.
Mouse (or finger touch) originated click event is a result of mouse down, move and up events. Note that mouse down, move and up are sinking/bubbling events. Target element(s) in these "primordial" events will be the target(or source) of the click event. If mouse-down/up events have different targets (DOM element) then their common parent is used.
Sequence of mouse down, move and up events may produce different logical events: click, swipe/scroll, etc.
I believe this is a full list of basic concepts.

Categories