Reputation: 5213
I've had the following code working for some time in my Rails app:
$(function () {
// Change the link's icon while the request is performing
$(document).on('click', 'a[data-remote]', function () {
var icon = $(this).find('i');
icon.data('old-class', icon.attr('class'));
icon.attr('class', 'glyphicon glyphicon-refresh spin');
});
// Change the link's icon back after it's finished.
$(document).on('ajax:complete', function (e) {
var icon = $(e.target).find('i');
if (icon.data('old-class')) {
icon.attr('class', icon.data('old-class'));
icon.data('old-class', null);
}
})
}
The code replaces a glyphicon with a "refresh" glyphicon and spins it until the AJAX request is complete, then replaces it with the original glyphicon.
I recently noticed the code had stopped working. The AJAX request works fine, but I no longer get my spinning refresh glyphicon during the AJAX call. It's possible an upgrade to Rails 5.1 has caused the problem.
I do have gem 'jquery-rails'
in my Gemfile, so I had thought the Rails 5.1 upgrade was fully backward-compatible. It could be some other change that has cause this to break.
I've updated the first function as follows:
$('a[data-remote]').on('click', function () {
var icon = $(this).find('i');
icon.data('old-class', icon.attr('class'));
icon.attr('class', 'glyphicon glyphicon-refresh spin');
});
This works the first time a button is clicked, but if the same button is clicked again, I get no spinner.
Is there a solution to this problem, preferably using the standard DOM API? I messed around with:
document.querySelectorAll('[data-remote]').onclick = function () { ... }
but haven't had any luck.
Am I even close to a workable answer? Would this be a good candidate for using addEventListener?
Upvotes: 1
Views: 808
Reputation: 551
@Jeff is probably right, you're replacing the link element and loosing the listener. This would not be a problem using the $(document).on('click', 'a[data-remote]', ...)
pattern, as events fired on elements added to document after declaring the event handler would be caught (see this question). The issue is something seems to be preventing the link click
event from bubbling up to the document
, and the listener function fails to be called.
Binding to body
instead of document
appears to resolve the issue:
$("body").on('click', 'a[data-remote]', function () {
var icon = $(this).find('i');
icon.data('old-class', icon.attr('class'));
icon.attr('class', 'glyphicon glyphicon-refresh spin');
});
Upvotes: 3
Reputation: 660
Does your ajax request also refresh the element that has the original listener on it? Whats happening sounds like the listener is gone.
$('a[data-remote]')
is clicked and the listener tells the spinner to spin.
The AJAX calls probably replaces some elements on the page including the original $('a[data-remote]')
element.
Because the listeners arent added again after ajax page load/refresh, clicking the button the second time will not work.
Just a hypothesis but you can also put a debugger
within the click handler and open the google chrome extension tools. See if the script pauses each time you click the button.
Update with basic fix:
function toggleIcons() {
$('.regularIcon').toggle();
$('.spinningIcon').toggle();
}
function toggleButtons() {
$('.postButton').toggle();
$('.deleteButton').toggle();
}
$('.postButton').click(function(e) {
e.preventDefault();
toggleIcons();
// ajax call here
$.ajax({
type: "GET",
url: "/route",
data: data,
dataType: "JSON",
}).success(function(json) {
toggleIcons();
toggleButtons();
})
});
// Listener for deleteButton
$('.deleteButton').click(function(e) {
e.preventDefault();
toggleIcons();
// ajax call here
$.ajax({
type: "GET",
url: "/route",
data: data,
dataType: "JSON",
}).success(function(json) {
toggleIcons();
toggleButtons();
})
});
Relies on you making sure both buttons are styled in the same place on the DOM.
Upvotes: 2