1

i have the following jquery code. basically - it takes a value from an anchor - and displays a sub-menu content below it. this works perfectly.

$(function(){ 

    $('.plus').live('click', function(event){
        event.preventDefault();
        $(this).addClass('lower');
        var existingPath = $(this).attr('rel');

        GetSubs(this, existingPath);
        $(this).removeClass('plus').addClass('open');   //.delay(10000).removeClass('lower');
    });

    function GetSubs(IDclicked, existingPath){
        var dataString;
        dataString = 'lang=<%=Lang%>&rel=[' + existingPath + ']';


        $.ajax({
            type: "GET",
            url: "/includes/getSubCatMenuLinks.asp",
            data: dataString,
            success: function(data) {
                $(data).insertAfter(IDclicked);
            },
            error: function(obj,msg) {
                alert('*** Error! ***\n\n'+msg);
            }  

        });
    } 
});

what i would like to do - is have a a "loading" icon showing, while the content is loading - and then remove it when done.

displaying it is fine - thats whats done in the line

 $(this).addClass('lower');

when a few lines down, i try remove that class - but the movemext is so fast - that the loading icon doesnt even show. ie - the ajax content hasnt appeared yet, but the jquery code has already loaded the class, loaded the ajax (somewhere) and then removed the class - so the loading icons doesnt even display.

i tried using the delay method - to have it removed a seconds or a few later - but it doesnt work.

any help appreciated!

thanks!

1
  • I think you need to move the line that you used delay on into the success callback of your GetSubs function. Commented Aug 19, 2012 at 19:38

2 Answers 2

5

As already said, .delay only works with animation methods. Simply remove the class when the Ajax request completed. To keep your code decoupled, make use of the deferred object returned by $.ajax and pass a callback to the .always method [docs]:

 var $this = $(this).addClass('lower');
 // ...

 // or GetSubs(this, existingPath).done if you only want to remove the loader
 // when the call was successful. Use .fail to handle error cases.
 GetSubs(this, existingPath).always(function() {
     $this.removeClass('lower');
     // or
     // $this.removeClass('plus').addClass('open').removeClass('lower');
     // not quite sure when exactly you want to remove / add which classes
 }); 

 // ...

function GetSubs(IDclicked, existingPath){
    // ...

    return $.ajax({ // <- return the $.ajax return value
        // ...
    });
} 
Sign up to request clarification or add additional context in comments.

3 Comments

Out of curiosity, if I understand the docs correctly ajax returns a promise interface, does that mean I could use .done instead of .always if I would like to only execute something on the success of the ajax call?
@François: Yes, that would also be possible.
@FelixKing: Thanks for getting back +1 on the answer. Using the return object looks cleaner as the ajax call itself doesn't have to worry about executing it which is much better alright. Nice one.
1

delay() only works with animations, and not on functions like removeClass, for that you'll need a timeOut. Not only that, but the Ajax call is asynchronous, so your class is removed instantly and does not wait for the Ajax call to finish.

You could always do:

$(function(){ 
    $(document).on('click', '.plus', function(e){
        e.preventDefault();
        var self = this,
            existingPath = $(this).attr('rel');

        $(self).addClass('lower');

        GetSubs(self, existingPath, function() { //added callback
            setTimeout(function() { //and a one second delay
                $(self).removeClass('lower');
            }, 1000);
        });
    });

    function GetSubs(IDclicked, existingPath, callback){
        var dataString = 'lang=<%=Lang%>&rel=[' + existingPath + ']';

        $.ajax({
            type: "GET",
            url: "/includes/getSubCatMenuLinks.asp",
            data: dataString,
            success: function(data) {
                $(data).insertAfter(IDclicked);
            },
            complete: function() {
                callback.call(); //callback function is called when ajax is finished
            },
            error: function(obj,msg) {
                alert('*** Error! ***\n\n'+msg);
            }  
        });
    } 
});​

This waits for the ajax call to finish, then waits one second, and then does the class stuff. Usually a timer just to show off your nice spinner is considered bad UI, so I would just keep the callback function.

4 Comments

thanks gang. appreciate the help. the delay method was just aan attempt to try slow down the process - but what you all suggested worked perfectly - moving it into the success function.
Complete will always trigger so the class is removed regardless of success or failure. Just noting it in case OP wanted to only remove class on success.
@FrançoisWahl - That's actually why I added a complete function, to make sure the loader was removed, but if that is not how it was intended the callback could be called from the success handler instead. Using the built in deffered object instead of defining a callback is IMO a better solution, so I would go for the answer Felix posted instead !
@adeneo: Yeah, I looked at Felix's answer and like it as well. Using the returned promise interface does sound more proper alright.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.