1

I have a function of which I'm supposed to pause on mouseenter and pause on mouseleave but the problem is that the function is recursive. You pass in a parent and index and it will recursively loop through each inner div displaying and hiding. The function looks like this:

var delay = 1000;
function cycle(variable, j){
    var jmax = jQuery(variable + " div").length;
    jQuery(variable + " div:eq(" + j + ")")
        .css('display', 'block')
        .animate({opacity: 1}, 600)
        .animate({opacity: 1}, delay)
        .animate({opacity: 0}, 800, function(){
            if(j+1 === jmax){ 
                j=0;
            }else{ 
                j++;
            }
            jQuery(this).css('display', 'none').animate({opacity: 0}, 10);

            cycle(variable, j);
        });
}

I've tried setting a timeout and clearing it but it doesn't seem to do anything (it seems to ignore the timeout entirely), I've tried using stop() and calling the function again on mouseout but that seemed to repeat the function call (I was seeing duplicates) and it stopped mid animation which didn't work. I tried adding in a default variable at one point (var pause = false || true;) but I also couldn't get it to work as expected (though I feel the solution relies on that variable). I'm open to all suggestions but there are some rules of engagement:

Rules: There can't be any major changes in how this function works as many things rely on it, it's something I do not have control over. Assume the function call looks like this jQuery('#divList', 0) and holds a bunch of div elements as children.

The timeout function is the last solution I tried which looks like:

jQuery('#divList').on('mouseenter', function(){
    setTimeout(cycle, 100000);
})
.on('mouseleave', function(){
    window.clearTimeout();
});
8
  • When using stop, you need to pass true as an argument to prevent callbacks from being called. Commented Aug 18, 2014 at 16:19
  • window.clearTimeout requires a timeout ID which is returned from the setTimeout function. Commented Aug 18, 2014 at 16:19
  • That solution with a pause variable should work as well. Can you post the whole code of that try? Commented Aug 18, 2014 at 16:19
  • Technical issue here: This isn't a recursive call since only one instance of the cycle function exists at a time. This uses events to re-invoke itself -- something very different from a technical point of view. Were this actually recursive, you wouldn't be able to pause it. Commented Aug 18, 2014 at 16:30
  • @JeremyJStarcher Isn't any function that calls itself recursive? So wouldn't this be a recursive loop? Commented Aug 18, 2014 at 16:33

2 Answers 2

1

Perhaps something like this? I simplified the animation just to make the example simpler, but you should be able to adapt it to your needs.

First, we have a function that's responsible for animating a set of elements. Every function call returns a new function that allows to toggle the animation (transition between pause and resume).

function startCycleAnimation(els) {

    var index = 0,
        $els = $(els),
        $animatedEl;

    animate($nextEl());

    return pauseCycleAnimation;

    function animate($el, startOpacity) {
        $el.css('opacity', startOpacity || 1)
           .animate({ opacity: 0 }, 800, function () {
                animate($nextEl());
            });
    }

    function $nextEl() {
        index = index % $els.length;
        return $animatedEl = $els.slice(index++, index);
    }

    function pauseCycleAnimation() {
        $animatedEl.stop(true);

        return resumeCycleAnimation;
    }

    function resumeCycleAnimation() {
         animate($animatedEl, $animatedEl.css('opacity'));

         return pauseCycleAnimation;
    }
}

Then we can kick-start everything with something like:

$(function () {
    var $animationContainer = $('#animation-container'),
        toggleAnimation = startCycleAnimation($animationContainer.children('div'));

    $animationContainer.mouseenter(pauseOrResume).mouseleave(pauseOrResume);

    function pauseOrResume() {
        toggleAnimation = toggleAnimation();
    }
});

Example HTML

<body>
    <div id="animation-container">
        <div>Div 1</div>
        <div>Div 2</div>
        <div>Div 3</div>
    </div>
</body>

If you want something more generic, it seems there's a plugin that overrides animate and allows to pause/resume animations in a generic way.

Sign up to request clarification or add additional context in comments.

Comments

1

You will need to put a flag that each cycle checks before it determines if it is going to run. Then you can just change that flag when the mouse events are triggered. If you need to pick up where you left off when you unpause, consider saving the last value of j

function cycle(variable, j){
    if (window.paused) {
        window.last_j = j;
        return;
    }
    ...

Then when you want to pause, just set window.paused = true . To resume, change it back to false and call cycle again:

cycle(variable, last_j);

2 Comments

There's no reason to make this a global window property, especially since cycle can be invoked multiple times with different arguments.
I tried this but it never paused (I got all my expected console messages though) then multiple divs started appearing and cycling like there were multiple instances of the function: Paste Bin Code

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.