Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Easy undo-redo in JavaScript.
function UndoItem (perform, data) {
this.perform = perform;
this.data = data;
}
/**
* UndoStack:
* Easy undo-redo in JavaScript.
**/
function UndoStack(self) {
this.stack = [];
this.current = -1;
this.self = self;
}
/**
* UndoStack#push (action, data);
* perform(true, data) -> Function which performs redo based on previous state
* perform(false, data) -> Function which performs undo based on current state
* data -> Argument passed to undo/redo functions
**/
UndoStack.prototype.push = function (perform, data) {
this.current++;
// We need to invalidate all undo items after this new one
// or people are going to be very confused.
this.stack.splice(this.current);
this.stack.push(new UndoItem(perform, data));
};
UndoStack.prototype.undo = function () {
var item;
if (this.current >= 0) {
item = this.stack[this.current];
item.perform.call(this.self, false, item.data);
this.current--;
} else {
throw new Error("Already at oldest change");
}
};
UndoStack.prototype.redo = function () {
var item;
item = this.stack[this.current + 1];
if (item) {
item.perform.call(this.self, true, item.data);
this.current++;
} else {
throw new Error("Already at newest change");
}
};
UndoStack.prototype.invalidateAll = function () {
this.stack = [];
this.current = -1;
};
@dsamarin

This comment has been minimized.

Copy link
Owner Author

@dsamarin dsamarin commented Jul 5, 2012

Usage

var undostack = new UndoStack(null);

function pushAction (perform, data) {
    var return_value;

    // We want the redo call before the push in case it throws.
    return_value = perform.call(this, true, data);
    undostack.push(perform, data);

    return return_value;
};

function changeBackground (color_new) {
    var style = document.body.style;
    var color_old = style.backgroundColor;
    pushAction (
        function (redo, data) {
            style.backgroundColor = redo ? data[1] : data[0];
        },
        [color_old, color_new]
    );
);

// Now call undostack.undo() or undostack.redo()
// as necessary to rollback calls to changeBackground().
@FlyingTopHat

This comment has been minimized.

Copy link

@FlyingTopHat FlyingTopHat commented May 12, 2014

If it is of any interest I've used a similar technique to show how to create undo functionality when painting the HTML canvas.

@Sandeepidolize

This comment has been minimized.

Copy link

@Sandeepidolize Sandeepidolize commented Dec 30, 2016

sds

@vasiliyfk

This comment has been minimized.

Copy link

@vasiliyfk vasiliyfk commented Jun 7, 2017

Very nice. I will use this example to help me redesign my current out-dated undo-redo.

@jfan04

This comment has been minimized.

Copy link

@jfan04 jfan04 commented May 3, 2019

What's the use of this.self in this case?

@mohamad-hejazi07

This comment has been minimized.

Copy link

@mohamad-hejazi07 mohamad-hejazi07 commented Aug 14, 2019

can you make an example how to use it with textarea and two buttons to undo and redo the text on the textarea ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment