Reputation: 61
I’m running into this issue where a single action by the user is supposed to trigger two events but it only triggers the first one.
The scenario:
A user enters some text into a special field that modifies the layout on focusout , after entering the text, without leaving the field, they click a button.
What’s happening?
I have a focusout event on a text box and click event on a button. What I see is the focusout event gets fired but the click event never does.
I’ve encapsulated this in a jsfiddle:
$('#theText').focusout(function (){
$("#focusevent").text("Focusevent");
console.log("focus");
});
$('#theButton').click(function (){
$("#clickevent").text("Clickevent");
console.log("click");
});
So if you click in the text field then click the button I’d expect both events to fire, but we only see the focus out event.
I put in a temporary fix for this by having the mousedown event fire the button instead of a click event (this fires before the focusout event) but that is causing some other behaviors and issues that I don’t want to see. Due to those I think optimal solution is finding a way to get the focusout and click events to both fire. Does anyone have thoughts on how to fix this problem?
Edit: After seeing initial responses I dug a little deeper, the issue here is that the focusout event is changing the page layout which very slightly pushes the location of the button down. The click event triggers after the focusout is done but since the button is no longer in the exact same location, nothing happens.
Here is an updated fiddle that shows my problem http://jsfiddle.net/fCz6X/11/
Upvotes: 1
Views: 386
Reputation: 173
I had a similar issue in which the focusout function seemed to be preventing the click event from firing.
A click event consists of both a mousedown and a mouseup event. If a click invokes a focusout for the previous element, the focusout event occurs after the mousedown event and before the mouseup event of the subsequent element.
In my case, my focusout function invokes an action using the fetch API, during which an animated image is displayed on an overlay. The overlay prevented the mouseup event from being registered for the original element so that only the mousedown event was registered; no click event was produced.
My solution was (1) to set a global variable preserving a reference to the original element clicked and (2) to add a mouseup listener to the image overlay (whose id is "wait") to invoke the original element's click method (in those instances in which the click was released while the overlay was still visible):
The function invoked by the focusout event, which calls the fetch API wrapper, must include its related element as a parameter:
// function called by a focusout event
function myResponse(e) {
myRequest = 'whatever';
return myFetch('save.php', myRequest, true, e.relatedTarget).then(response => {
...
}
The element clicked is set to a global variable:
let relatedTarget;
// simulate the original click
function finishClick(e) {
if (relatedTarget) {
relatedTarget.click();
relatedTarget = null;
}
}
// but not if both mousedown and mouseup registered on the overlay
function unsetRelatedTarget(e) {
relatedTarget = null;
}
// custom wrapper for fetch API
async function myFetch(page, request, wait, relatedTargetParam) {
if (wait) {
wait = document.getElementById('wait');
if (wait) {
wait.style.display = 'block';
if (relatedTargetParam) {
relatedTarget = relatedTargetParam;
wait.addEventListener('mousedown', unsetRelatedTarget);
wait.addEventListener('mouseup', finishClick);
}
}
}
return await fetch(page,
{
method: 'POST',
body: request
}
).then(response => {
...
An alternate solution is to use merely a mousedown event instead of a click event when there is a possibility of a focusout event interrupting the click. A downside to this solution is that a user can no longer abort the click event by moving the mouse away from the element before releasing the clicker.
Upvotes: 0
Reputation: 3771
As Joe said, the blocking alert call is what is breaking the event. Using a non-blocking call you will see both events.
If you really need to perform an alert like this, though, you can defer calling 'alert' until later using setTimeout()
$('#theText').focusout(function (){
setTimeout(function() { // alert after all events have resolved
alert("focus left text box");
}, 0);
});
Edit: In your updated fiddle the reason the click event never fires is because no click event occurs. If you move the button out from under the mouse on mousedown, there is no followup mouseup which is what initiates the 'click' event.
You may need to reconsider other aspects of your design. Your solution of using 'mousedown' is the best you can achieve because it's the only event that actually occurs.
Upvotes: 0
Reputation: 4294
I believe this is because the focusout
event fires first, executing the handler, and the alert
then prevents the browser from recognizing the click.
Try again with console.log
instead of alert
- it's less invasive.
Upvotes: 0
Reputation: 596
It is the Alert that is blocking. Some browser security prevents firing too many window.alert at the time.
When trying with other triggers, it looks. You may try console.log()
$('#theText').on("focusout",function (){
$("#theText").val($("#theText").val()+"flb");
});
$('#theButton').on("click",function (){
$("#theText").val($("#theText").val()+"but");
});
Upvotes: 0
Reputation: 40403
It's because you're calling alert
- the focusout
event fires, but before the browser recognizes you've clicked the button, the alert box blocks it.
Change your event handler to console.log
or something else that's non-obtrusive and you'll be ok.
Upvotes: 1