Huey
Huey

Reputation: 5220

How can a timeout be cancelled upon a button press?

I'm creating a delete function to delete items from a system. Basically, an admin can click the delete button and have an item deleted. The problem comes in because some admins are trigger-happy or overly enthusiastic and so need to undo the delete.

The solution I propose involves an undo button that appears for 20s before the actual item is deleted, similar to the undo send email function in Gmail Labs.

function deleteStuff(type,id){
    var element = $("#"+type+id);
    element.slideUp(500,function(){element.html("<button class='btn undoDelete' >UNDO</button>");});
    var timeout = window.setTimeout(element.slideDown(),20000);
    if($(".undoDelete").click()){
         clearTimeout(timeout);
         //replace the item
    } else {
         //delete the item with ajax
    }
}

I know you can stop a timeout with stop() or clearTimeout(), but what I want to know is how to do it within a function. So far, I have considered checking every second for a click until 20s, but I'm unsure if that's the most efficient way to proceed.

Bear in mind that it is possible this function can be called multiple times simultaneously.

To be clear, I know this function isn't complete (e.g. will not fully restore), but that's irrelevant. The rest of the function just sets the context for the timeout part.

Upvotes: 1

Views: 615

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074295

There are a few problems there. You're calling setTimeout with a jQuery object (the return value of element.slideUp), but you should give it a function. You're also never restoring the button (if the user clicks undo), and there's nothing that actually stops the deletion from occurring.

I've taken a brief stab at it:

function deleteStuff(type,id){
    var element, timeout;

    element = $("#"+type+id);
    element.slideUp(500, function() {
        // Update button and slide down *immediately*
        element.html("<button class='btn undoDelete' >UNDO</button>")
                .one("click", function() {
                    // Stop the delete
                    clearTimeout(timeout);

                    // Restore the button
                    // ...exercise for the reader...
                },
                .slideDown();
    });
    timeout = window.setTimeout(function() {
        // Delete item with ajax
    }, 20000);
}

This works even if there are multiple overlapping calls to the function, provided the id values are different, because each call has its own timeout variable.


However, what if the user deletes something, then navigates away from the page? My expectation as a user would be that the thing would be deleted, but your deletion code would never run.

Instead, I recommend sending information to the server immediately, and if the user cancels send that to the server. The server would be responsible for waiting 20 seconds before actually deleting. Near-pseudocode:

function deleteStuff(type,id){
    var element, timeout;

    // Send "pending delete" to server
    // ...

    // Update button
    element = $("#"+type+id);
    element.slideUp(500, function() {
        element.html("<button class='btn undoDelete' >UNDO</button>")
                .one("click", function() {
                    // Cancel timer
                    clearTimeout(timeout);

                    // Send "cancel delete" to server
                    // ...

                    // Restore the button
                    // ...
                },
                .slideDown();
    });
    timeout = window.setTimeout(function() {
        // Remove or restore the button
        // ...
    }, 20000);
}

Upvotes: 2

Barmar
Barmar

Reputation: 780984

Javascript is single-threaded, you can't "wait" for an event. You have to do what you want in the event's handler.

function deleteStuff(type,id){
    var element = $("#"+type+id);
    timeout = window.setTimeout(function() { element.slideDown() },20000);
    element.slideUp(500,function(){element.html("<button class='btn undoDelete'  data-timeout='"+timeout+"'>UNDO</button>");});
}
$(document).on("click", ".undoDelete", function() {
    clearTimeout($(this).data('timeout'));
});

You need to use event delegation with .on() because this is binding to dynamically-created elements.

Upvotes: 2

Related Questions