I'm injecting some code which creates a text input into existing pages on the internet.
On some sites, when the user presses some specific keys while focused onto my text input, the event gets captured instead of being bubbled.
I've been trying multiple solutions to prevent this but they do not seem to work:
keyUp = event => {
event.preventDefault();
event.nativeEvent.stopImmediatePropagation();
event.stopPropagation();
return false;
};
<Form.Control
as="textarea"
onChange={this.updateInput}
onKeyUp={this.keyUp}
onKeyPress={this.keyUp}
onKeyDown={this.keyUp}
/>
Is there a way I can prevent this event from being triggered on the body instead of being triggered in my element first?
React attaches its event listener to body by default, and uses that to trigger its own SyntheticEvent
You could try attaching your event in capture mode by appending Capture to your event name, as outlined here
Edit (added for clarification to comment):
Attach an event to your window:
window.addEventListener("keydown", function (evt) {
if (evt.target.id === 'elementID') {
//do what you want to, here.
//if you want to prevent the event propagating:
evt.stopImmediatePropagation();
evt.stopPropagation();
}
}, true);
in your jsx:
...
<input id='elementID' />
...
I've added a listener at window level to capture the event and prevent propagation down. Of course if a listener is already added at window level this will not work:
window.addEventListener("keydown", this.keyDown, true);
Related
How can I prevent default functionality for paste event (when trigger from browser menu option Edit->Paste) for monaco editor?
I am handling paste event in the following way:
editor.getContainerDomNode().addEventListener('paste', (event) => {
event.preventDefault();
event.stopPropagation();
}
I also tried to handle the paste event on the textarea which is the source element in event propagation.
let textarea = editor.getContainerDomNode().querySelectorAll("textarea.inputarea.monaco-mouse-cursor-text")[0];
textarea.addEventListener('paste', (event) => {
event.preventDefault();
event.stopPropagation();
});
But here also seems like preventDefault is not working. It is pasting into editor whatever the text is copied.
Bascially I want to disable paste action when triggered from browser Edit menu. Need to know how this can be achieve in monaco-editor. Am I doing anything wrong here or it is a bug of monaco editor?
Your problem comes from the way an event is handled by listeners, when they are set on different ancestor elements.
An event is generally treated on a target element, then it "bubbles" on its ancestors.
On your case, you are adding a listener on a parent element of the editor. Which means that your listener will be executed after any listener on the actual target (which is actually how monaco handles the paste event).
So you are trying to prevent an action that was already executed.
Luckily, there is a way to achieve what you want! It is called "capturing".
By specifying in the call to addEventListener that you want to capture the event on its way down. This can be done by adding a third argument {capture: true}, or simply true:
editor.getDomNode().addEventListener('paste', (event) => {
event.stopPropagation();
event.preventDefault();
}, true);
This should prevent the editor from actually executing the action.
See Documentation on bubbling and capturing events.
I want to be able to activate an element's listener without activating the listener of the div that contains my element.
$('body').on('click', '.thiscoll', function(){
if (type === "form") {
hidePanels();
$('#navbar-pannel').show();
}
});
$('#main_container').on('click', 'a', function(){
hidePanels();
$('#custom-nav').show();
$('#l-name').html("New link name");
$('#l-destination').html("New link destination");
});
The first listener is on my div, while the second listener is on my links that are contained into my div. When I click on a link, it first triggers the 'a' listener, then the '.thiscoll' listener, while I only want to trigger the 'a' listener.
Is it possible?
Thanks.
Long story short, you want to stop event propagation. Something like
$('a').on('click', function(event) {
event.stopPropagation();
// and possibly do something else you require
});
should do.
Yes, what you want is possible. With events in Javascript we have this nice thing called event capturing and event bubbling. By default, browsers will register events in the bubbling phase.
For you, this means that the target you click will have its event handlers fired first. Then its parent's. Then its parent's parent's and so on. You can read more about it on MDN
To stop this propagation, you can use the stopPropagtion method on the Event-object. The Event object is supplied as the first argument in your event listener:
const main = document.querySelector('.main');
const button = document.querySelector('.button');
const stopPropagation = document.getElementById('stopPropagation');
main.addEventListener('click', () => console.log('Clicked on main'));
button.addEventListener('click', (evt) => {
if (stopPropagation.checked) {
evt.stopPropagation();
}
console.log('clicked button');
});
<input id="stopPropagation" type="checkbox">
<label for="stopPropagation">stopPropagation</label>
<div class="main">
Hello, World
<button class="button">Button</button>
</div>
I would like to implement a confirmation logic for links or buttons, where I can annotate the related element with a special data-confirm attribute. If this attribute exists on the element, I would like to attach a click handler which has the power to prevent any other handler including default event, and other jQuery handlers added before or after my confirm handler was added.
Here is my code:
$(document).on("click", "a[data-confirm], button[data-confirm]", function(e) {
var confirmData = $(this).data("confirm");
if (confirmData === "true")
confirmData = $(this).prop("title");
if (confirmData && !confirm(confirmData)) {
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
return false;
}
return true;
});
The problem is that I'm not sure about where this handler will go into the handlers list, so I guess there is a good probability that other handlers could execute before it. Also, I'm not sure if this handler will precede for example knockoutjs' click binding.
To prevent other handlers from being called use stopImmediatePropagation() and to prevent default behavior use preventDefault().
Like so:
$("body").on("click", ".elementClass[attributeName]", function(event){
event.stopImmediatePropagation();
event.preventDefault();
});
By adding the [attributeName] in the selector, the selector will only apply to elements with the attribute "attributeName"
In the example above this will be executed before your $(document) handler as the event bubbles up the DOM and gets to the body before it gets to the document. To ensure that this event is attached sooner, you can attach the handler to the element like so:
$(".elementClass[attributeName]").on("click", function(event){
event.stopImmediatePropagation();
event.preventDefault();
});
The downside to this, is that this handler must be attached after the element has the attribute.
Another technique you can use with just vanilla JavaScript, however, is event capturing: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener.
If true, useCapture indicates that the user wishes to initiate capture. After initiating capture, all events of the specified type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. Events which are bubbling upward through the tree will not trigger a listener designated to use capture
You can do something like this:
document.addEventListener("click", function(event){
if(event.target.hasAttribute("attributeName")){ // jQuery: if($(event.target).attr("attributeName")){...
event.stopPropagation();
event.preventDefault();
//Do stuff
}
}, true);
By adding true at the end the event is captured, meaning this will be run before other handlers.
If an element has more than one click handlers, the execution order of those is unspecified. So you can't reliably make one always execute first.
Although all EventListeners on the EventTarget are guaranteed to be triggered by any event which is received by that EventTarget, no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget.
W3C-Events-flow-basics
I'm trying to make an extension with 'pick' functionality: letting the user click on any element on the page and see the xpath for it.
However, to make it usable I have to prevent the element from reacting on clicking: I don't want clicking on hyperlinks to forward me to another page, buttons to submit forms, etc., etc.
How would I do that?
UPD: I need it only for Chrome
The cleanest way in my opinion would be to bind one event handler to body in the capturing phase and prevent the event from propagating/the default behavior.
You can get a reference to the clicked element with event.target:
document.body.addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
console.log(event.target);
}, true);
The advantage is that this event handler will be the very first that is triggered, no matter which other event handlers are bound to elements in the page (unless of course the page binds a similar event handler to body, but usually event handlers are bound in the bubbling phase).
For more information see quirksmode.org - Event order and MDN - addEventListener.
DEMO
For a CSS only solution:
* {
pointer-events: none;
}
Try this
$('body *').on('click',function(e){
return false;
});
In your click event handler make sure you either call preventDefault() in the event object, or return false from your function. For example:
function myClickHandler(e) {
e.preventDefault() // e is the event object
// Do your click stuff here
.
.
.
return false; // not required if you've called e.preventDefault()
}
Whats the easiest way to temporarily disable all mouse click/drag etc events through javascript?
I thought I could do document.onclick = function() { return false; }; ...etc, but that's not working.
If the objective is to disable click on the whole page then you can do something like this
document.addEventListener("click", handler, true);
function handler(e) {
e.stopPropagation();
e.preventDefault();
}
true argument in addEventListener would ensure that the handler is executed on the event capturing phase i.e a click on any element would first be captured on the document and the listener for document's click event would be executed first before listener for any other element. The trick here is to stop the event from further propagation to the elements below thus ending the dispatch process to make sure that the event doesn't reach the target.
Also you need to stop default behavior associated with event target elements explicitly as they would be executed by default after the dispatch process has finished even if the event was stopped propagating further from above
It can be further modified to use selectively.
function handler(e) {
if(e.target.className=="class_name"){
e.stopPropagation();
e.preventDefault();
}
}
handler modified this way would disable clicks only on elements with class "class_name".
function handler(e) {
if(e.target.className!=="class_name") {
e.stopPropagation()
}
}
this would enable clicks only on elements with class "class_name".
Hope this helped :)
Dynamically disable all clicks on page
let freezeClic = false; // just modify that variable to disable all clics events
document.addEventListener("click", e => {
if (freezeClic) {
e.stopPropagation();
e.preventDefault();
}
}, true);
I often use it while loading or to avoid user to accidentally clic twice on an action button. Simple and performance friendly :)
Please check this working example
Alternative CSS way
Another one that I really like because of the visual feedback the user have:
/* style.css */
.loading {
cursor: wait; /* busy cursor feedback */
}
.loading * {
/* disable all mouse events on children elements */
pointer-events: none;
}
A simple example to dynamically add the .loading class:
const elm = document.getElementById('myElm')
elm.classList.add('loading')
myAsyncFunction().then(() => elm.classList.remove('loading'))
If you want absolutely nothing draggable/clickable, disabling typing in input fields etc, I'd consider showing a absolutely positioned transparent div over the entire page, so that every click will be on the div, which will do nothing. That will grant you swift and neat switching on and off of this click-disabler, without having to register heaps of listeners
The winning answer works well, but if you had pass the capture true boolean value, at the moment you want to remove the listener, you have to pass the exact same value. Otherwise, the listener removal will not work.
Example:
listener addition
document.addEventListener('click', DisableClickOnPage.handler, true);
listener removal
document.removeEventListener('click', DisableClickOnPage.handler, true);
Doc: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
window.addEventListener("click", (e) => {
e.stopPropagation();
e.stopImmediatePropagation();
e.preventDefault();
}, true)
If we added a listener to document instead of window anyone can add a listener to window and it works. Because of document child of window and its events trigger always after window events.
We use 3 method of Event object.
stopPropagation for prevent all capturing and bubbling
stopImmediatePropagation for prevent same listeners (e.g. another window click listeners)
preventDefault for prevent all user agent event (e.g anchor href or form submit)
If onclick = null has been executed how to revoke the onclick event to normal functioning.. or
Link text
<script type="text/javascript">
function yourFunction(anchor)
{ if(anchor.disabled) return;
/* Your function here */
}
</script>
This article would probably be useful:
http://www.computerhowtoguy.com/how-to-use-the-jquery-unbind-method-on-all-child-elements/
One part in particular is a recursive function that removes all click events. Remember that jQuery will remove click events IF the click event was created using jQuery. the function given in the article will remove both those created with jQuery and those that were not. The function given is this:
function RecursiveUnbind($jElement) {
// remove this element's and all of its children's click events
$jElement.unbind();
$jElement.removeAttr('onclick');
$jElement.children().each(function () {
RecursiveUnbind($(this));
});
}
You would call the function like this:
RecursiveUnbind($('#container'));
That function takes a jQuery object parameter, but you could easily change it up to pass a string as the name of the ID for the element, or however you think is best.
To prevent the default behavior of an event, use event.stopPropagation() and event.preventDefault() in your event handler. And don't forget, return false; is another method for indicating that you want to cancel the default action...
The Event property returnValue indicates whether the default action for this event has been prevented or not. It is set to true by default, allowing the default action to occur. Setting this property to false prevents the default action. (Source: MDN Web Docs: Event.returnValue.)
Typically, we return a value from any function when it has any meaningful or useful purpose -- return false to cancel an event is meaningful because it indicates a failed event, and it's useful because the event-handler uses it.
For greatest cross-browser compatibility, remember to return false;...
document.addEventListener("click",handler,true);
function handler(e){
e.stopPropagation();
e.preventDefault();
return false;
}