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

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}

Related

Click event fired twice on touch device

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

Firing a function with onkeydown event

I made a cookie clicker using javascript and html, and at the moment I have it made so that upon clicking an image, it fires a function which increases your score. I want to make it so that instead of only being able to click the image, you can just click any button on the keyboard to fire the same function. I've only seen code to do this in an input field. I'm not sure if "document.addEventListener("keydown", function())" is what I'm looking for.
Try:
document.onkeydown = function() {
console.log('my code');
};
Edit (or):
document.onkeydown = myFn;
To listen for keypress you can add keydown event on document
document.addEventListener("keydown", callBack, false);
function callBack(e) {
var keyCode = e.keyCode;
console.log(keyCode);
}
Just assign a event handler to your event.
If you want to assign for the whole document, use this:
document.addEventListener("keydown", keyDownTextField, false);
document.getElementById("yourinput").addEventListener("keydown", keyDownTextField, false);
function keyDownTextField(e) {
console.log(e.keyCode);
}
<input id="yourinput" type="text" />
document.addEventListener('keydown', () => {
console.log('a');
});
should work.
However, some browsers don't give document focus, which is required for keydown events.
The classic approach to this (which works more universally) is to create an off-screen <input> element, and give it a blur event to regain focus when it loses it. This will force focus to always stay on that input, then it can receive key events.
const input = document.createElement('input');
input.style.position = 'absolute';
input.style.left = '-100000px';
input.addEventListener('blur', () => input.focus());
input.addEventListener('keydown', () => console.log('key down'));
document.body.appendChild(input);
input.focus();
This code:
- creates a new input element
- positions it absolutely way off the left of the screen (so it's not visible)
- adds a blur event which automatically gets focus back
- adds the keydown event itself

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

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
}

Jquery Listbox Change event does not fire on Keyboard scrolling

I've got a simple Listbox on a HTML form and this very basic jQuery code
//Toggle visibility of selected item
$("#selCategory").change(function() {
$(".prashQs").addClass("hide");
var cat = $("#selCategory :selected").attr("id");
cat = cat.substr(1);
$("#d" + cat).removeClass("hide");
});
The change event fires fine when the current item is selected using the Mouse, but when I scroll through the items using the keyboard the event is not fired and my code never executes.
Is there a reason for this behavior? And what's the workaround?
The onchange event isn't generally fired until the element loses focus. You'll also want to use onkeypress. Maybe something like:
var changeHandler = function() {
$(".prashQs").addClass("hide");
var cat = $("#selCategory :selected").attr("id");
cat = cat.substr(1);
$("#d" + cat).removeClass("hide");
}
$("#selCategory").change(changeHandler).keypress(changeHandler);
You'll want both onchange and onkeypress to account for both mouse and keyboard interaction respectively.
Sometimes the change behavior can differ per browser, as a workaround you could do something like this:
//Toggle visibility of selected item
$("#selCategory").change(function() {
$(".prashQs").addClass("hide");
var cat = $("#selCategory :selected").attr("id");
cat = cat.substr(1);
$("#d" + cat).removeClass("hide");
}).keypress(function() { $(this).change(); });
You can chain whatever events you want and manually fire the change event.
IE:
var changeMethod = function() { $(this).change(); };
....keypress(changeMethod).click(changeMethod).xxx(changeMethod);
The behavior you describe, the change event triggering by keyboard scrolling in a select element, is actually an Internet Explorer bug. The DOM Level 2 Event specification defines the change event as this:
The change event occurs when a control
loses the input focus and its value
has been modified since gaining focus.
This event is valid for INPUT, SELECT,
and TEXTAREA. element.
If you really want this behavior, I think you should look at keyboard events.
$("#selCategory").keypress(function (e) {
var keyCode = e.keyCode || e.which;
if (keyCode == 38 || keyCode == 40) { // if up or down key is pressed
$(this).change(); // trigger the change event
}
});
Check a example here...
I had this problem with IE under JQuery 1.4.1 - change events on combo boxes were not firing if the keyboard was used to make the change.
Seems to have been fixed in JQuery 1.4.2.
$('#item').live('change keypress', function() { /* code */ });

Categories

Resources