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.
Related
I've been having trouble getting stopPropagation to work for my dataview. Basically the issue is as follows;
Select node in the dataview, this fires the select, selectionchange events
Selected node has an image with an onClick event, this creates a report in a pop up window.
When image is clicked I call stopPropagation to prevent the event being bubbled up to the dataview which would cause the deselect, selectionchange events to be fired.
stopPropagation only seems to work in Firefox for me. Chrome and IE it seems to have no effect as the node is deselected and the unwanted events fired.
I've tried the following function on the onClick event
handleBubbleEvent: function(e) {
if (!e) {
var e = window.event;
}
e.cancelBubble = true;
e.returnValue = false;
if (e.stopPropagation) {
e.stopPropagation();
}
}
Have also seen stopImmediatePropagation, preventDefault, stopEvent but these also has had no effect
I am doing all this inside an Ext XTemplate
I'm wondering if this is an ExtJS 5 issue and the same code is working for an older version of ExtJS. I just can't seem to stop the click event bubbling back up to the dataview which then fires the deselect and selectionchange events.
Could it be that it is a timing/order issue. I use ExtJS's selectionchange event on the dataview whereas in the XTemplate I am using my own listener function? I see stuff online referencing capturing/bubbling of events and as I'm not a web developer I'm struggling on this.
UPDATE:
I'm now looking at events and capturing/bubbling, it seems the capturing is going up to the parent and calling the deselect, selectionchange then going down into the actual click handler at which point I then call stopPropagation but it is too late at this stage. Looking at creating my own listener for selectionchange with either target/delegate set so that it is only called when class != 'some class' and then a listener on 'some class' click to handle what I want and stopPropagation, if that makes any sense!
Try using a setTimeout on any dom manipulation triggered by the event.target element's event handler so that the event bubbling completes before any of the dom manipulation occurs.
I had the same issue with Chrome, and (like with you) Firefox worked. I discovered that Chrome seems to get confused if you modify the dom before the event bubbling completes.
In my case, I was adding two elements to the document.body (a canvas and a fieldset) and by the time chrome bubbled the event, the event.target was incorrect (it thought "BODY" was the event.target.tagName -- WRONG!). I wrapped the DOM manipulation portion in a setTimeout like this:
setTimeout(()=>
{
document.body.appendChild(canvas);
document.body.appendChild(fieldSet);
},0);
After this, chrome started reporting the correct element.target on the bubbled body click event. Maybe this same technique will help you get e.stopPropagation() to do its thing before the dom manipulation occurs.
I have a click event on the body of my document:
$("body").on('click.findElem', function(e) {
e.stopPropagation();
e.preventDefault();
self.hinter(e.target);
return false;
});
It basically catches the clicked target and does something to it. However, there are some targets that already have a click event on them, and they prevent my click from being executed at all. How do I overcome that issue? I tried unbinding it, but the click doesn't even work at all to actually execute the unbinding.
e.stopImmediatePropagation() does the job, but only if your handler executes before whatever other handler exists.
Unfortunately there is no way to insert your own handler in the first position - but you can use this nasty hack if the other handlers were bound using jQuery, too: How do you force your javascript event to run first, regardless of the order in which the events were added?
If you really need this you might want to bind an event handler in capture mode using the native DOM API: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener
Capture handlers are triggered before bubble handlers (which are used by jQuery and most other scripts) so that way you have very high chance to execute your handler first.
try this and see demo
$( "body" ).on( "click", ".clickme:not(.clicked)", function( event ) {
$(this).addClass('clicked');
alert('here');
});
i tend to not use on and stick with the bind/unbind combo.
i have some pages that reload partial content and then has to rebind the events.
i tipically do something like this
$(".theobjectsiwant").unbind("click").bind("click", function() {
alert('hi there');
});
If you want/have to stick with the on() function, you shouldn't mix on() with unbind() and try a similar approach with .off("click").on("click")
Check here for a sample http://api.jquery.com/off/
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
I have a div which onmouseover, displays a panel and onmouseout makes it disappear. JavaScript code is as follows:
function ShowPanel() {
document.getElementById("thePanel").style.display = "inline";
}
function HidePanel() {
document.getElementById("thePanel").style.display = "none";
}
The code works in Firefox and IE perfectly. The problem is Chrome. It works until the mouse is on the textbox in the panel. When the mouse goes on the textbox the onmouseout event is called even though the textbox is part of the panel and should remain open.
What you need is the behavior of the onmouseenter event instead of onmouseover and onmouseleave instead of onmouseout. The problem is that those events work only in IE (they actually got those ones right). You either need to simulate that behavior taking into account all of the differences in the event handling in different browsers, or just use a good JavaScript library that would take care of that for you. For example jQuery has .mouseenter() and .mouseleave() that are simulated on browser that don't support those events natively, and even a nice shortcut .hover() to set both at the same time.
I wouldn't recommend doing it manually unless you really know all of the quirks and inconsistencies of event models in different browsers (and you don't since you asked this question) but if you want to see how jQuery is doing it then see events.js and search for mouseenter and mouseleave.
In Chrome as you've found the mouseout event is fired whenever you move from the parent element (on which the handler is registered) and child elements contained within it.
The simple fix is to use jQuery, which will simulate mouseleave events (which don't suffer that problem) on browsers that don't support it.
Alternatively, in your mouseout handler, look at the toElement property of the event, and traverse its parent list and see if your original parent is in that list. Only process your action if it was not in the list.
document.getElementById('outer').addEventListener('mouseout', function(ev) {
var el = ev.toElement;
while (el && el !== document.body) {
if (el === this) {
console.log('mouseout ignored');
return; // enclosed - don't do anything
}
el = el.parentNode;
}
console.log('mouseout');
}, false);
demo at http://jsfiddle.net/raybellis/s4EQT/
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.