How to get over a blocked event? - javascript

I'm developing an JavaScript app which needs to trigger mousemove event, and I am using tampermonkey to "embed" it on websites to test if it works on them. On one website, the mousemove event is simply blocked (I don't know why) and even console.log within this event doesn't show. It seems like this event has never triggered (the website ignores).
Is it possible to "override" events and block them? If so, how can I override this action and enable my specific mousemove event?
This script won't work:
document.addEventListener("mousemove", function(e) {
e = e || window.event;
e.stopPropagation();
console.log("[HELLO] Is it moving?!");
}, false);
(the result will be... nothing. this won't be logged)
Update They set window.onmousemove as null. Is there any way to revert this?

If you can run your code before theirs, you can save the function
window.onmousemove = function() {
console.log("moving",new Date())
};
window.myMove = window.onmousemove;
document.getElementById("x").addEventListener("click",function() {
window.onmousemove=null
})
document.getElementById("y").addEventListener("click",function() {
window.onmousemove=myMove;
})
<button type="button" id="x">Turn off mousemove</button>
<button type="button" id="y">Turn mousemove back on</button>

If some other event handler called stopPropagation() on the event, it won't be propagated up to the document level during the "bubble" phase.
However, you can make your event handler be called sooner, by running it in the "capture" phase. Simply change the third argument from false to true. This will solve most issues, except if some other event handler on the document was added before yours, and calls stopImmediatePropagation on the event. But this is rare.
See Bubbling and capturing explained for more background information.

Related

Why event propagates to parent even after calling e.nativeEvent.stopPropagation in react?

I have made a Sidepanel component in React and have attached a click listener to close the panel when user clicks anywhere outside the Sidepanel component, like so:
function Sidepanel({ isOpen, children }) {
const [isPanelOpen, setPanelOpen] = useState(isOpen);
const hidePanel = () => setPanelOpen(false);
...
useEffect(() => {
document.addEventListener('click', hidePanel);
return () => {
document.removeEventListener('click', hidePanel);
};
});
return (
<aside
className={`side-panel ${isPanelOpen ? 'side-panel--open' : ''}`}
onClick={stopEventPropagation}
>
<div className="side-panel__body">{children}</div>
</aside>
);
}
function stopEventPropagation (e) {
e.stopPropagation(); // <-- DOESN'T SEEM TO WORK
};
export default Sidepanel;
But, this code doesn't work as expected since the panel starts to close on a click even inside the <aside> element itself. It seemed like e.stopPropagation() did nothing so I updated the stopEventPropagation code to:
function stopEventPropagation (e) {
e.nativeEvent && e.nativeEvent.stopPropagation();
};
...which also didn't work. But, when I did this:
function stopEventPropagation (e) {
e.nativeEvent && e.nativeEvent.stopImmediatePropagation(); // <- IT WORKED!
};
...it worked. Weird!
I read some docs about stopImmediatePropagation I found on Google and realised that although it makes the code work, it just doesn't make any sense here. Am I missing something?
Replace document.addEventListener by window.addEventListener so that event.stopPropagation() can stop event propagate to window.
I hope this will help you.
When talking about event.stopPropagation() or event.stopImmediatePropagation(), we are talking about the term Event bubbling (or Bubbling for short). So make sure you have knowledge about it, otherwise, please take a look at this article
In the above article, you may find this useful one:
If an element has multiple event handlers on a single event, then even if one of them stops the bubbling, the other ones still execute.
1. event.stopPropagation() stops the move upwards, but on the current element all other handlers will run.
2. To stop the bubbling and prevent handlers on the current element from running, there’s a method event.stopImmediatePropagation(). After it no other handlers execute.
Back to your problem, it belongs to the later case. You have two onClick handlers attached to document, so using event.stopPropagation() is not the case here, but event.stopImmediatePropagation().
---Update 1:
React (currently) uses a listener on the document for (almost) all events, so by the time your component receives the event it has already bubbled all the way up to the document.
That means, the later onClick handler which is attached to the aside element will bubble up to document.
If you added the event listener normally (by window.addEventListener()) then e.stopPropagation() should work. But if you added it via React (by onClick) then it won't work, because React uses its own event processing which itself listens on document. So by the time the onClick handler on aside runs, the event may have been bubbled up and reached document, where your hidePanel() listener fired.
e.nativeEvent.stopImmediatePropagation() might cancel all listeners before that happens and solve your problem, but blindly bypassing event handlers (possibly added by some UI library) might cause side effects. So a better approach that is not affected by all the confusing event propagation, might be handling the logic within the document click listener.
const hidePanel = (e) => {
// Only hide panel if clicked outside it
if (!e.target.classList.contains('side-panel')) {
setPanelOpen(false);
}
}

Can mouseenter and click event exist together?

I am wondering if mouseenter and click event can exist together and they can both exist to TRUE when checked with:
if ((evt.type === 'mouseenter') && (evt.type === 'click'))
It is because when I mouse over the link, the mouseenter triggers (set to TRUE) and even when I clicked on it, the hover is still shown. Probably they could exist together but I'm no expert on this.
If someone can give insights, I would appreciate it a lot.
Also how can I trigger the click event during the mouseenter event?
The mouseenter event fires when the mouse enters the control. The click event fires when the mouse is clicked. They are two separate events which call two separate event handlers. If you click just as the mouse enters the element they will be called within a short timespan of one another but they are still two distinct events.
It is also important that you differentiate between the mouseenter and the mouseover events. mouseenter fires when the mouse physically enters an element, whereas mouseover fires continually while the mouse remains over an element.
While you cannot trigger the click event per se, you can call the same function that is called by the click event handler. For example if you have this:
var myfunc = function (e) { ... }
document.getElementById("id").onclick = myfunc;
Then you could simply call myfunc directly and you would get the same result as if the mouse was clicked.
They can 100% exist together, and this is a great question with no good answer... When you're on a mobile device, a mouseenter event will be thrown on tap... If you are also detecting onclick as well as mouseenter, then there will be a discrepancy between mobile devices and desktop machines.
It's kind of hard to solve such a small issue at the moment.
const x = document.getElementById('some_node')
x.onclick=(e)=>{
e.stopPropagation()
// this logic will be triggered on click for both desktop and mobile
}
x.onmouseenter=(e)=>{
e.stopPropagation()
// this logic will be triggered on click for mobile only (but will
//have already been triggered on desktop when cursor entered node)
}
The only workaround I came up for this, and I think it's pretty clever, is using a eventlistener for taps/touches. The order/priority that these events are fired goes: touch > mouseenter > click.
Since the touch event is fired first, you can add a touch event listener (which will only register on a mobile device), and change a variable that prevents the mouseenter event from being triggered (which is the logic that would generally be conflicting with the onclick logic)... like this:
let isMobile = false
x.addEventListener('touchstart',(e)=>{
isMobile = true
}, false);
Then your mouseenter would need to look like this:
x.onmouseenter=(e)=>{
e.stopPropagation()
if(!isMobile){
// this logic will no longer cause a conflict between desktop and mobile
}
}
they can exist on the same object, think a button with a hover state and then a click action. The click event, though will only read the click event since the enter event actually occurred earlier.
You can create a var like mouseIsOverand set it to true when the enter event fires. I can be safely assumed, though that if a click happens the mouse is over the same target.
The two events may happen at the same time, but they will still be processed on after the other. So the if you posted will never evaluate to true.
If you look at your code again you can see that it doesn't make sense. How can something be X and Y at the same time? It can't.
for the first question i think u got an answer....
however, for Also how can I trigger the click event during the mouseenter event?
u can use trigger() function..
http://jsfiddle.net/PDhBW/2/
if u want to read more about trigger
here is the link
http://api.jquery.com/trigger/
With Jquery event delegation, You can use binding multiple events at once
$('#IdElement').on('mouseenter click', function () {
//Your Code
});
http://jqfundamentals.com/chapter/events

What's the cross-browser way to capture all single clicks on a button?

What's the best way to execute a function exactly once every time a button is clicked, regardless of click speed and browser?
Simply binding a "click" handler works perfectly in all browsers except IE.
In IE, when the user clicks too fast, only "dblclick" fires, so the "click" handler is never executed. Other browsers trigger both events so it's not a problem for them.
The obvious solution/hack (to me at least) is to attach a dblclick handler in IE that triggers my click handler twice. Another idea is to track clicks myself with mousedown/mouseup, which seems pretty primitive and probably belongs in a framework rather than my application.
So, what's the best/usual/right way of handling this? (pure Javascript or jQuery preferred)
Depending on your situation you can use different approaches, but I would suggest using namespaced event handlers with jQuery like this:
function eventHandler(event) {
// your handler code here
doSomeMagic();
}
var element = $('#element');
element.one('click.someNameSpace', function(event){
// first we unbind all other event handlers with this namespace
element.unbind('.someNameSpace');
// then we execute our eventHandler
eventHandler();
}).one('dblclick.someNameSpace', function(event){
// If this fires first, we also unbind all event handlers
element.unbind('.someNameSpace');
// and then execute our eventHandler
eventHandler();
});
I'm not sure this will work the way you want it, but it's a start, I guess.
Mousedown and mouseup works just like the click functions, unfortunately so much that when IE omits a click because of a doubleclick it will also omit the mousedown and mouseup. In any case, you can add both click and dblclick to the same object and feed the clicks through a function that sort out any click happening too close to the last.
<div onclick="clk()" ondblclick="clk()"></div>
lastclicktime=0
function clk(){
var time=new Date().getTime()
if(time>lastclicktime+50){
lastclicktime=time
//Handle click
}
}
I by the way just found out that, at least in Firefox the dblclick event is not given an event time, therefore I had to resolve to the Date method.

jQuery live('click') firing for right-click

I've noticed a strange behaviour of the live() function in jQuery:
normal
live
$('#normal').click(clickHandler);
$('#live').live('click', clickHandler);
function clickHandler() {
alert("Clicked");
return false;
}
That's fine and dandy until you right-click on the "live" link and it fires the handler, and then doesn't show the context menu. The event handler doesn't fire at all (as expected) on the "normal" link.
I've been able to work around it by changing the handler to this:
function clickHandler(e) {
if (e.button != 0) return true;
// normal handler code here
return false;
}
But that's really annoying to have to add that to all the event handlers. Is there any better way to have the event handlers only fire like regular click handlers?
It's a known issue:
It seems like Firefox does not fire a
click event for the element on a
right-click, although it fires a
mousedown and mouseup. However, it
does fire a click event on document! Since .live catches
events at the document level, it sees
the click event for the element even
though the element itself does not. If
you use an event like mouseup, both
the p element and the document
will see the event.
Your workaround is the best you can do for now. It appears to only affect Firefox (I believe it's actually a bug in Firefox, not jQuery per se).
See also this question asked yesterday.
I've found a solution - "fix" the the live() code itself.
In the unminified source of jQuery 1.3.2 around line 2989 there is a function called liveHandler(). Modify the code to add one line:
2989: function liveHandler(event) {
2990: if (event.type == 'click' && event.button != 0) return true;
This will stop the click events from firing on anything but the left-mouse button. If you particularly wanted, you could quite easy modify the code to allow for "rightclick" events too, but this works for me so it's staying at that.
You can actually rewrite it as:
function reattachEvents(){
$(element).unbind('click').click(function(){
//do something
});
}
and call it when you add a new dom element, it should have the expected result (no firing on the right click event).
This is an unfortunate consequence of how live is implemented. It's actually uses event bubbling so you're not binding to the anchor element's click event, you're binding to the document's click event.
I solved this by using mousedown events. In my situation the distinction between mousedown and click didn't matter.

Multiple events firing from single action

I have an onclick event attached to a region in my page that causes a certain action to fire when the user clicks in it (naturally). I recently added an image to that region. When the user clicks on that image, I want another action to occur, and I do NOT want the action associated with the entire region to occur. However, I find that both events are, in fact fired when one clicks the image. How do I suppress the region-wide action when the image is clicked?
The issue you are running into is known as event bubbling. The click event of the image bubbles up to all parent elements of that node. You want to cancel bubbling.
The best way to do this that works across all browsers is by using a JavaScript framework. jQuery has a very simple way to do this. Other frameworks have similar mechanisms to cancel bubbling, I just happen to be most familiar with jQuery.
For example, you could do something like this in jQuery:
$('img').click(function () {
// Do some stuff
return false;// <- Cancels bubbling to parent elements.
});
Darit is correct, you need to stop the event from bubbling (propagating):
function imgEventHandler(e) {
// ^notice: pass 'e' (W3C event)
// W3C:
e.stopPropagation();
// IE:
if (window.event) {
window.event.cancelBubble = true;
}
}
In the event handler for the image do
event.cancelBubble = true;
and then at the end do
return false;

Categories

Resources