0

I have a div containing a photo that a user clicks on to reveal a name. I've set up a snazzy flip effect for this reveal. On the next click, a new photo and name are loaded (via ajax) to replace the old ones.

In order to provide a smooth transition, I've set it up to flip 90 degrees (i.e. become invisible), load the new set, then complete the flip to reveal the photo. Here's code that does this, running beautifully:

$name_div.one("click", function(){
        $flipper.css("transform", "rotateY(270deg)") ;

        // Update mastery to level 1        
        updateMastery(student, 1, i)

        $flipper.css("transform", "rotateY(0deg)") ;
    })

Only one problem... the updateMastery() function begins running before the transform has completed. This means the new name can replace the old name before it's been hidden.

$name_div.one("click", function(){
        $flipper.css("transform", "rotateY(270deg)") ;

        // Update mastery to level 1        
        $flipper.on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', 
            function() {
                 updateMastery(student, 1, i)
            });

        $flipper.css("transform", "rotateY(0deg)") ;
    })

This code does what I want it to do. It waits for the transition to end, then loads the new name/photo, then finishes the flip. On the first couple of clicks it runs well, but as I click and progress through the photos the script gets progressively slower and slower until eventually hanging my browser.

Any idea why the .on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd'... script would do this? Is it storing something in the browser each time? Is there something I can do to prevent or clear this?

Thanks

Rob
  • 128
  • 1
  • 7
  • 29
  • 5
    Every click adds another event handler to the "flipper". Adding an event handler *inside* another event handler is almost always not the right thing to do. – Pointy Aug 15 '18 at 18:01
  • Aha... any suggestions on how to fix that? – Rob Aug 15 '18 at 18:02
  • 1
    You only need to add the handler *once*. – Pointy Aug 15 '18 at 18:02
  • You mean use $flipper.one instead of $flipper.on ? – Rob Aug 15 '18 at 18:02
  • No, I don't. Don't add another copy of the event handler if you've already added it. Use delegation and add it outside the handler completely, if you can (and that should be possible). – Pointy Aug 15 '18 at 18:04
  • Thank you. Would you be able to point me to an example of this being done, please? – Rob Aug 15 '18 at 18:08
  • Simplest thing to do is just keep a flag somewhere and skip the `.on()` call if it's already been set. – Pointy Aug 15 '18 at 18:09
  • 1
    Upon googling "javascript call function after css transform is complete" I found [this post](https://stackoverflow.com/questions/9255279/callback-when-css3-transition-finishes), which has a number of ideas that you may find useful. You should be able to use `.off` to turn off the handler after it fires once, although simply putting the transitions handler outside of the click handler should work as Pointy suggests. – BobRodes Aug 15 '18 at 18:38
  • Have you tried using [`.one()`](http://api.jquery.com/one/) insted of `.on()`? – Louys Patrice Bessette Aug 15 '18 at 21:34
  • Thank you all for your help. Pointy's diagnosis was correct, and BobRodes's solution was the best I think. I was indeed adding handlers repeatedly with my original code. Trying to move the transitions handler outside the click handler was not ideal for my situation, as it complicated other things. Using .off() after the handler is triggered worked beautifully instead. – Rob Aug 16 '18 at 10:05
  • Incidentally, .one() doesn't work here because it only removes the handler that actually fired, rather than all of the aliases that were added for multiple browser support. .on() and .off() was my chosen solution. I'll add an answer with credit. Thanks again. – Rob Aug 16 '18 at 10:06

1 Answers1

0

Answer based on comments from @Pointy and @BobRodes:

The problem, as identified by @Pointy:

Every click adds another event handler to the "flipper". Adding an event handler inside another event handler is almost always not the right thing to do


Solution 1 (@Pointy):

Simplest thing to do is just keep a flag somewhere and skip the .on() call if it's already been set.

This is a good solution. However, because I had multiple examples of this transition happening, but only wanted the handler to be there under certain circumstances (specifically when one "side" of the is clicked, but not the other), this led to too much additional code for me.


Solution 2 (@BobRodes):

You should be able to use .off() to turn off the handler after it fires once

This works perfectly:

$flipper.on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', 
            function() {
                $flipper.off() ;
                updateMastery(student, 1, i, $flipper) ;     

            });

Postscript

Have you tried using .one() instead of .on()? ~@Louys Patrice Bessette

If I was only attaching one handler, this would work. However, because I attached multiple handlers (to provide cross-browser support), only the one that has been triggered gets removed by .one(). The others remain, getting added to on every click.

Rob
  • 128
  • 1
  • 7
  • 29