0

You'll have to forgive me if I show any kind of ineptitude here, jquery and java isn't my usual area of work. But here goes:

I have a page that shows a random list of items that are pulled from a server using an API call. The idea is that every time the user clicks "generate" a new list is produced and inserted into the page. This works but it's very fast and all the user sees is a list rapidly changing. To spruce things up I've decided to put some nice animations and effects in.

I've got a jquery function that loops through each element in the list of child elements and toggles the css style of the child element so that an effect from the animate.css library is applied. The problem is when I have another function that loads the new list and this is called immediately and therefore all of the css transitions are ignored; or rather they don't get a chance to run because the second method immediately triggers.

I've tried using a callback and had no joy, I've tried using deferred objects. No luck at all.

Here's the code I have so far:

function removeOldContent() {

$('#removableContent > div').each(function (index) {
    var elm = $(this);
    setTimeout(function () {
        elm.toggleClass('customAnim', function () {
            $(this).remove();
        });
    }, index * 150);
});
}

function getList() {    
var rId = $('.tab-content').find('.active').data('d-id');
var serviceUrl = '/GetRandom/GetList';
$.ajax({
    type: 'POST',
    url: serviceUrl,
    data: {
        reportId : rId
    },
    success: function(data) {
        $('#reportContainer').html(data).fadeIn('slow');
    }
});
}

Ideally I'd like to be able to let removeOldContent() finish completely, after all the timeouts have run. And then trigger getList() to update the content. I'll work on making a nice transition for the inbound data but first I just need to get this working.

Any advice or pointers would be greatly appreciated.

***** Update ******

I've made a fiddle. Not giving me the same error as my dev env but should be close enough for you to see

https://jsfiddle.net/rdt1pfhk/9/

Ian Murray
  • 339
  • 1
  • 12
  • Show how you currently call removeOldContent and getList in code? – Vinod Louis Nov 25 '16 at 10:10
  • try [`queue()`](https://api.jquery.com/queue/) – zer00ne Nov 25 '16 at 10:11
  • 2
    Can you make a fiddle? I don't see how to code is all wired together, seems like there's some missing parts in what you show. – Swimburger Nov 25 '16 at 10:16
  • At the minute it's just called by having a button that triggers the function $(document).on('click', '.quickPick', function (e) { removeOldContent(); }); I would make a fiddle but the API is all internal still and all of my CSS and animations are bundles up in a SASS package. I'll try and make one though. Thanks – Ian Murray Nov 25 '16 at 10:19
  • Create a js fiddle of the problem https://jsfiddle.net/ it will make it easier for us to debug the problem – mbx-mbx Nov 25 '16 at 10:45
  • As requested, I've made a fiddle - https://jsfiddle.net/rdt1pfhk/9/ - the second method getInitialPicks() doesn't work on there properly though. On my dev env the animations won't run - or rather they don't get the chance to run since the getpicks method runs almost immediately and just overwrites the content – Ian Murray Nov 25 '16 at 11:21

2 Answers2

1

Your problem is with the timing of events. Your removeOldContent function uses a setTimeout function which in turn animates and removes the items from the DOM. Your getList() function was executing before the other function had finished. I put a quick untidy solution together using your fiddle. I return a jquery deferred object from you removeOldContent method and then only call the getList when that is resolved (and the older items removed from the dom). It is not the neatest but it will point you in the right direction. I updated your fiddle here: https://jsfiddle.net/rdt1pfhk/16/

function removeOldContent() {
    var deferred = new jQuery.Deferred();
    ....
    return deferred;
}

$(document).on('click', '.quickPick', function (e) {
    removeOldContent().then(function(){
       getList();        
    });
});
mbx-mbx
  • 1,765
  • 11
  • 23
  • That's doing the trick nicely. The elm.remove() is stopping the animation from playing but I've moved it around and fixed it. That is absolutely spot on! Thanks for your help!!! – Ian Murray Nov 25 '16 at 12:24
  • 1
    @IanMurray Check the latest fiddle (16) I put the new html in the return of the ajax call too. – mbx-mbx Nov 25 '16 at 12:24
  • Hmm..having elm.remove() in there with the promise() stops the animation from playing. It just removes the element from the dom before the animation can play it would seem. – Ian Murray Nov 25 '16 at 12:41
  • You might be ok with not removing the element from the dom anyway as in the success of your ajax call you replace the html in the div anyway – mbx-mbx Nov 25 '16 at 13:51
  • 1
    Ahh you are using a css transition which is why if you put the remove in there the animation does not happen. If you want to do that, you will have to use something like the slide effect in jquery ui e.g. https://jsfiddle.net/rdt1pfhk/20/ – mbx-mbx Nov 25 '16 at 14:03
  • 1
    You could also look into the transitioned event fired when the css transition is completed: https://developer.mozilla.org/en-US/docs/Web/Events/transitionend – mbx-mbx Nov 25 '16 at 14:05
0

If I understood correctly you need to only delay some functions for as long as you need. I think the answer you are looking for could be found here:

How to delay calling of javascript function?

Also, I'd like to mention that I don't see a

$(document).ready(function () {
    //your code here
});

anywhere. Maybe I'm wrong but since you mentioned that CSS is ignored, are you sure the page is loaded before your code starts being executed?

Community
  • 1
  • 1
Jabberwocky
  • 768
  • 7
  • 18
  • I am using a $(document).ready(function). I just thought the two functions would be more than an enough to show what I'm trying to do. The page is definitely loaded. If I don't call the function that gets the new list of items to update the page with I get a very nice looking effect where each element in the list zips off to the left. As soon as I add the function to update the data that effect stops; not because it's breaking the CSS or the previous script but because it executes before the animations on the first method are finished running. – Ian Murray Nov 25 '16 at 10:39
  • Furthermore, I already have a setTimeout in my first method, it's what makes each element slide out staggered, individually – Ian Murray Nov 25 '16 at 10:41
  • They indeed where. I just wanted to mention it, just in case. Then, it really seems that my link will get you want you want. Looks like your function is 'too early' executed. What if you try to delay it until the animation finishes? – Jabberwocky Nov 25 '16 at 10:43
  • I think I understand where the problem is. You're using an AJAX call, which is executed asynchronously, because that's what AJAX does. Which means that it's executed while your first function gets executed too. You need to load AJAX synchronously, I think. If that helps you, I'll update my answer. – Jabberwocky Nov 25 '16 at 10:48
  • Because I'm not using jquery animations. I am using the animate.css library to animate each child element. As such i loop through each one and use a delay to make the elements pop away one by one. The problem is that the delay doesn't take into account the triggering of the next method. – Ian Murray Nov 25 '16 at 10:49
  • I think the problem is the asynchronous AJAX call. Try async: false in your AJAX call. If that doesn't work, I don't know. That's as far as my knowledge goes. – Jabberwocky Nov 25 '16 at 10:52
  • 1
    Do not set async to false! – mbx-mbx Nov 25 '16 at 11:15
  • Is it bad practice? He could also just delay the second function, that would be the same thing I think. – Jabberwocky Nov 25 '16 at 11:22
  • Never mind,that was my bad, it's deprecated as it looks like. He could still do it in another way. he could manually add an overlay to prevent the user to interact with the interface, and then remove it once the AJAX query is done. – Jabberwocky Nov 25 '16 at 11:24