jquery toggleClass not working on img element - javascript

I have two buttons ("Approve","Unapprove") and an image of a loading spinner. By default the Approve button is showing and the Unapprove button and loading spinner image are hidden.
When the "Approve" button is clicked I want it to disappear, show the loading spinner, and then perform an ajax request. On completion of the request, I want the loading spinner image to get hidden again. If the ajax request was successful, I want the "Unapprove" button to show. If the ajax request failed, I want the "Approve" button to show again.
The problem I have is that upon completion of the ajax request, my .always() method runs but the spinner image does not get hidden. Here is an example:
https://jsfiddle.net/ebme6fjs/7/
If I change the url of my ajax request to give me a 404 not found error, this process works like it's supposed to:
https://jsfiddle.net/ebme6fjs/8/
Does anyone know why in my first case the spinner.toggleClass("hide"); command isn't running in the always() function? Thanks.
UPDATE:
One interesting thing I found is that if I redefine my spinner variable in the .always() function, it works correctly on a succesful ajax request:
https://jsfiddle.net/ebme6fjs/9/
If you modify the ajax url to receive a 404 though, the same problem of the spinner not disappearing happens again.

In a context where you always want to add the class, use addClass, not toggleClass. In your code, the class is toggled twice: Once in "always" and then again in "fail". So it ends up in the same state as before.
The reason you are targeting the image twice, is because you are defining otherButton like this:
var otherButton = currentButton.siblings();
When what you want to do is this:
var otherButton = currentButton.siblings('button');
toggleClass is most useful when you might want to add or remove the class depending on some condition.

In your code, you were toggling hide class on image twice, in always() callback and done()/fail() ones.
This is because you were targeting image in both variables, spinner & otherbutton.
You should define otherbutton as following:
var otherButton = currentButton.siblings("button");
Not including image in matched set.
As a side note, for code readability, you should use addClass()/removeClass() instead as noted in KWeiss answer and btw, set all your logic for displaying/undisplaying elements only in always() callback.

Related

jQuery .off() not removing event

