HTML onchange event sometimes fires twice on input checkbox element - javascript

This happens to me in Chrome 79.0.
CodePen replicating the issue:
https://codepen.io/puckowski/pen/eYmEbVz
I have a basic input element which looks like:
<input type="checkbox">
In JavaScript, I define an onchange property like so:
let eventObj = {
onchange: function() { console.log('event fired'); }
}
I then set my input element's onchange property like so:
for (let [k, v] of Object.entries(eventObj)) {
vType = typeof v;
if (vType === 'function') {
inputElem[k] = v; // inputElem is the correct element in the document and not undefined
}
}
Effectively, inputElem is defined by:
inputElem = document.getElementById('foo');
The onchange event is bound only once per the method above.
When I click on the checkbox, sometimes the onchange event fires once, sometimes it fires twice.
This doesn't seem to be an event bubbling issue. If I change the bound function to the following:
function(evt) { console.log('event fired'); evt.stopPropagation(); }
The onchange event will still fire twice occasionally.
Any ideas as to what is going on? Is this perhaps a element focus issue?

Here is what worked fine for me:
function(evt) {
console.log('event fired');
evt.stopImmediatePropagation();
}

Related

this.id on an Element Event Listener becomes an array of all id's of elements clicked

I have an event listener on all textboxes. When a textbox is clicked, I'd like to open a keyboard. On Enter of the keyboard I'd then like to use the id of the textbox which called it to do some logic. However the id (txtbxId in code) just becomes the first textbox I click, then the second textbox I click in an array.
E.g, the alert becomes 'textbox1' - after second textbox click alert is 'textbox1' 'textbox2'
I've tried to force the variable id to '', to delete it etc. to no avail,
Code snippet here:
$('.textbox').click(function() {
var txtbxId = this.id;
$("#Keyboard").show();
$(document).on('keydown', function(e) {
if (e.which === 13) {
alert(txtbxId);
}
});
});
});
The issue is because you're nesting events. Therefore as well as duplicating the keydown event when a click event happens, you're supplying each individual id to those events.
To fix this, use a single event handler for all the .textbox elements, and read their own id from the reference to the element which raised the event which is available through the this keyword:
$('.textbox').click(function() {
$("#Keyboard").show();
});
$(document).on('keydown', '.textbox', function(e) {
if (e.which === 13) {
alert(this.id);
}
});
The problem is that your on('keydown') function the first time you click a textbox never gets unassigned, so for every time you click a .textbox, you're making a NEW keydown callback, but not removing your old ones.
I would recommend making an object outside of your onClick callback which manages .keydown callbacks, so that you only have one at any time.
Something like this:
window.keydownmanager = {
init: () => {
$(document).on('keydown', function (e) {
window.keydownmanager.callback(e);
});
},
callback: () => {},
setCallback: (cb) => {
window.keydownmanager.callback = cb;
}
}
And inside your onClick callback, do this:
var txtbxId = this.id;
$("#Keyboard").show();
window.keydownmanager.setCallback(function(e) {
if (e.which === 13) {
alert(txtbxId);
}
})

preventDefault in function with more parameters?

I have a function that is run when clicking an input in the DOM. I want to stop the element from being checked until my function can approve it. I'm trying to do this using e.preventDefault(); or event.preventDefault(); (is there any difference?!) but I'm not succeeding, what am I doing wrong?
This is my code without the preventDefault-part.
$(document).on("click","[data-item]", function() {
cart.updateCart(this);
});
cart.updateCart = function(target) {
// do stuff and perhaps check the input element
}
I tried this, which is not working:
$(document).on("click","[data-item]", function() {
cart.updateCart(this, event);
});
cart.updateCart = function(target, event) {
event.preventDefault();
console.log(event); // returns MouseEvent -- is this even the correct "event" ?
// do stuff and perhaps check the input element
}
I think I'm not "getting" how this works. Perhaps someone can explain how this works and what I'm doing wrong?
event should be the first parameter passed into your click handler. So your code really should be like this.:
$(document).on("click","[data-item]", function(event) {
cart.updateCart(this, event);
});
cart.updateCart = function(target, event) {
event.preventDefault();
console.log(event); // returns MouseEvent -- is this even the correct "event" ?
// do stuff and perhaps check the input element
}
You can read more about preventing default actions here.

