简体   繁体   中英

Inside my function there's a jQuery animate. I'd like to return from my function only after the animation is complete. How?

Consider this function:

function hideElements( selector ) {
    var number = $( selector ).length;
    if ( number ) {
        $( selector ).animate( { opacity: 0 }, 1000, function() {
            *RETURN* number + " elements are now completely hidden.';
        } );
    } else return "No matching elements found";
}

var returnedMessage = hideElements( ".hideable-divs" );

The uppercase RETURN is the callback's return and so it won't work; I just put it there to convey the idea. How can I make it so the hideElement function returns something only after the animation is complete? Yes, whatever code that needs the returnedMessage variable will have to wait (synchronously(?)) until the animation is complete. Thank you!

** EDIT ** This isn't a duplicate question but rather a very specific scenario I'm dealing with:

A third-party script uses a confirm dialog to ask the user if they're sure they want to delete a div. If the user clicks OK, the element is removed instantly from the DOM.

My goal is to:

  1. Bypass the confirmation dialog so the user doesn't have to confirm.
  2. Animate the deletion so the user clearly sees it happening and, in case they clicked by accident, undo if necessary.

For this, I'm redeclaring window.confirm like in the code sample below. (To eliminate any confusion, please note that this update changes the idea behind my original question and the code block I posted above.)

var clickTarget;
$( document ).on( 'mousedown', function( event ) {
    clickTarget = event.target;
} );
window.confirm = function( message ) {
    if ( message.indexOf( 'OK to delete' ) !== -1 ) {
        var $element = $( clickTarget ).parent();
        $element.animate( { height: 0 }, 1000, 'swing', function() {} );
        return true; // the 3rd party script receives this confirmation asynchronously and removes $element before it has a chance to be animated.
    } else return confirm( message );
};

At the moment I'm achieving the effect by creating a clone and animating that instead of the actual element to be deleted. But this is rather "dirty" and I was wondering if there's a "cleaner" way to do it. Thanks!

As suggested in the link provided by Weedoze, normally you don't want to block a script on asynchronous code, but rather provide a callback.

The main difference with said link is that you want to wait till all animations are completed, not call it on each element's animation finish. In JQuery you can do that with .promise().done() :

 function hideElements( selector, callBack ) { var sel = $( selector ); if ( sel.length) { sel.animate( { opacity: 0 }, 1000).promise().done(function(){ callBack(sel.length + " elements are now completely hidden."); }); } else callBack("No matching elements found"); } hideElements('div', console.log); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div>a</div> <div>b</div> <div>c</div> 

An alternative would be to make your hideElements function itself return a Promise (and return a resolved promise if no elements are found):

 function hideElements( selector ) { var sel = $( selector ); if ( sel.length) { return sel.fadeOut(1000).promise().then(function(){ return sel.length + " elements are now completely hidden."; }); } else return Promise.resolve(("No matching elements found")); } hideElements('div').then(console.log); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div>a</div> <div>b</div> <div>c</div> 


edit

Based on the added info: letting the 3rd party code wait is a bit tricky. You can make your own code wait (eg with async await), but by definition the calling code should continue (not blocking the ui). What you can do is cancel the delete on the first click (return false), but remember which element the code was called for. Then in the callback, invoke the click on the same element to restart the 3rd party code and this time return true to let the delete proceed:

 let clickTarget, curDelete; $( document ).on( 'mousedown', function( event ) { clickTarget = event.target; } ); let defConfirm = window.confirm; //needed to store the default functionality to avoid recursion in the call below window.confirm = function(message) { if ( message.indexOf( 'OK to delete' ) !== -1 ) { if(clickTarget === curDelete)return true; //the confirm is called from the current delete, invoked by the click from the callback, return true var $element = $( clickTarget ).parent(); //layout based on your example $element.slideUp(2000, function(){ //used slide up, but the callback can be used for any animation curDelete = clickTarget; //NB: perhaps an extra check is needed to see if another delete isn't started? $(curDelete).click(); //invoke 3rd party script by emulating click (preferably, if a method is known it is called directly) }); return false; //this result is returned before the animation finishes to stop the direct deletion } else return defConfirm( message ); //show default confirm window }; //3rd party spoof $('div > div').click(function(){ if(confirm('dsaf asfda OK to delete aadf?')) console.log('3rd Party delete executing'); }); 
 div > div{ width:100px; border: 1px solid gray; margin: 2px; cursor: pointer; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div> <div id=a>click me</div> <div id=a>or me</div> <div id=a>or me</div> </div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM