I have an <input> element that can either have the focus set via code, or as the result of a mouse click.
If the user clicks on the input, then the click event handler will fire - all well and good. If the element receives the focus via some other way (e.g. via code) then I want to manually trigger the click event so that the handler will also fire.
I could do this:
$elem = $('input');
$elem
.on('focus', function() { $(this).trigger('click') })
.on('click', function() { alert('Clicked!') });
However, this will result in click handler being fired twice; once for the click event and once for the focus event.
Is there any way to selectively trigger the click handler only if the focus was not received as the result of a click event?
UPDATE
This is a very simplified version of my problem, so I can't do things like bind both handlers to the focus event etc. I'm trying to merge two third-party pieces of code.
The .trigger() function adds a property isTrigger in the event object to identify that the event was triggered by its usage. Although, it is not documented the property is still present in jQuery 1.8.3 but it seems to only be used internally.
Anyways, you can make use of the extraParameters parameter to add a custom property to the event object. For instance,
$(this).trigger('click', {
isTrigger: true
});
It will keep the compatibility with isTrigger even if it is gone in a future release.
After doing some more research it appears that there is no way of guaranteeing which event will fire first: click or focus. (There doesn't seem to be a standard that dictates the order of events.)
This means that when the focus event fires there's no way to determine if a click event will or will not be triggered by the browser shortly afterwards.
I managed to solve the issue by using setTimeout() to run a test about 100ms after the focus event fired to check if the click event had fired. The third-party code that I was using (bound to the click event) added an extra class to the <input>, so I was able to check for that.
You can tap into the mousedown event which fires before the focus event. When you click a focusable object the order of events is as follows... mousedown, focus, mouseup, click.
You could set a flag in the mousedown event and then check for it in the focus event to see if the focus came from a mouse click. Obviously make sure to clear the flag in the focus event handler. Every application is different, but tapping into the mousedown event allows you to figure out a solution.
Here is a JSFiddle demonstrating the order of events... http://jsfiddle.net/ek7v7/
$elem = $('input');
$elem
.on('focus', function() { alert("Focused!") })
Focus can be fired by focusing the input by using tab, clicking it, or by using .focus()
Is there a reason for on('click', ...)?
Related
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
With the following html structure:
<div>
<form><span><input></span></form>
</div>
<p>
and the following jquery code:
$('form').on("focus", function(event) {
$("p").append("focus no delegation<br>");
})
Why doesn't the focus event ever reach my event handler? Binding the event with a selector parameter works fine:
$('form').on("focus", "input", function(event) {
$("p").append("focus delegation<br>");
})
Event the next snippet works so the focus event does in fact bubble...
$('form').on("focus", "span", function(event) {
$("p").append("focus delegation<br>");
})
Both forms work with click and change events:
$('form').on("click", function(event) {
$("p").append("click no delegation<br>");
})
$('form').on("click", "input", function(event) {
$("p").append("click delegation<br>");
})
The only note I found about the focus event's bubbling is relative to older jQuery versions which I don't use. See it in action here
edit 1
Well this is confusing... According to jQuery's focus doc:
The focus event does not bubble in Internet Explorer. Therefore, scripts that rely on event delegation with the focus event will not work consistently across browsers. As of version 1.4.2, however, jQuery works around this limitation by mapping focus to the focusin event in its event delegation methods, .live() and .delegate().
And according to jQuery's focusin doc:
The focusin event is sent to an element when it, or any element inside of it, gains focus. This is distinct from the focus event in that it supports detecting the focus event on parent elements (in other words, it supports event bubbling).
Is it too late for me or does one contradict the other?
As Ohgodwhy pointed out, using focusin instead of focus does work.
However, I can't understand how the following code can work if the "focus" event does not bubble:
$('form').on("focus", "span", function(event) {
$("p").append("focus delegation<br>");
})
If a span child of the form receives the "focus" event, it means that the event bubbled from the input to the span. Even this works!
$('div').on("focus", "form", function(event) {
$("p").append("focus delegation<br>");
})
So... using "focusin" does fix the problem, but I'm not fully satisfied by this workaround. If anybody has a better answer, I'll happily accept it.
Yes, it appears the jQuery docs are misleading. I believe the documentation on focus neglected to mention that this was for the elements that aren't involved in user input (#Ohgodwhy listed them above in your question's comments).
I imagine it has something to do with the browser's need to trap the cursor in the input element that has focus, so that it can accept input from the keyboard. In other words, if jQuery allowed it to bubble, then you would never be given the chance to type in the input field.
Either way you'll never get a form to accept a focus event unless you first put a tabindex on it: http://jsfiddle.net/qxLqP/ but if an input based field gets focus first, it will never bubble. By default, the form element doesn't have a tabindex, and you can't set focus to something that doesn't have a tabindex.
I'd just accept #Ohgodwhy's solution of using focusin and then go let the jQuery team know about their confusing documentation.
Jquery has a cross browser function which takes care of focus delegations.
Chrome,safari and opera support an event called "DOMFocusIn" which actually delegates.
IE supports an event "focusin" for delegating focus.
Firefox supports "focus" only on capturing phase and not bubbling phase.
jQuery made a cross browser event which is "focus" which actually delegates.
I hope this answers all your doubts.
I don't understand why the jQuery blur handler isn't working in the most simple case. I'm literally creating a div 100px by 100px and setting a blur event on it, but it's not firing (JSFiddle):
<div id="test">this is a test</div>
$(document).ready(function() {
$('#test').bind('blur', function() {
alert('blur event!');
});
});
Is my understanding of blur wrong? I expect the blur event to fire when I click anywhere that is not the div...right?
According to jQuery's documentation:
In recent browsers, the domain of the event has been extended to
include all element types. An element can lose focus via keyboard
commands, such as the Tab key, or by mouse clicks elsewhere on the
page.
I've tried it on the latest Chrome and Firefox on Mac.
From the W3C DOM Events specification:
focus
The focus event occurs when an element receives focus either via a pointing device or by
tabbing navigation. This event is valid for the following elements: LABEL, INPUT, SELECT,
TEXTAREA, and BUTTON.
blur
The blur event occurs when an element loses focus either via the pointing device or by
tabbing navigation. This event is valid for the following elements: LABEL, INPUT, SELECT,
TEXTAREA, and BUTTON.
The jQuery docs state browsers extended the events to other elements, which I'm guessing means blur and focus are aliases for the more generic DOMFocusIn and DOMFocusOut events. Non-input elements aren't eligible to receive those by default though, and an element has to somehow gain focus before losing it - a blur still won't fire for every click outside the div.
This SO question mentions that giving an element a tabindex would allow that, and seems to work for me in Chrome after modifying your jsFiddle. (Albeit with a fairly ugly outline.)
As far as I knew, blur happens on inputs that had the focus, either way you say
I expect the blur event to fire when I click anywhere that is not the div...right?
Not exactly, the blur event only happens for an element that had the focus first
So in order for a blur event to occur, you would first have to give focus to the div, how is the div getting focus first?
If you are really try to determine if there was a click outside of your div, you need to attach a click handler to the document, and then check to see where your click came from.
var div_id = "#my_div";
var outsideDivClick = function (event) {
var target = event.target || event.srcElement;
var box = jQuery(div_id);
do {
if (box[0] == target) {
// Click occured inside the box, do nothing.
return;
}
target = target.parentNode;
} while (target);
}
jQuery(document).click(outsideDivClick);
Just remember that this handler will be run for EVERY click on the page. (in the past if i ha to use something like this, i attach the handler when I need it, and remove it when I no longer need to look for it)
A can't "blur" because that would involve the div having focus in the first place. Non-input elements like a and textarea can have focus, which is what jQuery's documentation refers to.
What you need is the "mouseout" or "mouseleave" event (mouseleave doesn't bubble, mouseout does), which will be fired when the cursor leaves the div. If you need to have clicks, I would attach a "click" event to the body, as well as the div and stopping the event propagation on only the div:
$("div").click(function(e) {
return false; // stop propagation
});
Or, if you're really determined, you can fake the appearance of a div with a and some CSS rules :)
If you want something to happen while you move your mouse over the box, you could use the mouseover event.
for arcane reasons I need to be able to cancel the click event via the mousedown event.
Briefly; I am creating a context menu in the mousedown event, however, when the user clicks on the page the context menu should disappear.
I am not able to use the mousedown event over the click in that scenario as I want the user to be able to click links inside the menu ( a full click would never travel to the <a> based menu elements ).
If it is any help, jQuery can be applied.
I would like to either be able to prevent the click event from happening from within the initial mousedown, or be able to pass information to the click event (via originalEvent or otherwise).
TIA
Seems to be impossible, neither FF nor Opera didnt cancel upcoming click when prevented in mousedown and/or mouseup (as side note: click is dispatched after mouseup if certain conditions met). testcase: http://jsfiddle.net/ksaeU/
I have just had the exact same problem. I fixed my context menu by closing it on mousedown and eating the mousedown event on the menu so that I can still receive clicks on the menu, like so:
$(document).one('mousedown.ct', null, function() { cmenu.hide(); return false; });
cmenu.bind('mousedown', function(e) { e.stopImmediatePropagation(); });
And in the hide() function I unbind the mousedown.ct again, in case it was closed due to a click on an item.
Hey, I think this is what you are trying to do with your code. If not, I apologize, I may have misunderstood the question. I used jQuery to get it done: http://jsfiddle.net/jackrugile/KArRD/
$('a').bind({
mousedown: function(){
// Do stuff
},
click: function(e){
e.preventDefault();
}
});
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.