简体   繁体   中英

How to make a undo/redo function

I want to add a undo/redo function in my script. I have looked around and see some suggestions, most of them recommended to use command pattern .

The function must work over one page - after a reload of the page the function must able to redo/undo the last things.

I don't know how command pattern works, I think about to create a object, to store the a name of the function, the old and the new value - but I'm not sure if this is a efficient way to do this or not.

Maybe somebody could give me a small example how the code for a undo/redo function should look.

There's 2 common ways of implementing undo/redo:

  • The Memento Pattern , where you capture the whole current state . It's easy to implement, but memory-inefficient since you need to store similar copies of the whole state.
  • The Command Pattern , where you capture commands/actions that affect the state (the current action and it's inverse action). Harder to implement since for for each undoable action in your application you must explicitly code it's inverse action, but it's far more memory-efficient since you only store the actions that affect the state.

The Memento Pattern

Before an action is applied, you take a snapshot of the current state and save it into an array. That snapshot is the Memento .

If the user wants to undo, you simply pop the last memento and apply it. The program returns to the state it was before the last action was applied.

This pattern is limited in terms of efficiency. Each memento is relatively large since it captures the whole current state.

But it's also the easiest to implement since you don't need to explicitly code all cases and their inverse actions that you need to in the Command Pattern (see below).

 const mementos = [] const input = document.querySelector('input') function saveMemento() { mementos.push(input.value) } function undo() { const lastMemento = mementos.pop() input.value = lastMemento ? lastMemento : input.value }
 <h4> Type some characters and hit Undo </h4> <input onkeydown="saveMemento()" value="Hello World"/> <button onclick="undo()">Undo</button>

The Command Pattern

Each action has a corresponding inverse action(command). For example, each time you add character in a textbox, you save the inverse function; which is to delete the character at that position.

If the user wants to undo, you apply the inverse action.

 const commands = [] const input = document.querySelector('input') function saveCommand(e) { commands.push({ // the action is also saved for implementing redo, which // is not implemented in this example. action: { type: 'add', key: e.key, index: input.selectionStart }, inverse: { type: 'remove', index: input.selectionStart } }) } function undo() { let value = input.value.split('') const lastCommand = commands.pop() if (!lastCommand) return switch (lastCommand.inverse.type) { case 'remove': value.splice(lastCommand.inverse.index, 1) break; } input.value = value.join('') }
 <h4> Type some characters and hit Undo </h4> <input onkeydown="saveCommand(event)" value="Hello World"/> <button onclick="undo()">Undo</button>

The snippets I've written only work when adding characters, then hitting undo to return to the state before the characters were added so they are an oversimplification on how you should implement this.

Nevertheless I think they demonstrate the core concepts of both patterns.

FWIW I'm using UndoManager in my projects as a stack for my commands.

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