Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
ianh opened this issue Oct 20, 2021 · 6 comments
Open

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

ianh opened this issue Oct 20, 2021 · 6 comments

Comments

@ianh
Copy link

ianh commented Oct 20, 2021

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 Why do textareas and inputs of type "text" return an empty array from getTargetRanges()? #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 Should execCommand dispatch beforeinput or not? editing#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
Copy link
Author

ianh commented Oct 20, 2021

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

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

ianh commented Oct 20, 2021

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

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

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

ianh commented Oct 21, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants