w3c / w3c/editing

Add ability to control native undo/redo stack

Open
#509 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

TPAC2025
Dominant language
HTML
Stars
202
Forks
43
PR merge metrics
No merged PRs in 30d

Description

The problem: inputType historyUndo/historyRedo unusable for editors that maintain their own document state

I was excited to learn that I'm now able to intercept historyUndo and historyRedo inputTypes at beforeinput stage.

But unfortunately I can't utilize them because they are only fired when there are items on the browser-native undo/redo stacks. 😢

Here's a scenario from Svedit:

  • user enters a character (I handle this at beforeinput and add a character to my internal model, which triggers a rerender and updates them DOM)
  • now i go to Edit > Undo in the browser
  • nothing happens because the browser didn't get a history event, as there's nothing on the browser-native undo stack (because I handled everything myself)

There's no way for me to "fake native history entries" as I would always need to let some input slip through and mess with the DOM in order to be recognized as a change that gets added to the native undo history.

I see two directions to solve the problem:

  • historyUndo/historyRedo could be triggered always (even though the browser undo stack is empty, so they are more like an "historyUndoRequested" input). Then I get the chance to handle this in my model and run undo (because there I know there's a change that can be undone).
    • Only when I don't handle historyUndo/historyRedo (+ call preventDefault) the browser's native undo would run (e.g. when I'm in a text area outside of the contenteditable)
  • Programmatic access to create "custom history entries" in the browser programmatically. This means that basically each time I apply a transaction in my model, i can call an API ala document.addHistoryEntry(), without a payload, just to keep the native history stack in sync with my app-specific one. Now I could handle the native historyUndo/historyRedo also for my custom history entries. If not handled, they'd result in a no-op. However I may also sometimes call undo/redo programmatically (undo/redo buttons in a toolbar), which would then just work with document.executeCommand('undo|redo') I guess?

Without having had a deep reflection on this, my gut says the first option is enough (and more straightforward). Though the second option would allow a more deep integration for custom apps with an undo/redo stack (those app may not even use contenteditable).

A lot of webtools suffer from that issue. E.g. when you edit a file in figma.com, and you use the native "Edit > Undo" nothing happens. You have to use the keybindings, or the menu Figma renders in the browser.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the beforeinput behavior for historyUndo/historyRedo described in the issue, then compare the proposed always-fire and custom-history-entry directions. A complete contribution would define one API direction, its interaction with preventDefault and native undo/redo, and the expected behavior for contenteditable and application-managed document state.

Written by the indexing model from the issue text.

Assessment

Tech stack
html
Domain
frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.