Nest Javascript custom events - javascript

There are more custom onclick events on a page. I want for every custom onclick event to first prevent the default, do my things and then pass the command to the first custom event.
In other words I want to put my event on top of any other assigned click events. How can I do that?

You may see this answer.
OR
Refering to this explanation you can choose where to get involved in the event-dispatching-process.
It states clearly
In the browsers that support the W3C DOM, a traditional event registration
element1.onclick = doSomething2;
is seen as a registration in the bubbling phase.
So you have to hook into the capturing phase:
element.addEventListener('click', function() {
//do your stuff
},true);
This way your event is on top of other assigned click events.
But it is still a question, if the browser supports this kind of event handling.

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);

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.

jQuery/Javascript temporarily disable events attached by addEventListener/attachEvent

Is there a way to temporarily disable an event listener?
In my case, I have a third party library (not jQuery centric) that creates mouseover/mouseout events on an element using addEventListener/attachEvent.
Under certain circumstances another event fires on a different element and I need to disable those event listeners. My solution thus far has been to simply unbind the mouseover/mouseout. This usually works fine because that event generally causes the page to refresh.
However, every now and again an error can occur (think validation error) that results in the page not refreshing, and I need to re-attach the mouseover/mouseout event listeners.
Helpful information
It's probably worth mentioning that because the mouseover/mouseout event listeners are created and attached within a third party library I cannot simply assign the event to a variable and bind/unbind it in that manner (which AFIK is the best way to do this).
Update
I had originally asked
Is there a way in jQuery to get the event listeners already assigned to an object?
I have since found out it is impossible to access events assigned by addEventListener/attachEvent: Access events added with attachEvent() / addEventListener() in JavaScript
jQuery uses data to store events internally, so you can use it to get all of the event handlers for an object:
$("#foo").data("events")
You can then remove a specific handler by using unbind:
$("#foo").unbind('click', $("#foo").data("events").click[42]);
Unfortunately, you can't access them. At best, you can remove event listeners using W3C's removeEventListener (docs) and/or Microsofts detachEvent (docs). Once the listener is removed, however, it's gone for good.
There's one caveat with removeEventListener, in that if the event was registered twice, once indicating to capture, and once indicating not to capture, you must remove it twice; once for each case.
To learn more about capturing and not capturing, see the W3C spec.
If you want to temporarily disable an event handler being run, why not just add escape code to the function?
like so:
$('#button').click(function(){
var clicked_element = $(this);
if(elem.hasClass('event-click-disabled'))
{
// logging code so we know exactly what events are being skipped
console.info(
'The click event on following element was skipped',
clicked_element
);
return;
}
alert('Button clicked');
});
Then if you want to disable an event on a specific element, just call
element.addClass('event-click-disabled');
The event handler is still run, but it will return immediately.

Categories