I have a page which has two different event listeners picking up click events from inside the page. One listener is generic to the site, the other is specific to the page. Recently, a link was added which runs through the first handler, which processes it, opens the url in a new window and then stops the event. The problem is, the second handler then executes, stops the event again and somehow the event continues and executes.
I have stopPropagation, and cancelBubble both executing on this event. When it arrives at the second handler, it has a prevented field which is set to true, but still it carries on. The only way to stop it is to put a check in the second handler which skips its code if the event arrives with prevented set to true.
My question is, why would this happen at all? Why is stopPropagation not working? This happens in all browsers, BTW.
I suppose you try to stop event from firing on the same element. Have you tried to use stopImmediatePropagation method?
As Samuel Liew correctly commented, adding return false; to the end of your first event handler might do the trick. This is because some browsers ignore the .stopPropagation() (However, others will ignore the return)
Related
MDN explains:
If an EventListener is added to an EventTarget while it is processing an event, that event does not trigger the listener. However, that same listener may be triggered during a later stage of event flow, such as the bubbling phase.
What are the ways to prevent this from happening?
Possible Solutions:
Call event.stopPropagation() when the event is invoked.
I'm not a fan of this solution because the solution is not localized near the problem. The solution is in where the click originates and the problem is the code that adds the new event listener.
Add the event listener inside of a setTimeout:setTimeout(() => {element.addEventListener('click', clickHandler},0)
This solution is cohesive: the problem of the new eventListener triggering immediately is solved right where the event listener is added. Also it doesn't stop propagation for an event that other listeners might depend on. However, it feels like a hack to wrap it inside a setTimeout like this.
Do better solutions exist to prevent the new event listener from firing immediately due to the click that caused the event listener to be added?
This problem occurred for me when attempting to make a popup appear that closes itself for a click outside. Since the click that creates the popup is also a click outside. The popup closes immediately.
Link to example use-case including solutions
I am trying to write a userscript which creates a text input on a website, however in the website's javascript code, a function called on keypress or something else calls e.preventDefault() which prevents text input. I do not have access and cannot change that function. Is there any way to bypass that?
We have to register an event listener to be able to call preventDefault or stopPropagation. If the methods are called, I believe there's no way negating the effect after the default was prevented or the propagation stopped.
The only chance would be: register you own listener before the website has a chance to register it's own. Which probably happens in a script and if you can find that, you can inject your own script before that. Then your handler would be called first and that could work for you. (you could call stopPropagation and the other listener wouldn't be called anymore)
I always wondered how an event handler in JavaScript was always listening for subsequent actions to trigger the event again and again even after the JavaScript code you write has been parsed.
I've tried to research online but I've not found any resources that explains how the events listeners are kept active or alive.
I understand how the call stack works but I just want to know how all the event listeners are kept alive or active.
For example addEventListener with a "click" event will always run the specified callback if the button is clicked. So how is the callback added by the addEventListener method always kept alive or active?
This behaviour of JavaScript is commonly described as "the event loop". The JavaScript engine runs and runs and runs and keeps looking for new events. It will run until all event handlers were detached. Now if an event gets triggered, it will end up in an event queue. Then somewhen the event loop takes the event out of the queue, and executes it. That will create a callstack, if that unwinded, the loop continues.
JS Engine keeps in memory all the handlers, attached to specific events, and it becomes a JS engine to know what event have happened and what handler was attached to that event... Hopeful this will help
In short
Is there a way in which, when listening to a native event, I can detect if the event was somehow used by CKEditor before it propagated to my listener, or prevent it from propagating at all?
Use case
I'm listening to the keyup event using jQuery, to detect when escape is pressed. When it is, the user is prompted if they want to discard changes, and the CKEditor instance is destroyed and its element removed from the DOM.
$('body').on('keyup', function(e){
if(e.which==27){
CKEDITOR.instances.myDiv.destroy();
$('#myDiv').remove();
}
});
The problem here is that CKEditor allows the user to interact with certain UI elements using the escape key. For instance to close a dialog window or drop-down list.
So my event should only execute its code if CKEditor did not already use the event to close a UI element of its own.
Attempt
I tried to listen to the dialogShow and dialogHide events to detect if a dialog window is open, and my action should thus be ignored. This didn't work for two reasons:
CKEditor handles the event first, so by the time the event propagates to my listener, no dialog windows are open and my code is executed.
Even if it would work, it wouldn't for drop-down lists as they do not trigger the dialog* events.
Ideas
I don't know enough about the workings of CKEditor to come up with a solution, but I think I'm looking for something along the lines of:
A setting in CKEditor to prevent event propagation: CKEDITOR.instances[0].noEventPropagation = true
An indication in the original event object: if(event.CKEditorWasHere){/*do nothing*/}
A plugin providing functionality that I can use.
Worst case scenario: A setTimeout in the dialogHide event which I'll use to suppress my own events for a short time.
So
Maybe I'm completely overlooking something. This seems to me like a common problem which should have a simple solution.
Thanks for your time.
How can I both trigger and bind to an event after the page load?
The problem I am having is that in one place, on page-load I fire an event:
//producer.js
pageLoaded(){
var e = jQuery.Event("eventA");
$("body").trigger(e);
...}
and in another I bind to it:
//consumer.js
pageLoaded(){
$("body").bind("eventA", function(args){console.log("Got it!");} );
... }
The problem is that the first time event is fired (when page is loaded) - the consumer does not see it. The second time event is fired, however, everything is fine!
I am guessing this is because event is actually fired BEFORE consumer starts listening to it.
Is there a good practice to follow when creating "triggers" and "listeners" to events - that have to work both on page load, and later?
If there are no listeners when you triggered an event nothing will happen which is why you are not seeing anything on the first trigger.
Sounds like you need to control the order of document.ready() calls .. perhaps the responses to this question or this article might assist you
Using something like RequireJS will allow me to first load one javascript file, and then another.
So I can make producer.js dependent on consumer.js