Click event fired twice on touch device - javascript

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
}
});

Related

Assigning click event handler inside keyup event causes the click event to be fired multiple times

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()
});

Double event handler and one function. How to prevent executing code twice?

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}

how to disable and enable click event with no specific handler name

I have a lot of links in my app that I need to enable/disable the click event to them, but still want allow the hover and other events.
so I need a way (CSS / JS / jQuery) to disable only the Click event. and also with no need to use the handler name.
for example:
HTML:
Run All
JS - call to the disable function:
disableElement('#runAll', "no sensors to record"); // call to the disable function
JS - enable / disable functions
//disable element (add disable class and unbind from click event)
function disableElement(element, tooltip) {
$(element).addClass('ui-disabled');
$(element).disableClick; // need this option!
if (typeof tooltip != 'undefined' && tooltip != "")
loadTooltip(element, tooltip);
}
//enable element (remove disable class and bind to click eventl
function enableElement(element) {
$(element).removeClass('ui-disabled');
$(element).prop("title", "");
$(element).enableClick; // need this option!!
}
I tried this but didn't work for me. any idea please?
for now this is my function and
With JS:
$(element).attr('onclick', 'return false'); // disable
$(element).removeAttr('onclick'); // enable
Html
google
Use the 'off' call on your elements
$(element).off('click');
They can be rebound with 'on'
$(element).on('click', function(){
//your handler
});
You can stop the handler you have for a specific event by using $el.off('click').
Note though that re-attaching the event can be long winded, so it's better to use a flag within the handler itself to direct the logic. Something like this:
$el.click(function(e) {
var $el = $(this);
if ($el.data('click-disabled')) {
e.preventDefault();
}
else {
// do your thing...
}
}
Then you can disable/enable the handler logic by setting the data attribute:
$el.data('click-disabled', true);
Is it possible that you are taking the wrong approach?
why do you want to remove the handlers if there is the possibility that you will reattach?
It might be more appropriate to have the handlers always attached, then have a check for whatever condition you feel would mean removal
i.e.
$('#someThing').on('click', function () {
if(!someConditionThatYouFeelShouldRemoveTheHandler){
// do some code here
}
});
That way you dont need to attach and reattach multiple times
Answer 2
$(document).ready(function() {
$('.ui-disabled').click(function(e) {
e.preventDefault();
});
});
function disableElement(element, tooltip) {
$(element).addClass('ui-disabled');
// do nothing
if (typeof tooltip != 'undefined' && tooltip != "")
loadTooltip(element, tooltip);
}
//enable element (remove disable class and bind to click eventl
function enableElement(element) {
$(element).removeClass('ui-disabled');
$(element).prop("title", "");
// do nothing
}

prevent immediate Radio :checked property on click event

I have a click event handler attached to a group of radio inputs, and want to see if I am clicking on the radio button that is already selected (has property 'checked' set). I figured the event handler would act in a daisy chained fashion (first calling my event handler on the click and then continuing down the chain to the default behavior of the click). Apparently this is not the case, because when I evaluate if the property 'checked' is true on the radio button I just clicked, it always returns true in my click event handler.
The click has already been processed by the default behavior and has already applied the 'checked' property to the radio button I just clicked. Again, I figured my click event handler would be processed prior to that default behavior. What's even more odd is that even when I prevent the default behavior, it still returns true for the 'checked' property. I assume this is because the 'checked' property is being processed by the 'change' event, so preventing the default behavior on my click event handler is not affecting anything.
$("input[type='radio']").click(function(e) {
e.preventDefault();
alert($(this).prop("checked")); // always returns true; i want prop value immediately prior to click
});
How can I achieve what I'm after? That is, to see whether or not the radio button that was just clicked on was the one already checked. Thanks.
Based on the response of #Jeffman , I was able to get something to work for what I needed to do. I have custom buttons, only one of which can be selected (hence the use of radio buttons). However, if you click on the one that's already selected, it should deselect and select the default value instead.
Things I had to do. Handle mousedown events on the radio labels. If I am clicking on the already selected radio, set the default button to be 'checked'. Else I just select the button that has been clicked on. I had to disable the 'click' event on these buttons, as that would override my irregular handling of the radio buttons (for some reason the selection would snap back to the one that was clicked when I overrode it and chose the default one manually). This also meant I would need to manually trigger the change event, as I do the custom radio button styling there.
$(".radios > label").mousedown(function(e) {
var l = $(this); // label
var t = l.parent(); // container for radio group
var i = l.find("input"); // input element of this label
if(i.prop("checked")) { // clicking on the already selected button
t.find(".default_radio input").prop("checked", true).trigger("change");
} else {
i.prop("checked", true).trigger("change");
}
}).bind('click',false);
$("input[type='radio']").change(function(e) {
// style the buttons here
});
Save radio status before click via mousedown
var was_checked;
$( "#radio" ).mousedown(function() {
was_checked = $(this)[0].checked;
});
$( "#radio" ).click(function() {
$(this).attr('checked', !was_checked);
});
i think you should also return false. not only preventDefault
Here is a sample code that would do the trick
<script>
$(function() {
var radios = {};
$("input[type='radio']").each(function() {
radios[$(this).uniqueId().attr('id')] = false;
});
function resetRadioState() {
$("input[type='radio']").each(function() {
radios[$(this).attr('id')] = $(this).is(':checked');
});
}
resetRadioState();
$("input[type='radio']").click(function(e) {
alert(radios[$(this).attr('id')]);
resetRadioState();
});
})
</script>
<input type="radio" name="group[]"/>
<input type="radio" name="group[]" checked/>

JS function applies checkbox value not immediately for IE

Another one cross-browser issue.
JS logic:
if one specific check-box is checked,that dependent ones are checked automatically
and vice versa,if this check-box is unchecked ,that dependent unchecked also:
function changeStatusCheckBox(statusCheckbox) {
if (statusCheckbox.id == "id1") {
if (statusCheckbox.checked == true) {
document.getElementById("id2").checked = true;
document.getElementById("id3").checked = true;
}
else {
document.getElementById("id2").checked = false;
document.getElementById("id3").checked = false;
}
}
}
FF is OK - check/uncheck performed immediately.
IE7 check/uncheck works after clicked on some other browser area.
It looks like IE expects for additional blur behaviour.
JS called from this .jsf:
<h:selectBooleanCheckbox id="id1"
value="#{payment.searchByPaymentCriteria}" onchange="javascript:changeStatusCheckBox(this);"/>
What is your opinion?
Thank you for assistance.
Internet Explorer and some other browsers also works like this. The onchange event is called only when the blur occours and something changed. Text inputs and select combos are also like this.
The better way to do that with checkboxes, crossbrowser, is to bind it to the onclick event.
The onclick is called right after the mouseup event, so the checkbox status(checked or not) would be changed when the function is called.
Just do like
<h:selectBooleanCheckbox id="id1" value="#{payment.searchByPaymentCriteria}" onclick="javascript:changeStatusCheckBox(this);"/>
The way I like to this is to trap the click event only in IE, and blur/focus the checkbox. That will fire the change event in IE, and you can continue to use the change event for other browsers that support it. Click is not the same, and could introduce other issues. (Example utilizes $.browser from jQuery and assumes jQuery is included on the page.) Same example would work for radio buttons (substitute :radio for :checkbox).
function fixIEChangeEvent (){
if ($.browser.msie && $.browser.version < 9) {
$("input:checkbox").bind("click", function () {
this.blur();
this.focus();
});
}
}

Categories

Resources