Is there a way to determine if a select element's change event was performed by user versus via code (dom manipulation)?
I have a timer that auto sets some select values based on some condition and then formats the select color to signify "success" (green select box).
Because I have the form set to fire off an ajax request when the select change event is fired, it does this for both the timer-based changes to the selects, as well as user-based select changes.
I'd like to only fire that ajax event when it's a user-based select change.
Is this possible?
EDIT:
When I output the value of e.isTrusted, it says Undefined in the log.
Here is my code:
$(document).on('change', 'select', function (e) {
e.preventDefault();
console.log(e.isTrusted); // This outputs "Undefined"
if (e.isTrusted) {
save_review();
console.log("Point status changed by user - Saved");
} else {
console.log("Point status changed programmatically - Not Saved");
}
});
Managed to solve this based on a solution I found googling and with the help of the community-provided answers above.
When the timer-based event triggers, I now pass it a boolean value (auto_selected):
$("#pt" + value + " option:nth-child(2)").attr('selected', true).trigger('change', [true]);
In my event listener, I am then able to check against the value of that parameter:
$(document).on('change', 'select', function (e, auto_selected) {
e.preventDefault();
console.log("Auto-selected: " + auto_selected);
if (typeof auto_selected == 'undefined' || !auto_selected) {
save_review();
console.log("Point status changed by user - Saved");
} else {
console.log("Point status changed programmatically - Not Saved");
}
});
There is a property called isTrusted on the event object that you can use to check whether the event is triggered by user action or by script.
that is true when the event was generated by a user action, and false when the event was created or modified by a script or dispatched via EventTarget.dispatchEvent()
if (e.isTrusted) {
/* The event is trusted */
} else {
/* The event is not trusted */
}
if you use jQuery, it's inside event.originalEvent
if (e.originalEvent && e.originalEvent.isTrusted) {
/* The event is trusted */
} else {
/* The event is not trusted */
}
If you use select2, listen to the event select2:select, and check for event.params.originalEvent.originalEvent (two originalEvent here, seems like a bad implementation inside the library itself)
$('#your-selector').on('select2:select', function(e) {
if(e?.params?.originalEvent?.originalEvent) {
// user action
} else {
// programatically trigger
}
});
I looked a bit through the documentation of select2 because I was not really happy with the initial solution in my answer. And they actually provide a solution for that using a namespaced events.
Limiting the scope of the change event:
It's common for other components to be listening to the change event, or for custom event handlers to be attached that may have side effects. To limit the scope to only notify Select2 of the change, use the .select2 event namespace
I'm wondering why they don't suggest that one as default in the documentation. I still think that their suggested way (using the not namespace verion) to sync the native select element with the HTML representation of select2 is odd. Because why should the default behavior of programmatically changing an input element trigger a change event.
So if you use $("select option:nth-child(2)").attr('selected', true).trigger('change.select2'); it won't trigger the change event.
$('select').select2();
function commonTaskOnChange() {
console.log('commonTask')
}
$(document).on('change', 'select', function (e) {
console.log('selection changed by user')
// in case you want to perform some common task for timer and change event
commonTaskOnChange();
})
setTimeout(() => {
console.log('timer will change selection')
// trigger the namespaced change event
$("select option:nth-child(2)").attr('selected', true).trigger('change.select2');
// you could still perform some action that the change event has in common by calling a function here
commonTaskOnChange();
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2#4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2#4.1.0-rc.0/dist/js/select2.min.js"></script>
<select>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
I have a checkbox in a sortable div where the click or changed event is triggered twice on touch devices but not on desktop (nor simulating touch on Firefox dev tools) for some obscure reason. The unwanted effect is the checkbox being toggled twice in a row leaving it at its original state. I mention the div is sortable because when they are made no longer sortable the checkbox works just fine. Here is the relevant part:
$sortable.sortable({
items: '.sortable',
cancel: 'input,textarea,button,select,option'
});
I have no idea why the sortable widget makes the click event be fired twice.
My attempts to handle this have been unsuccessful:
I've tried disabling the default behaviour of toggling a the checkbox and setting its value manually. However for some reason the checkbox won't be checked/unchecked. If I do it via console it does work:
// same with the "change" event
// the reason the event is bound to $sortable rather than the inputs themselves is that
// new sortable divs are added dynamically so otherwise they wouldn't have the event bound to them
$sortable.on('click', 'input[type="checkbox"]', function (e) {
e.stopImmediatePropagation();
// this does disable the default behaviour of checking/unchecking the box
e.preventDefault();
const $this = $(this);
// this doesn't check/uncheck the box at all, but setting a global variable to $this and doing it via console does work
$this.prop('checked', !$this.prop('checked'));
});
I have tried a more typical approach using a flag, however there seems to be some race condition (or whatever it's called because if I am not mistaken js is monothreaded) and the function is run twice anyway:
let checked = false;
$sortable.on('click', 'input[type="checkbox"]', function() {
if (checked) return;
// this is output twice, which is not what we are after
console.log('running');
checked = true;
// here ideally we would set the value of the checkbox
checked = false;
});
I have checked for any problematic events bound to the inputs using $._data($checkboxes[0], 'events') but other than a bootstrap tooltip (mouseover and mouseout events) and of course the change event there's nothing else fishy.
edit: Basically it seems that the sortable widget produces a separate click event when you click anywhere in the div or its children. That's the reason why click/change is fired twice.
My question is: how can I discriminate between the originator of the event so that if it's the sortable plugin the event can be ignored, while if it's the listener I set up the checkbox does change its value (and I perform any other actions I may want to perform when the checkbox is actually checked)?
try touchend and if it works for click as well use the following way , else separate it
$sortable.on("touchend click", 'input[type="checkbox"]', function(e) {
if(e.type == 'touchend'){
$(this).off('click');
// your function
}
});
else try this for both
let checked = false;
$sortable.on("touchend click", 'input[type="checkbox"]', function(e) {
if(e.type == "touchend") {
checked = true;
// your function
}
else if(e.type == "click" && !checked ) {
// your function
}
});
I am attempting to add an event handler to an anchor only when certain form fields are populated, like so:
$('#newName, #newFrom').keyup(function (e) {
if ($('#newName').val() || $('#newFrom').val()) {
$('#add-person').click(function (e) {
//Handle event, includes adding a row to a table.
$('this').off();
});
}
});
It seems like the first event is getting propagated to the second one since I end up with the same number of rows in my table as keys I have typed.
I've tried adding
e.stopPropagation();
But with no success.
$('this').off(); should be $(this).off();
also probably you'd better go using the input event instead of keyup. input event will trigger even if one pastes content into your fields.
nevertheless I'd go the other way around:
// (cache your selectors)
var $newName = $("#newName"),
$newFrom = $("#newFrom");
// create a boolean flag
var haveNewValue = false;
// modify that flag on fields `input`
$newName.add( $newFrom ).on("input", function() {
haveNewValue = ($.trim($newName.val()) + $.trim($newFrom.val())).length > 0;
});
// than inside the click test your flag
$('#add-person').click(function (e) {
if(!haveNewValue) return; // exit function if no entered value.
// do stuff like adding row to table
});
What was wrong:
on every keyup you was assigning a new (therefore multiple) click event/s to the button, but the (corrected to:) $(this).off() was triggered only after an actual button click.
Also a better way to use .on() and off.() (notice the difference in using the .click() method and the .on() method) is:
function doCoffee() {
alert("Bzzzzzzzz...BLURGGUZRGUZRGUZRG");
}
$("#doCoffeeButton").on("click", doCoffee); // Register "event" using .on()
$("#bossAlertButton").click(function() {
$("#doCoffeeButton").off("click"); // Turn off "event" using .off()
});
I have one callback function bound to two events (change and focusout). However, I need the focusout to happen only when the element we're interacting with is not a checkbox.
This is the example code:
$(document).on('focusout change', '.selector', function() {
if ($(this).is(':checkbox')) {
// Do stuff and prevent the focusout to trigger. HOW???
}
doStuff(); // Action that applies to both cases, but needs to be limited at one execution only
});
The code above will execute twice:
When the checkbox gets checked/unchecked
When you click outside of the checkbox (lose focus (blur))
I tried using .off, but it ends up killing the focousout handler altogether, which I will need later for other elements which aren't checkboxes.
What would be the way to prevent the focusout handler to trigger for certain elements?
What you want to do is
$(document).on('focusout change', '.selector', function(event) {
event is an event object, which has properties, one of which is type. Checking the type you can now see if your function has been called because of a focusout or a change and run code as appropriate
The best way is to affect both events (or more) to the same function, like this :
A text input for example
<input id="myfield" type="text" />
Now the Javascript
var myfield = document.getElementById('myfield');
myfield.onfocus = myfield.onchange = function(e)
{
//your code
}
Yo can even add an other element
button.onclick = myfield.onkeyup = function(e)
{
//when the client press the enter key
if(e.key && e.key == "Enter")
{
//catch the target
}
//when the client click the button
else if(!e.key || e.target == button)
{
//catch the target
}
//otherwise you can do not care about the target and just execute your function
}
You must only know that you can add many elements and many events
element1.onfocus = element1.onblur = element2.onchange = element3.onchange = function(e){//your code}
I have following jQuery code to prevent double clicking a button. It works fine. I am using Page_ClientValidate() to ensure that the double click is prevented only if the page is valid. [If there are validation errors the flag should not be set as there is no postback to server started]
Is there a better method to prevent the second click on the button before the page loads back?
Can we set the flag isOperationInProgress = yesIndicator only if the page is causing a postback to server? Is there a suitable event for it that will be called before the user can click on the button for the second time?
Note: I am looking for a solution that won't require any new API
Note: This question is not a duplicate. Here I am trying to avoid the use of Page_ClientValidate(). Also I am looking for an event where I can move the code so that I need not use Page_ClientValidate()
Note: No ajax involved in my scenario. The ASP.Net form will be submitted to server synchronously. The button click event in javascript is only for preventing double click. The form submission is synchronous using ASP.Net.
Present Code
$(document).ready(function () {
var noIndicator = 'No';
var yesIndicator = 'Yes';
var isOperationInProgress = 'No';
$('.applicationButton').click(function (e) {
// Prevent button from double click
var isPageValid = Page_ClientValidate();
if (isPageValid) {
if (isOperationInProgress == noIndicator) {
isOperationInProgress = yesIndicator;
} else {
e.preventDefault();
}
}
});
});
References:
Validator causes improper behavior for double click check
Whether to use Page_IsValid or Page_ClientValidate() (for Client Side Events)
Note by #Peter Ivan in the above references:
calling Page_ClientValidate() repeatedly may cause the page to be too obtrusive (multiple alerts etc.).
I found this solution that is simple and worked for me:
<form ...>
<input ...>
<button ... onclick="this.disabled=true;this.value='Submitting...'; this.form.submit();">
</form>
This solution was found in:
Original solution
JS provides an easy solution by using the event properties:
$('selector').click(function(event) {
if(!event.detail || event.detail == 1){//activate on first click only to avoid hiding again on multiple clicks
// code here. // It will execute only once on multiple clicks
}
});
disable the button on click, enable it after the operation completes
$(document).ready(function () {
$("#btn").on("click", function() {
$(this).attr("disabled", "disabled");
doWork(); //this method contains your logic
});
});
function doWork() {
alert("doing work");
//actually this function will do something and when processing is done the button is enabled by removing the 'disabled' attribute
//I use setTimeout so you can see the button can only be clicked once, and can't be clicked again while work is being done
setTimeout('$("#btn").removeAttr("disabled")', 1500);
}
working example
I modified the solution by #Kalyani and so far it's been working beautifully!
$('selector').click(function(event) {
if(!event.detail || event.detail == 1){ return true; }
else { return false; }
});
Disable pointer events in the first line of your callback, and then resume them on the last line.
element.on('click', function() {
element.css('pointer-events', 'none');
//do all of your stuff
element.css('pointer-events', 'auto');
};
After hours of searching i fixed it in this way:
old_timestamp = null;
$('#productivity_table').on('click', function(event) {
// code executed at first load
// not working if you press too many clicks, it waits 1 second
if(old_timestamp == null || old_timestamp + 1000 < event.timeStamp)
{
// write the code / slide / fade / whatever
old_timestamp = event.timeStamp;
}
});
you can use jQuery's [one][1] :
.one( events [, data ], handler ) Returns: jQuery
Description: Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
see examples:
using jQuery: https://codepen.io/loicjaouen/pen/RwweLVx
// add an even listener that will run only once
$("#click_here_button").one("click", once_callback);
using count,
clickcount++;
if (clickcount == 1) {}
After coming back again clickcount set to zero.
May be this will help and give the desired functionality :
$('#disable').on('click', function(){
$('#disable').attr("disabled", true);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="disable">Disable Me!</button>
<p>Hello</p>
We can use on and off click for preventing Multiple clicks. i tried it to my application and it's working as expected.
$(document).ready(function () {
$("#disable").on('click', function () {
$(this).off('click');
// enter code here
});
})
This should work for you:
$(document).ready(function () {
$('.applicationButton').click(function (e) {
var btn = $(this),
isPageValid = Page_ClientValidate(); // cache state of page validation
if (!isPageValid) {
// page isn't valid, block form submission
e.preventDefault();
}
// disable the button only if the page is valid.
// when the postback returns, the button will be re-enabled by default
btn.prop('disabled', isPageValid);
return isPageValid;
});
});
Please note that you should also take steps server-side to prevent double-posts as not every visitor to your site will be polite enough to visit it with a browser (let alone a JavaScript-enabled browser).
The absolute best way I've found is to immediately disable the button when clicked:
$('#myButton').click(function() {
$('#myButton').prop('disabled', true);
});
And re-enable it when needed, for example:
validation failed
error while processing the form data by the server, then after an error response using jQuery
Another way to avoid a quick double-click is to use the native JavaScript function ondblclick, but in this case it doesn't work if the submit form works through jQuery.
One way you do this is set a counter and if number exceeds the certain number return false.
easy as this.
var mybutton_counter=0;
$("#mybutton").on('click', function(e){
if (mybutton_counter>0){return false;} //you can set the number to any
//your call
mybutton_counter++; //incremental
});
make sure, if statement is on top of your call.
If you are doing a full round-trip post-back, you can just make the button disappear. If there are validation errors, the button will be visible again upon reload of the page.
First set add a style to your button:
<h:commandButton id="SaveBtn" value="Save"
styleClass="hideOnClick"
actionListener="#{someBean.saveAction()}"/>
Then make it hide when clicked.
$(document).ready(function() {
$(".hideOnClick").click(function(e) {
$(e.toElement).hide();
});
});
Just copy paste this code in your script and edit #button1 with your button id and it will resolve your issue.
<script type="text/javascript">
$(document).ready(function(){
$("#button1").submit(function() {
$(this).submit(function() {
return false;
});
return true;
});
});
</script
Plain JavaScript:
Set an attribute to the element being interacted
Remove the attribute after a timeout
If the element has the attribute, do nothing
const throttleInput = document.querySelector('button');
throttleInput.onclick = function() {
if (!throttleInput.hasAttribute('data-prevent-double-click')) {
throttleInput.setAttribute('data-prevent-double-click', true);
throttleInput.setAttribute('disabled', true);
document.body.append("Foo!");
}
setTimeout(function() {
throttleInput.removeAttribute('disabled');
throttleInput.removeAttribute('data-prevent-double-click');
}, 3000);
}
<button>Click to add "Foo"!</button>
We also set the button to .disabled=true. I added the HTML Command input with type hidden to identify if the transaction has been added by the Computer Server to the Database.
Example HTML and PHP Commands:
<button onclick="myAddFunction(<?php echo $value['patient_id'];?>)" id="addButtonId">ADD</button>
<input type="hidden" id="hasPatientInListParam" value="<?php echo $hasPatientInListParamValue;?>">
Example Javascript Command:
function myAddFunction(patientId) {
document.getElementById("addButtonId").disabled=true;
var hasPatientInList = document.getElementById("hasPatientInListParam").value;
if (hasPatientInList) {
alert("Only one (1) patient in each List.");
return;
}
window.location.href = "webAddress/addTransaction/"+patientId; //reloads page
}
After reloading the page, the computer auto-sets the button to .disabled=false. At present, these actions prevent the multiple clicks problem in our case.
I hope these help you too.
Thank you.
One way I found that works is using bootstrap css to display a modal window with a spinner on it. This way nothing in the background can be clicked. Just need to make sure that you hide the modal window again after your long process completes.
so I found a simple solution, hope this helps.
all I had to do was create a counter = 0, and make the function that runs when clicked only runnable if the counter is = 0, when someone clicks the function the first line in the function sets counter = 1 and this will prevent the user from running the function multiple times when the function is done the last line of the code inside the function sets counter to 0 again
you could use a structure like this, it will execute just once:
document.getElementById('buttonID').addEventListener('click', () => {
...Do things...
},{once:true});