Reputation: 2103
A page in my app has some simple user preferences - for instance, "logging on/off."
The page displays checkboxes...then updates them to show the user's actual preference (checked/unchecked).
I then want to add a listener to make sure I capture any change to this preference.
In the code below (in the 'Ready' section so it runs when the page loads) I first update the HTML to display the correct on/off, THEN create a listener for the same element to capture any subsequent changes.
I'm clearly not understanding events/bubbling - when this page loads the first piece of code updates the checkbox and then the listener fires...but the listener did not exist yet because of the order of the code.
I've tried a number of things to stop the initial setup of the checkbox from firing an event - stopPropagation in the code below. Nothing works - I fire an unnecessary update to my server because of this event listener.
Help/guidance appreciated.
if (arrUserData.userPrefWordLog === 1){
$('#logMode').prop('checked', true).change(function (e) {
console.log('ON - stop prop...2');
e.stopPropagation();
});
} else {
console.log('OFF - stop prop...1');
$('#logMode').prop('checked', false).change(function (e) {
console.log('OFF - stop prop...2');
e.stopPropagation();
});
}
console.log('Now set listener for CHANGE to wordPrefs...');
// $('body').on('change', '.wordPref1', function () {
$('#logMode').change(function () {
console.log('Fire change to logMode...');
updatePrefs();
});
Upvotes: 1
Views: 51
Reputation: 2859
Here is an example of that to which I was referring in the comments. It is in plain JS and I assume you can figure out how to apply in jQuery to fit your code. There is one change event listener on the parent container. The console will display which element was changed and whether it was checked or unchecked by displaying the boolean value.
If having a single listener on a parent container does not fit your purposes, this code should still illustrate that you don't need two change event listeners on a single element in the code that updates the last saved user preferences.
I hope this helps.
I added a second version of the example that does not require the matches
method or searching the DOM for each individual checkbox by class or ID. If you can structure the HTML and data object that holds the users' preferences in a similar manner, you can traverse the DOM and make use of the HTML data attribute to simplify the code further.
First Example
"use strict";
let userPref_1 = 1,
userPref_2 = 0,
userPref_3 = 1;
// Restore last selected user settings.
document.querySelector('.set_1').checked = userPref_1 === 1 ? true : false;
document.querySelector('.set_2').checked = userPref_2 === 1 ? true : false;
document.querySelector('.set_3').checked = userPref_3 === 1 ? true : false;
// Add change event listener to parent container.
document.querySelector('.parSet').addEventListener( 'change', chg_evt, false );
function chg_evt(evt)
{
let e = evt.target;
if ( e.matches( '.set_1' ) )
{ console.log( 'Element set_1 changed to : ' + e.checked ); }
else if ( e.matches( '.set_2' ) )
{ console.log( 'Element set_2 changed to : ' + e.checked ); }
else if ( e.matches( '.set_3' ) )
{ console.log( 'Element set_3 changed to : ' + e.checked ); };
// Invoke update user preferences function here.
} // close chg_evt
<div class="parSet">
<label><input class="set_1" type="checkbox" role="button" unchecked>Setting One</label>
<label><input class="set_2" type="checkbox" role="button" unchecked>Setting Two</label>
<label><input class="set_3" type="checkbox" role="button" unchecked>Setting Three</label>
</div>
Second Example
let userPrefs = [ 1, 0, 1 ],
p = document.querySelector('.parSet'),
C = p.children,
c, f;
// Restore last selected user settings.
for ( c of C )
{
f = c.firstElementChild;
f.checked = userPrefs[ f.dataset.i ] === 1 ? true : false;
};
// Add change event listener to parent container.
p.addEventListener( 'change', chg_evt, false );
function chg_evt(evt)
{
let e = evt.target;
console.log( 'User preference ' + ( parseInt(e.dataset.i) + 1 ) + ' changed to : ' + e.checked );
// Invoke update user preferences function here updatePrefs( e.dataset.i, e.checked) if needed.
} // close chg_evt
<div class="parSet">
<label><input data-i="0" type="checkbox" role="button" unchecked>Setting One</label>
<label><input data-i="1" type="checkbox" role="button" unchecked>Setting Two</label>
<label><input data-i="2" type="checkbox" role="button" unchecked>Setting Three</label>
</div>
Upvotes: 1
Reputation: 355
If the whole code you have given is in the ready function, this shouldn't happen. The program should get executed sequentially. The code below is set up in the JSFiddle below which will show that no extra event is firing before registering an event.
$('#logMode').prop('checked', true);
$('#logMode').prop('checked', false);
console.log('Now set listener for CHANGE to wordPrefs...');
$('#logMode').change(function () {
console.log('Fire change to logMode...');
});
https://jsfiddle.net/ashhaq12345/rbnztvx0/4/
Upvotes: 0