w3c / input-events

Input Events
https://w3c.github.io/input-events/
Other
24 stars 16 forks source link

Request: a way to get and react to target ranges for all changes #124

Open ianh opened 2 years ago

ianh commented 2 years ago

Hello all,

The combination of the beforeinput event and the getTargetRanges() method let you implement many useful text editing operations, including validation and persistent state tracking on a character level. Unfortunately, there are certain situations where an editable element can change its content without the changed ranges being reported. This makes it difficult to preserve the invariants around validation and state tracking. In particular, the following actions cause a change without precise ranges being provided by getTargetRanges():

  1. Any changes to a <textarea> or <input type="text"> element. This was covered in #43, and while the justification makes sense, there may be another way to deliver this information. For example, the ranges could be provided with a null text node. The lack of target ranges can be worked around by using a contenteditable element, but that leaves the page author responsible for converting rich text to plain text.

  2. Changes produced by execCommand. The workaround for this is to manually track and react to any changes before calling execCommand. Knowing which changes will be caused by an execCommand requires understanding how the browser will react to each command, and every execCommand must be accounted for. There is some discussion of this in https://github.com/w3c/editing/issues/200.

  3. Changes resulting from an undo or redo. A potential workaround for this is to keep a parallel undo stack of changed ranges and assume that undo/redo will revert/reapply the changes in the same way they were made, but properly delimiting this undo stack requires understanding where the browser makes undo checkpoints.

Basically, I'd like a callback which is triggered before every change, no matter its source, allowing you to enforce text editing invariants in a single place. I should also mention that comparing the text before and after the operation is insufficient to track character-level persistent state, since in the change 'aaa' -> 'aa', any of the three 'a' characters could have been deleted.

ianh commented 2 years ago

It seems I misremembered how the Cocoa callback worked -- I made some edits to remove references to that. I think some way of tracking precise ranges for undo and redo would be sufficient for my purposes, in any case.

johanneswilm commented 2 years ago

Hey @ianh, interesting! May I ask what you are using execCommand for? When we created input events, we did not make it compatible with execCommand because we had come to the conclusion that it is a fundamentally broken API that cannot meaningfully be fixed. That's why most editors have stopped using almost all execCommand commands. As for undo/redo - it was our understanding that if you intervene at any time with your own the dom changes, the browser provided undo stack will become unusable. That's why editors usually provide their own undo stack. That being said, the gettarget ranges for undo/redo should deliver the correct information. Do they not do that?

ianh commented 2 years ago

Hi Johannes, thanks for the quick response!

I'd like to use the browser undo stack rather than reimplementing it myself -- that's why I'm using execCommand to insert text while preserving undo/redo. Is there a way to interact with system-level undo and redo events from JavaScript (e.g. shake-to-undo on iOS)?

The table at https://w3c.github.io/input-events/#overview indicates that events with a inputType of "historyUndo" or "historyRedo" will return an empty array from getTargetRanges(), and that's what I'm seeing, at least in Safari and Chrome.

johanneswilm commented 2 years ago

I'd like to use the browser undo stack rather than reimplementing it myself -- that's why I'm using execCommand to insert text while preserving undo/redo.

There have been a small number of special purpose editors that have done this and for whom this is working. In general those seem to be editors for which it is not important whether the content can be moved between browsers because they don't allow users to save content and they are editors that don't mind crashing when users paste arbitrary content, etc.. If this is the kind of editor you are working on, it may work for you.

If, however, you want to write an editor that creates the same html in all browsers, that doesn't crash, etc. then there is unfortunately a large history of people spending a lot of amount trying to create execcommand-based editors, only to realize months or years into the task that it is not really possible. That's where there is a big notice on the top of the execCommand spec on the state of that document.

We wrote input events with the second type of users in mind. The input events do not really cover all the execCommands.

The table at https://w3c.github.io/input-events/#overview indicates that events with a inputType of "historyUndo" or "historyRedo" will return an empty array from getTargetRanges(), and that's what I'm seeing, at least in Safari and Chrome.

You are right.

johanneswilm commented 2 years ago

@ianh FWIW - there seems to be a person making some type of request to revive execCommand about once every 6-18 months here. So it's not impossible that there are at some time will be a majority for going back to that. But at least for the past 7 years or so that is not where the taskforce/working group has been moving as it has instead moved toward making this more the job of JavaScript. So if you are just about to start writing a new editor, I would not probably not bet on browser makers shifting priorities in the near future. For most purposes there should be open source editing libraries out there that can take care of most of the same things execCommand used to be used for + you can hack them in case you need different behavior. If you actually have been successful in creating an execCommand-only based production ready editor, then congrats! I am sure the execCommand fan community would like to hear about it.

ianh commented 2 years ago

To be clear, I'm not trying to make an advanced rich text editor; just a plain text editor that can track changed ranges. It seemed like this API would be appropriate for that purpose, and it was close to working, but the lack of ranges for undo/redo events made it impossible. That's what led to my filing this issue -- I wanted to document what I wished beforeinput/getTargetRanges() would do before moving on to implementing this stuff manually.