The setup I have makes an ajax request to a php script to grab some images from the server and display them. During the ajax request, I enable some additional controls. I have two modes for the images, one that brings up a larger version of the image, and one that lets me sort them.
I implemented a checkbox so that I can swap between the two modes. Whenever I click search and invoke the ajax request, the images correspond to the mode that the checkbox had when they were created.
What I want to do is make it so clicking the checkbox allows me to swap the mode without having to search again.
$.ajax({
url: './search.php',
type: 'POST',
success: function(data) {
$("#images").html(data);
enableControls();
},
This is the snippet of the ajax request where I receive the images, add them to my page, and enable the controls.
$('#sortable').change(function() {
enableControls();
});
This is the snippet where I allow my checkbox to change the state of the controls.
function enableControls() {
$('.img-responsive').off();
if($('#sortable').is(":checked")) {
$('.img-responsive').on('dblclick', sortableDoubleClick);
}
else {
$('.img-responsive').on('dblclick', imgDoubleClick);
}
}
This is a snippet where I enable the double click behaviour. Double clicking while it's in sort mode will make that image go to the front (top left) of the rows/columns of images. Double clicking while it's in the non-sort mode will bring up a larger version of the image.
The img-responsive class is a class all the images returned from the search have, and is how I reference them as a group.
Unfortunately, the line
$('.img-responsive').off();
does not work the way I want it to. I want it to completely disable existing controls so I can set whatever new ones I want. It does not do that. What happens right now is that I get both double click controls available.
So let's say I search in non-sort mode and then click my checkbox to put it in sort mode. When I double click on an image, it opens up the larger version of it AND moves it to the top left.
How can I fix this? I assume the way I am invoking .off() is incorrect, but as far as I understand, .off() should remove all the event handlers for the selector, so why is it the case that I still retain the original event handler?
I ended up solving my problem. In the portion of the code:
$('.img-responsive').on('dblclick', imgDoubleClick);
There was some additional code for touch controls:
$('.img-responsive').each(doubletapCover);
For some reason, even though that code path never occurred, it seemed to invoke that code. I made a change so it's not ever called and it fixes my problem.
Thanks to everyone who tried to help.

Element insertion is not reflecting during jquery AJAX

Am trying to create a DIV with image inside another DIV for showing AJAX processing.Am doing it in JQUERY AJAX "beforeSend". What happens is, the image am inserting get visible only after the AJAX call. I have inspected the DOM, the insertion is happening in "beforeSend" but gets visible after AJAX call. below is the piece of code
options.beforeSend = function(jqXHR, settings){
$('#'+_divId).prepend(jQuery('<div/>', {
class : 'ajax-progress'
}).append(jQuery('<img/>').attr('src',"/images/ajax.gif")));
}
I thought the image download has the problem. so i tried having the div in DOM with "display:none" and detaching it using jQuery and inserted in "beforeSend". but no use.
$('#'+_divId).prepend(
$('#imageDiv').detach());
I cant do it in "ajaxStart".
I got the reason. It is not working in AJAX call with attribute "async : false".
As AJAX is made synchronous, the DOM updates are suspended till call gets complete.

Execute 2 JavaScript functions in order

I have a function with two lines and I want them to be executed in order. The function is :
showNotification(message){
$("#notificationMessageBody").html(message); // line1
$("#notificationModal").modal('show'); //line2
}
So, whenever I call showNotification("Hello World!") how do I ensure line1 is executed before line2 (meaning content loading is done before the modal triggers)
Basically, I am trying to fill in my message in modal body and then show it, not before filling.
--EDIT--
The functions are indeed executing one after the other, but my modal pops before jQuery loads my message into my #notificationMessageBody
As a result, for example : If I call showNotification("Hello") I get a modal with "Hello" (the arrangement of modals and stuff is done), but then after that if I call showNotification("World") modal appears with "Hello" first then after that it changes to "World".
Note : "Hello" and "World" are big junk of text, so loading that into my DIV must be taking some time, I believe. Even though they are executed one after other, it appears (to common-er) that firstly modal is popping and then replacement is done. I hope the picture is a little clear now.
No AJAX involved anywhere here around the function. Basically, this is my custom alert() function one can say. A modal with proper ID is there in my page. I change the modal-body content(with jQuery's .html() function) and trigger the modal to show, as seen from the code.
You can use the .promise() method (added in jQuery 1.6) to ensure the second one is executed after first is completed:
$("#notificationMessageBody").html(message).promise().done(function() {
$("#notificationModal").modal('show');
});

How to prevent jQuery from changing href attriute on click?

I have a collection of anchor tags, which look like so:
<a href='#123'></a>
I use the #123 as an id, which I supply to an AJAX call. I make the call on click of a link. After a link is clicked, besides the displaying of the results of the AJAX call, two things happen:
the url in the address bar gets the hash like so: www.localhost.com/foo#123.
The href attribute of the clicked link gets changed from #123 to the AJAX address www.localhost.com/bar#123. If I don't make an AJAX call (for test purposes), then it changes to the current url in the address bar: www.localhost.com/foo#123.
If I use event.preventDefault, than both of these things don't happen. I want to keep 1. (changing of the hash value in the address bar), but I don't won't to keep the value of the href, as otherwise, a subsequent click on the same link results in an error.
How can I do this?
My code
I will include a simplified version of my code, which still produces the same effect.
makeAjaxCall = (brandId) ->
alert 'OK'
$ ->
$('.box-trigger').on 'click', (e) ->
makeAjaxCall($(this).attr('href'))
This code gets loaded on page www.localhost.com/foo. If I click on the link, than I gen an alert with 'OK', the address changes to www.localhost.com/foo#123 and the href of the link also changes to www.localhost.com/foo#123.
Clarification regarding foo and bar
In the makeAjaxCall function, if I really make an ajax call to the url www.localhost.com/bar, then the href changes to www.localhost.com/bar#123. However, I removed the Ajax call to try to debug the problem. In that case the href changes to the current url, in this case www.localhost.com/foo#123.
Solution
I ended up using event.prevenDefault + manually appending the hash to the address bar url like so:
$ ->
$('.lightbox-trigger').on 'click', (e) ->
e.preventDefault()
makeAjaxCall $(this).attr('href')
window.location.hash = $(this).attr('href')
Solution suggestion 1
I would use the data parameter, as suggested by Optimus in the comments. Like this:
HTML
<a class="box-trigger" data-id="#123"></a>
Note: I don't have the href parameter at all, this is because we don't want the URL to be affected at all, right? It's an AJAX call after all. (This will, however make most browsers not recognise the <a> tag as a link. It will lose its "link styling" as it where. You can easily simulate this by simply styling it using CSS.)
CoffeeScript
makeAjaxCall = (brandId) ->
alert 'OK'
$ ->
$('.box-trigger').on 'click', (e) ->
makeAjaxCall($(this).data('id'))
Note: I have never used CoffeeScript, so this is just a guess.
Solution suggestion 2
If you, for some reason wish to have the ID as an anchor inside the href, you could have a look at the JQuery event.preventDefault() function. This might just stop the URL from changing and all other behaviour you're experiencing.

Why are parameters not sent to a function in setInterval() all the time?

I have a page that automatically refreshes content via Ajax. I want this to be subtle so I do not want to display my loading gif during automatic page refreshed. So I did something like this with my getPosts function (unnecessary code cut out for succinctness)
function getPosts(image)
{
//loading icon while getPosts
if (image)
{
$("#postcontainer").bind("ajaxStart", function(){
$(this).html("<center><img src='ajax-loader.gif' /></center>");
});
} //... ajax call, etc. don't worry about syntax errors, they aren't in real code
I know the center tag is deprecated, just a shameless shortcut.
And then I will set the interval like setInterval(function() { getPosts(false); }, 10000);
Therefore my automated calls will not trigger the image to display
All my manual refreshes will then call it like this getPosts(true);
You can (probably) see the bug in action at my personal site
The problem is, the setInterval function seems to use the image bool from the latest function call. So it does not display the image at first during automated calls, but after I click a manual refresh, it starts showing the image during each call.
How can I combat this?
Thanks for anyone who views/posts this topic! I hope this question becomes a good reference to others.
The problem is that once you've bound your "ajaxStart" handler to the container it will execute on every ajax call for that container. That is, the first time you call it with getPosts(true) it will create the binding. The next time you call it with getPosts(false) it doesn't go down that if path but the binding still exists so when you do your ajax call the handler still executes - and the handler doesn't doesn't have any conditional logic. (Actually, I believe you'll end up with multiple bindings on the "ajaxStart" event, one created every time you call getPosts(true), but they're probably not noticable since they all just do the same thing overwriting the same html.)
Why not do something like this:
function getPosts(image) {
if (image) {
$("#postcontainer").html("<center><img src='ajax-loader.gif' /></center>");
}
// Actual ajax call here
}
setInterval(function() { getPosts(false); }, 10000);
Because after the first manual refresh you have attached a event handler "ajaxstart" which is to show the image when a ajax call starts. Now this event handler is there even in case you call the function with image = false. It will get triggered on all ajax calls.
What you need to do is something like:
$("#postcontainer").bind("ajaxStart", function(){
$(this).html("<center><img src='ajax-loader.gif' /></center>")
//Remove event handler
$(this).unbind("ajaxStart");
});

Categories