Reputation: 175
I'm currently developing a PHP web app, and I'm making use of the datatables jQuery plugin, and jQuery AJAX calls to create a dynamic table that can have elements edited, deleted and added. This (seems to be) working fine, however, I've observed that within some of my click handlers, AJAX calls are being fired multiple times - and I'm not quite sure why.
Here is the page at the moment so you have an idea of where I'm coming from:
As you can see, I have basic data on users, and actions to the left. Each row in the table looks like this in the HTML:
<tr>
<td><?= $userAccount['firstName'] ?></td>
<td><?= $userAccount['lastName'] ?></td>
<td><?= $userAccount['email'] ?></td>
<td><?= $userAccount['jobTitle'] ?></td>
<td class="text-right enrolr-datatable-actions-min-width">
<i data-userId="<?= $userAccount['id'] ?>" class="fas fa-user-edit enrolr-standard-icon mr-2 event-user-edit"></i>
<i data-userId="<?= $userAccount['id'] ?>" class="fas fa-user-times enrolr-danger-icon mr-2 event-user-delete-staff"></i>
</td>
</tr>
Since (after the initial page load/render) these table rows are being removed/added dynamically, I decided to therefore listen for events at the document level for clicks on the .event-user-delete-staff
class, like so:
$(document).on('click', '.event-user-delete-staff', function () {
// Storing some details for later use by handlers
const userEmail = $(this).closest('tr').find('td').eq(2).html();
const $button = $(this);
// Shows confirm dialog, only performs deletion when "yes" is clicked, which causes the third function parameter to run
confirmDialog(`Are you sure you want to delete ${userEmail}? This action cannot be undone.`, 'Confirm Deletion', function () {
const $parentToRemove = $button.closest('tr');
// Make post to remove user.
$.ajax({
type: 'POST',
url: '../php/account/_deleteUser.php',
data: {
id: $button.attr('data-userId')
},
dataType: 'json',
success: function (response) {
// Handle response.
if (response.success == true) {
displaySuccessToast(response.message);
// Fade out row then update datatable.
$parentToRemove.fadeOut(500, () => {
staffTable.row($parentToRemove).remove().draw();
});
} else {
displayErrorToastStandard(response.message);
}
},
error: function () {
// Show error on failure.
displayErrorToastStandard('Something went wrong while handling this request');
}
});
});
});
Now, the reason I noticed the AJAX calls are being made multiple times is because I noticed multiple toast messages are being displayed for each delete event (The displaySuccessToast
method, all this does is clone a toast template and display it). The following basically happens:
Having added some console logs and stepping through the code, I can tell this isn't because the $(document).on('click', '.event-user-delete-staff', function ()
event listener is firing multiple times. It only fires once per deletion, as it should.
I think the suspect is the confirmDialog()
method, as I've observed via some console logs, it is in here that it is firing multiple times.
This method looks like this:
window.confirmDialog = function (message, title, yesCallback) {
// Sets some details in the confirm dialog
$('#confirmMessage').html(message);
$('#confirmTitle').html(title);
// Shows the modal
$('#confirmModal').modal('show');
// On the "yes" button within the dialog being clicked, hides modal and calls the yesCallback function.
$('#confirmBtnYes').on('click', function () {
$('#confirmModal').modal('hide');
yesCallback();
});
// On "no" just hides the modal.
$('#confirmBtnNo').on('click', function () {
$('#confirmModal').modal('hide');
});
}
However, I suspect it's not really the fault of this method; but is some kind of misunderstanding of mine as to how function callbacks like this work, and using variables from the parent scope within them?
As I've said; I don't think this is because the event listener itself is firing multiple times, on the second deletion (and beyond...), the event still only logs being called once, it is within the confirmDialog
function callback method where it logs twice.
Can someone please advise what I'm doing wrong here, and what could be causing this behaviour?
Edit - For posterity, and anyone else who stumbles across this and wants to know what I changed...
The problem in this case, is that each time the confirmDialog
method was being called, it would add another listener to the yes button. This means, when the yes button in that dialog was clicked, it would fire the most recent event... and all the other ones attached before. I simply changed the confirm button yes dialog to look like so:
$('#confirmBtnYes').off().on('click', function () {
$('#confirmModal').modal('hide');
yesCallback();
});
Using off()
first ensures no other event listeners are lingering around on that yes button. Probably not the best or cleanest solution, but hey, it works.
A thank you to Nick with his answer below, who pointed out where I was going wrong 👍
Upvotes: 1
Views: 2079
Reputation: 147146
You're adding a new event handler to the Yes
button in the confirm dialog every time you call confirmDialog
:
$('#confirmBtnYes').on('click', function () {
$('#confirmModal').modal('hide');
yesCallback();
});
This means that the first time you click the yes button, yesCallback
will get called once, the second time yesCallback
will get called twice, etc. etc.
You're also doing the same for the No
button, but since that just hides the modal it doesn't really affect the operation of the page.
Upvotes: 2