How to execute a listener bound to an event and exclude the remaining listeners bound to different events from execution?

I have the following jQuery code:
$input.on('keyup', keyUpListener);
$input.on('input', inputListener);
// IE <= 8 fallback for input event.
$input[0].onpropertychange = function() {
if (window.event.propertyName === "value") {
inputListener(window.event);
}
};
$input is a jQuery input[type='text'].
Right now keyUpListener and inputListener are both executed when I type into the input or when I copy and paste something (onpropertychange is not fired because it is an IE only event).
But how can I tell JS to not fire inputListener if keyUpListener is executing and vice-versa?
Attach the specific event to the textbox?
$("#input").on('input', function () {
}
For instance the above - this way you can have multiple listeners not executing on the same input
EDIT
You are able to bind the paste function to the textbox also..
$("#input").bind('paste', function() {
var pasted = true;
});
Thus you can then have the below IF statement:
if (pasted) {
$input.on('input', inputListener);
} else {
$input.on('keyup', keyUpListener);
}

when removing a child element I get an error that it may have already been removed in a "blur" event

I have a dynamic element added to a LI element. When I remove it in a "blur" event it works fine. But when I attempt to remove the element from the parent in a "keydown" event it throws an error that the child may already have been removed in a blur event.
The item is removed as expected and the flow continues but I get an error every time on the "keydown" event.
// I get the error in this event.
input.addEventListener('keydown', function (event) {
if (event.keyCode === 13) {
value = this.value;
this.parentElement.removeChild(this);
callBack.call(null,[value]);
}
});
//no error removing the element here
input.addEventListener('blur', function (event) {
value = this.value;
this.parentElement.removeChild(this);
callBack.call(null,[value]);
});
When you trigger the blur event on the input, only the blur event fires.
When you trigger the keydown event that removes the input, the blur event also fires as a consequence of that removal, and tries to remove an input that is already gone.
To see this in action, check out this example:
http://jsfiddle.net/nate/kDKVy/
var input = document.getElementById('foo');
var valueDisplay = document.getElementById('value');
var value;
input.addEventListener('keydown', function (event) {
if (event.keyCode === 13) {
value = this.value;
valueDisplay.value = value;
this.parentElement.removeChild(this);
}
});
input.addEventListener('blur', function (event) {
console.log('BLUR');
value = this.value;
valueDisplay.value = value;
this.parentElement.removeChild(this);
});
Open up your console. First, type something in the red box and then click away to trigger the blur event. Notice that it works nicely and you see "BLUR" in the console.
Run the fiddle again. Now, type something in the red box and hit enter. Notice that you get the "BLUR" message in your console before the error.
Make sense?

Attaching properties to bubbled event object in Javascript

In Chrome/Firefox I can attach my custom properties to an event object in one handler and read them in a different handler for the same event even if the event handling is bubbled up.
I cannot do the same in IE. My custom property is lost while event is bubbled up.
Do you know if there's any solution or workaround to this?
The following is an example of that problem:
<div id="div1">
<input type="button" value="Foo" id="button1">
</div>
<script>
function attach(el, event, fn) {
if (el.addEventListener) {
el.addEventListener(event, fn);
} else if (el.attachEvent) {
el.attachEvent('on'+event, fn);
}
}
attach(document.getElementById("button1"), 'click', function (event) {
event.abc = "done";
return true;
});
attach(document.getElementById("div1"), 'click', function (event) {
alert(event.abc);
return true;
});
</script>
According with my test you cannot add property to event object in IE (IE8 tested).
Try next code:
attach(document.getElementById("button1"), 'click', function (ev) {
//ev=ev||event;
//ev.abc = "done";
// next lines show you why you cannot save properties in event object
var xx1=event;
var xx2=event;
alert(xx1===xx2); // // showed *false* in IE8, but expected *true*
return true;
});
I am not sure but maybe, when event object is requested, IE8 always return new object, that contains same properties/values as previous requested.

Categories

Resources