Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@dsamarin
Created July 5, 2012 00:37
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save dsamarin/3050311 to your computer and use it in GitHub Desktop.
Save dsamarin/3050311 to your computer and use it in GitHub Desktop.
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
Copy link
Author

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
Copy link

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
Copy link

sds

@vasiliyfk
Copy link

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

@jfan04
Copy link

jfan04 commented May 3, 2019

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

@mohamad-hejazi07
Copy link

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

Copy link

ghost commented Aug 9, 2022

Hi,
I hope you are well

Can this code be used with p5.js?

Thank you

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