Why is trigger() triggering the select() handler 3 times? - javascript

$("#select-test").select(function(e){
//console.log(e)
console.log("selected")
})
$("#newTest").click(()=> {
$("#select-test").trigger("select")
})
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<button id="newTest">TEST ME</button>
<input type="text", id="select-test" value="some">
This is in Chrome, in Firefox, it's triggered twice.
I read this JQuery - Why does Trigger method call it three times? thread which talked about this issue, but honestly I couldn't understand it.
Somebody said:
we have 1 isTrigger and 2 simple select events.
What is that mean? 2 simple select events? Where? We only have 1 select event, where's the second one?
The best answer says that this happens because of bubbling, but... how? I mean, where's the bubbling? I don't see how this explain the event handler being triggered 3 times. Bubbling is when you target a child element, and the parent with the same handler is triggered too, but that's not what we have here. We only have ONE select handler, so .. where's the bubbling? And why is it triggered twice in Firefox?

A select needs a prevent default if you wish to stop it at some point.
the select event isn't the focus event, when a field becomes in focus. The select event gets fired when something is selected.
When you trigger the select event with the below code and have the browser tools open, you will see that the debugger shows the below code. When you select call stack -> dispatch, you can see what triggered the event and see that the third event is triggered by a native event.
var count = 0;
$("#select-test").select(function(e){
if(count == 2) {
debugger;
}
count++;
console.log("selected")
})
$("#newTest").click(()=> {
count = 0
$("#select-test").trigger("select")
})
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<button id="newTest">TEST ME</button>
<input type="text", id="select-test" value="some">
If we look at the documentation for select we see this:
In addition, the default select action on the field will be fired, so the entire text field will be selected.
So what happens is:
You trigger select -> jquery fires select to all event handlers
jQuery focusses the field, which triggers an automatic browser select all text, which triggers the select event.
Jquery selects all text in the field, native select is triggered, which is wrapped by jquery and then passed on to the handlers.
To stop any of these steps from happening you can use event.preventDefault() on the first time select is triggered, this will have the side event of not selecting the text.
Rather than using select I suggest you use focus() unless you really need to know what text is selected every time a select is triggered.

I have no idea why .select() (and also .on("select")) trigger three times, but .one("select") triggers once :
const $selectTest = $("#select-test");
$selectTest.one("select", function(e){
console.log(e.target)
console.log("selected")
});
$("#newTest").click(()=> {
console.log("Clicked");
$selectTest.trigger("select")
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="newTest">TEST ME</button>
<input type="text", id="select-test" value="some">

Related

Why does adding a setTimeout inside a blur event handler fix the "masking" of another click handler?

Looking for an explanation to the answers provided here and here.
Put simply, I have two elements. An input with an onBlur event, and a div with an onClick event. Without any special handling, when I blur the input by clicking the div, the onBlur event is fired, while the onClick event is not.
However, if I put a setTimeout inside the blur event handler, both event handlers are called when I click on the div. Why does this work?
HTML:
<input type="text" name="input" id="input1" />
<div id="div1">Focus the input above and then click me. (Will see 1 alert)</div>
<br/>
<input type="text" name="input" id="input2" />
<div id="div2">Focus the input above and then click me. (Will see 2 alerts)</div>
Javascript:
$(document).ready(function() {
function clickHandler() {
alert('Click!');
}
function blurHandler() {
alert('Blur!');
}
$('#input1').on('blur', function() {
blurHandler();
})
$('#input2').on('blur', function() {
window.setTimeout(blurHandler, 200);
})
$('#div1').on('click', function() {
clickHandler();
})
$('#div2').on('click', function() {
clickHandler();
})
});
Fiddle demo is here.
It happens because the blur event occurs before the click. The alert() method stops the execution of the script and once stopped, the click event will not fire after you dismiss the alert box. Using the setTimeout() method at the blur handler, you are actually allowing the click event to be fired.
i sugest you to listen to mousedown instead of click. The mousedown and blur events occur one after another when you press the mouse button, but click only occurs when you release it.
This is because of the modal nature of alert(). Try using console.log() instead and you will see that both will get called.
the setTimeout will fire the event after the time you defined.. so you'll have more time to click on the text.
in the other hand, the first input doesn't have a time to fire the blur so it's more difficult to fire the click event, but if you click fast enough, you will see two alerts even for the first input.

Checking how an element gained focus

Context
I have a backbone app with an event listener for focus events on a textarea. Backbone uses jQuery events, so core of my question centers around jQuery focus events.
Question
Is there a way to tell how an element came into focus, be it by click or tab?
The behavior of how the cursor gets positioned needs to be handled differently between these two cases, however there doesn't seem to be a way to distinguish between the two offhand.
I could listen to click events, however will still need to listen to focus to capture tabbing - this will overlap with click event as it will also focus the textarea resulting in double events.
I may to rethink this entirely.
JSBin Example
$('textarea')
.focus(function(event){
console.log('You focused me by' + event.type);
// Here I wish I know if the focus came from a 'click' or 'tab' events
});
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
</head>
<body>
<form>
<input placeholder="focus me, then tab" type="text"><br>
<textarea>Focus me via click. Now try via tabbing.</textarea>
</form>
</body>
</html>
.onfocus() listener can get called in a number of ways which makes it a tricky even to bind to.
Click on element
Tab or Shift-Tab to element
jQuery programatic focus $( "#target" ).focus();
Switching between programs, and refocusing the internet browser
There is no unique identifier in the onfocus event to determine how it came into focus.
From what I found it is best to be more explicit and listen to click() and onkeyup() events to handle unique behaviors between them and avoid unexpected function calls (like the browser is refocused).
onkeyup() is great for capturing tab events as the tab key will be released 'up' when tabbing in, but not when tabbing out.
JSBin
$('textarea')
.click(focusedBy)
.keyup(checkTab);
function checkTab(event){
if (event.keyCode === 9) {
focusedBy(event);
}
}
function focusedBy (event){
console.log('You focused me by ' + event.type);
}
you will need a combo of focus, click and blur events to determine the origin of "getting focus". click->set value, focus -> check if that clickvalue was set -> do what you must -> reset on blur. you might also want to be looking out for ontouchdown
You could set a clicked variable on mousedown.
You'll need to blur the textarea on mousedown so that focus will will be triggered on mouseup:
var clicked= false;
$('textarea')
.focus(function(event) {
if(clicked) {
$('#status').html('clicked');
clicked= false;
}
else {
$('#status').html('tabbed');
}
})
.mousedown(function(event) {
clicked= true;
$(this).blur();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<input placeholder="focus me, then tab" type="text"><br>
<textarea>Focus me via click. Now try via tabbing.</textarea>
</form>
<div id="status"></div>
here's a scaling way to do it without rerouting events or simulating extra actions:
var targ=$('textarea');
targ.focus(function(event){
console.log('You focused me by ' + targ.eventType);
// Here I wish I know if the focus came from a 'click' or 'tab' events
});
$("body").mousedown(function(e){
targ.eventType="mouse";
}).keydown(function(e){
targ.eventType="keyboard";
});
this uses the jQuery collection to store the last event type, which is set by document-wide handlers.
if you need to re-use this functionality on other input types, just add more selectors to targ and differentiate in the handler using event.target.
http://jsbin.com/ruqekequva/2/edit

jQuery change event is not working as expected

I have added change event on the input field so that whenever user enters the text into it, so other task should happen, it works but when i click outside the input field.I don't know whether it is default behavior or i am doing some thing wrong. I tried using keyup and keydown events and it works as expect.
Please suggest.
Here is my code:
$("#mobile-number").on('change',function(){
// some other code
});
The change event fires when an elements value changes.
For select boxes, checkboxes, and radio buttons, the event is fired immediately when the user makes a selection with the mouse, but for the other element types the event is deferred until the element loses focus.
In other words, on an input, the change event fires when the element loses focus, not when you type, and that is the default behaviour.
That's why there are key events as well, and on modern browsers you can catch most changes to an input with the input event
$("#mobile-number").on('input',function(){ ...
Yes, it is the desired behavior.
Change Event
The change event is fired for , , and
elements when a change to the element's value is committed by the
user. Unlike the input event, the change event is not necessarily
fired for each change to an element's value.
Depending on the kind of form element being changed and the way the
user interacts with the element, the change event fires at a different
moment:
When the element is activated (by clicking or using the keyboard) for and ;
When the user commits the change explicitly (e.g. by selecting a value from a 's dropdown with a mouse click, by selecting a
date from a date picker for , by selecting a file
in the file picker for , etc.);
When the element loses focus after its value was changed, but not commited (e.g. after editing the value of or ).
Try using input event:
$(function() {
$("#mobile-number").on('input', function() {
$("#copy").val(this.value);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type='text' id='mobile-number' />
<input type='text' id='copy' readonly/>
Try this:( If i really understand your problem )
jQuery(document).on('change', '#mobile-number', function() {
// some other code
});
for type event:
jQuery(document).on('keyup', '#mobile-number', function() {
// some other code
});
You should provide your selector to the .on function:
$(document).on('change', '#mobile-number', function() {
// some other code
});

Blur event stops click event from working?

It appears that the Blur event stops the click event handler from working? I have a combo box where the options only appear when the text field has focus. Choosing an option link should cause an event to occur.
I have a fiddle example here: http://jsfiddle.net/uXq5p/6/
To reproduce:
Select the text box
Links appear
Click a link
The blur even occurs and the links disappear
Nothing else happens.
Expected behavior:
On step 5, after blur occurs, the click even should also then fire. How do I make that happen?
UPDATE:
After playing with this for a while, it seems that someone has gone to great lengths to prevent an already-occurred click event from being handled if a blur event makes the clicked element Un-clickable.
For example:
$('#ShippingGroupListWrapper').css('left','-20px');
works just fine, but
$('#ShippingGroupListWrapper').css('left','-2000px');
prevents the click event.
This appears to be a bug in Firefox, since making an element un-clickable should prevent future clicks, but not cancel ones that have already occurred when it could be clicked.
Other things that prevent the click event from processing:
$('#ShippingGroupListWrapper').css('z-index','-20');
$('#ShippingGroupListWrapper').css('display','none');
$('#ShippingGroupListWrapper').css('visibility','hidden');
$('#ShippingGroupListWrapper').css('opacity','.5');
I've found a few other questions on this site that are having similar problems. There seem to be two solutions floating around:
Use a delay. This is bad because it creates a race condition between the hiding and the click event handler. Its also sloppy.
Use the mousedown event. But this isn't a great solution either since click is the correct event for a link. The behavior of mousedown is counter-intuitive from a UX perspective, particularly since you can't cancel the click by moving the mouse off the element before releasing the button.
I can think of a few more.
3.Use mouseover and mouseout on the link to enable/disable the blur event for the field. This doesn't work with keyboard tabing since the mouse is not involved.
4.The best solution would be something like:
$('#ShippingGroup').blur(function()
{
if($(document.activeElement) == $('.ShippingGroupLinkList'))
return; // The element that now has focus is a link, do nothing
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
Unfortunately, $(document.activeElement) seems to always return the body element, not the one that was clicked. But maybe if there was a reliable way to know either 1. which element now has focus or two, which element caused the blur (not which element is blurring) from within the blur handler. Also, is there any other event (besides mousedown) that fires before blur?
click event triggers after the blur so the link gets hidden. Instead of click use mousedown it will work.
$('.ShippingGroupLinkList').live("mousedown", function(e) {
alert('You wont see me if your cursor was in the text box');
});
Other alternative is to have some delay before you hide the links on blur event. Its upto you which approach to go for.
Demo
You could try the mousedown event instead of click.
$('.ShippingGroupLinkList').live("mousedown", function(e) {
alert('You wont see me if your cursor was in the text box');
});
This is clearly not the best solution as a mousedown event is not achieved the same way for the user than a click event. Unfortunately, the blur event will cancel out mouseup events as well.
Performing an action that should happen on a click on a mousedown is bad UX. Instead, what's a click effectively made up of? A mousedown and a mouseup.
Therefore, stop the propagation of the mousedown event in the mousedown handler, and perform the action in the mouseup handler.
An example in ReactJS:
<a onMouseDown={e => e.preventDefault()}
onMouseUp={() => alert("CLICK")}>
Click me!
</a>
4.The best solution would be something like:
$('#ShippingGroup').blur(function()
{
if($(document.activeElement) == $('.ShippingGroupLinkList'))
return; // The element that now has focus is a link, do nothing
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
Unfortunately, $(document.activeElement) seems to always return the
body element, not the one that was clicked. But maybe if there was a
reliable way to know either 1. which element now has focus or two,
which element caused the blur (not which element is blurring) from
within the blur handler.
What you may be looking for is e.relatedTarget. So when clicking the link, e.relatedTarget should get populated with the link element, so in your blur handler, you can choose not to hide the container if the element clicked is within the container (or compare it directly with the link):
$('#ShippingGroup').blur(function(e)
{
if(!e.relatedTarget || !e.currentTarget.contains(e.relatedTarget)) {
// Alt: (!e.relatedTarget || $(e.relatedTarget) == $('.ShippingGroupLinkList'))
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
}
(relatedTarget may not be supported in older browsers for blur events, but it appears to work in latest Chrome, Firefox, and Safari)
If this.menuTarget.classList.add("hidden") is the blur behavior that hides the clickable menu, then I succeeded by waiting 100ms before invoking it.
setTimeout(() => {
this.menuTarget.classList.add()
}, 100)
This allowed the click event to be processed upon the menuTarget DOM before it was hidden.
I know this is a later reply, but I had this same issue, and a lot of these solutions didn't really work in my scenario. mousedown is not functional with forms, it can cause the enter key functionality to change on the submit button. Instead, you can set a variable _mouseclick true in the mousedown, check it in the blur, and preventDefault() if it's true. Then, in the mouseup set the variable false. I did not see issues with this, unless someone can think of any.
I have faced a similar issue while using jQuery blur, click handlers where I had an input name field and a Save button. Used blur event to populate name into a title placeholder. But when we click save immediately after typing the name, only the blur event gets fired and the save btn click event is disregarded.
The hack I used was to tap into the event object we get from blur event and check for event.relatedTarget.
PFB the code that worked for me:
$("#inputName").blur(function (event) {
title = event.target.value;
//since blur stops an immediate click event from firing - Firing click event here
if (event.relatedTarget ? event.relatedTarget.id == "btnSave" : false) {
saveBtn();
}
});
$("#btnSave").click(SaveBtn)
As already discussed in this thread - this is due to blur event blocking click event when fired simultaneously. So I have a click event registered for Save Btn calling a function which is also called when blur event's related Target is the Save button to compensate for the click event not firing.
Note: Didnt notice this issue while using native onclick and onblur handlers - tested in html.

DOM problem with click initiating a focusout event on a different input

I have an <input type=text> with focusout event handler
I have a <button> with click event handler
Focusout checks whether format in input box is correct. It does so by testing input value against a regular expression. If it fails it displays a message (a div fades-in and -out after some time) and refocuses my input by calling
window.setTimout(function() { $(this).focus(); }, 10);
since I can't refocus in focusout event handler. focusout event can't be cancelled either. Just FYI.
Click collects data from input elements and sends it using Ajax.
The problem
When user TABs their way through the form everything is fine. When a certain input box failes formatting check it gets refocused immediately after user presses TAB.
But when user doesn't use TAB but instead clicks on each individual input field everything works fine until they click the button. focusout fires and sets time-out for refocusing. Since time-out is so short focusing happens afterwards and then click event fires and issues an Ajax request.
Question
I have implemented my formatting check as an independent jQuery plugin that I want to keep that way. It uses .live() to attach focusout on all input fields with a particular attribute where format regular expression is defined.
Data submission is also generic and I don't want to make it dependant on formatting plugin. They should both stay independent.
How can I prevent click event from executing without making these two plugins dependant?
Example code I'm fiddling with
After some searching I've seen that all major browser support document.activeElement but I can't make it work in Chrome. FF and IE both report this being the active element, but Chrome always says it's BODY that is active even though click fired on the button element.
Check this code http://jsfiddle.net/Anp4b/1/ and click on the button. Test with Chrome and some other browser and see the difference.
You could use a flag...
Live demo: http://jsfiddle.net/Anp4b/4/
So your question is:
How can I prevent click event from executing without making these two plugins dependent?
Well, you obviously cannot prevent the click event. If the user wants to click the button, he will, and the click event will trigger. There's nothing you can do about that.
So the answer to the above question is: You cannot.
Based on the current conditions, you have to - inside the click handler - retrieve the validation result, and based on that result, decide if form submission should or should not occur.
JS Code:
$("#Name").focusout(function(){
var that = this;
valid = this.value.length ? true : false;
!valid && window.setTimeout(function() {
$(that).focus();
}, 0);
});
$("#Confirm").click(function(e) {
if ( !valid ) { return false; }
e.preventDefault();
alert('AJAX-TIME :)');
});
HTML Code:
<input type="text" id="Name">
<button id="Confirm">OK</button>
Is there are reason you use .focusout instead of .blur?
Using a flag is a good idea, but I would rather use a class on the element. By using classes to determine the state you can also style it accordingly. Here's my example based on your fiddle.
Another solution that hopefully gives the result you are looking for.
1) Create a named click handler:
var clickHandler = function(e){ /** submit form or whatever you want to do**/ };
$("button").click(clickHandler);
2) Add the following to the focusout event when it's failing validation:
$("button").unbind("click", clickHandler).one("click", function(){ button.click(clickHandler); return false;});
You can find an example of this here.

Categories