execCommand() is now obsolete, what's the alternative?
Asked Answered
Q

10

272

I intended to use Document.execCommand() method along with contenteditable attribute to build my custom WYSIWYG editor. But when I checked the documentation for Document.execCommand(), I found that it's now obsolete. What's the modern (or extant) alternative for it?

Quenna answered 7/3, 2020 at 19:11 Comment(9)
Is this helpful? #2887601Ethelda
Check this out: MDN: Making content editable - although it also seems to refer back to document.execCommand()...Lashio
Also see Is there something better than document.execCommand?Lashio
There's also the fact that contenteditable is a pretty unpredictable beast, so you might want to consider not even using contenteditable at all, and instead swapping element out for an editor with that element's content preloaded in.Suppressive
@Mike'Pomax'Kamermans you can read: ckeditor blog, contenteditable is not good, but it is the only solution.Lamellate
hardly, with vue and react etc. these days it's trivial to write something that takes the element that a user clicks on, replaces it with a code textarea (plain textarea, Draftail, CodeMirror, etc) so that it can be edited, and then back-replaces the original element with updated content on blur. Heck you don't even need a framework for it, element swapping code is easy enough to write in vanilla JS and will offer far more control than contenteditable could ever offer.Suppressive
@Mike'Pomax'Kamermans Thanks for your suggesting your approach. I'm going to try to implement it. Just to note, i was having a squiz at Draftail, seems on inspection with dev tools it does actually use contenteditable.Brandebrandea
@Mike'Pomax'Kamermans Can you please explain what you've suggested a bit more or link to an article or example code?Ambages
What's there to explain? If you want to write an editor in Vue or React, google still works fine and there are a lot of writeups that explain how to do that. Howver, linking to any specific one won't help future users when that link inevitably becomes a 404. (Which is why asking for tutorials, etc is off topic: what worked this year might not even work the next. or might now be a dead technology)Suppressive
P
110

Year 2022–2024 answer: The execCommand() is officially obsolete/deprecated but there's no alternative. So if you must have rich text support, you have to keep using execCommand() and figure out yourself what actually works with browsers that you want to support.

Note that real world user agents (browsers such as Chrome, Firefox and Safari) cannot drop support for execCommand() because so many services require support for it. The sad state of things is that HTML5 cannot specify any common ground. This is because browser vendors do not agree how execCommand() should work. So nothing can be specified for HTML5, which has the overall objective to specify all stuff – and only stuff – that's needed for full interoperability for any new browser to enter market. However, in my opinion HTML5 fails here because execCommand() compatibility is actually required for any new browser to enter market. So it would make sense to standardize at least one of the browser specific implementations as the official one.

All the current standardization efforts (Input Events 2, Clipboard API) fail to even try to cover the features that execCommand() actually does (for example, undo/redo, actually changing content within the selection range).

The hardest part to solve is the caret movement, selecting text, different IME (Input Method Editor) behavior and handling native clipboard. These all interact with each other in various ways depending on browser and operating system. For example, the behavior that iOS Safari has on iPad while writing Traditional Chinese is totally different from Firefox on Windows with Thai IME. Which is again totally different from Chrome running on Chromebook while writing Finnish or entering emoji characters with multiple UNICODE point encoding. And as none of the available JS APIs disclose all the details, you MUST use contenteditable and possibly execCommand(), too, for most browsers to get correct input from the IME.

Also note that e.g. Android GBoard needs to also know about the surrounding text within the same editable content because it uses AI logic to correctly figure out which word suggestions to give to the user while inputting text, so you cannot fake this with a single empty invisible textarea under the caret because that would be missing the required context. That hasn't prevented people from trying to do exactly that but it fails in practice in various ways.

This situation has already been going for over five years so don't expect any rapid changes either.

Pluralize answered 24/1, 2022 at 9:27 Comment(8)
It seems that the current "plan" is that applications that need rich text editor should listen for beforeinput event, collect the to-be-done modification information from the event and prevent browser default behavior and instead execute custom content modification (custom rich text editor logic) in JS code only and then use HTML+CSS to render the results. That requires implementing everything in JS including undo/redo and all actions so there's no need for any browser support.Pluralize
For background information: medium.com/content-uneditable/…Pluralize
And before you go willy-nilly with beforeinput, read this: medium.com/content-uneditable/…Pluralize
execCommand() stopped working on Chrome v99.0.4844.51Edward
@Edward can you provide additional details? According to caniuse.com/?search=execcommand it should be still supported.Pluralize
I tested it today and it is working on the latest browser version of Chrome and Firefox but not on SafariWearing
Note that even if you listen for beforeinput you cannot handle copy-paste without browser native support. And the paste is especially problematic because the user might have e.g. password on the clipboard and obviously allowing any open web page to read that from the clipboard is not okay. As such, web sites cannot implement paste that's triggered by javascript in response to clicking a visible button on the DOM!Pluralize
Still true for 2023Cavender
B
89

I created the Rich Editor for my platform's XML (HTML5 + XHTML) editing purposes. I would not say that document.execCommand() is completely dead because some parts of it still work fine. Unfortunately the primary issue for me was that browsers use a lot of different code to generate those styles which are not recognized by screen readers used by those who are blind or nearly so.

Additionally the most expensive time bug I ever had to conquer was a Gecko/Presto bug where the visual and technical selections (why they aren't the same thing, don't ask me) would result in part of the DOM being changed that the user did not intend and this would come down to the fact that the pixel count per character is low so if the Rich Editor did not honor visual selections a user would very quickly walk away. That required four months to conquer and there are other bugs too.

Ultimately it's a harsh though achievable endeavor though if you intend to build an HTML/XML editor like I did you should plan for at least six months if you plan to not only do it properly though test it to the point of hating cake to then only have someone come along and point out yet another bug.

Your primary focus JavaScript wise should be on the following:

In place of inconsistent code generated by using execCommand() from different browsers (often setting inline styling which would complicate your site's CSS if not outright negating it) you should stick to using the following elements which you not only can have control over though are compatible with screen readers:

  • em for emphasis (or "italics", <i> is deprecated).
  • strong for strongly read text (or "bold", <b> is deprecated).
  • u for underline (be sure your anchors are styled to differentiate from u elements; u might be considered "deprecated" though I will reverse this when I fix the standards in the next ten years or so, use it appropriately).
  • sub for sub-line text that appears vertically lower than normal text.
  • sup for supper-line text that appears vertically higher than normal text.
  • Do not use the <span> element to specifically add these styles as screen readers will not understand or reveal buggy behavior; it is still a valid generic inline element when used appropriately.

I actually have been intending to revise my Rich Editor (it's been getting patched though not yet properly rewritten) though you're welcome to look at the source code when it loads on a blog page in the site linked in my profile. The original project took me 11 months though with my experience now I think it would take me about three to four. If you're serious I highly recommend staying the hell away from frameworks and libraries. "But ... but, they make life easier!" ... until you want to use a new version and have to rewrite the entire project. Use pure JavaScript the first time and negate pointless maintenance. Good luck!


2021-09-24: I ended up resuming work on the Rich Editor II from roughly a year ago and managed to convert the code that changed styles from 100,515 characters to ~6,000 and reduce the file request (effective bandwidth after compression) by a full third. Here are the key parts to that success:

  1. The anchorNode and focusNode can switch depending on if you select left-to-right or right-to-left. Because I couldn't find any justification of why that would matter (for my platform) I made the an object (for anchorNode) on the left and the fn object (for focusNode) always on the right side.

  2. I resolved the Gecko/Presto issue using ~1,700 characters; you can find it on the site (visit a page that has a Rich form) first.

  3. To resolve the issue of selecting through numerous interchanges by <s>, <sub>, <sup>, <u> etc (you must test both simple and wildly convoluted examples) I ended up using window.getSelection().getRangeAt(0).commonAncestorContainer with cloneNode and then before processing that stripped what wasn't included in the selection. Then I simply used window.getSelection().deleteFromDocument(); to remove the selection and replace it with the new style element via document.createElement that I could easily appendChild the selection to and insert it at window.getSelection().getRangeAt(0).insertNode(id_('editor_rich_text').firstChild);.

Gecko browsers such as Waterfox, Pale Moon and the now utterly destroyed Firefox allow you to select multiple instances of text. To do so simply hold the Control key to create extra selections. Since it doesn't really help in any meaningful way (and this stuff is already convoluted enough as it is) I didn't go out of my way to support it at this time.

I'll be updating my platform either today or this weekend (as of this post edit) to reflect the new changes. I am keeping a lot of the older code due to numerous issues with Gecko browsers. I have expanded upon the functionality and resolved numerous bugs and did not have to resort to any hacks and as usual no garage (frameworks or libraries).


2021-09-26: For those interested in redo/undo functionality you'll have to resort to basically keeping text versions of the part of the DOM that you're editing. Sure, there could be other ways to implement it though those would be wildly convoluted. You basically just make a copy of the editor parent element using .cloneNode and then while it's in memory use something along the lines of while (e.firstChild) {xml += new XMLSerializer().serializeToString(e.firstChild);}. Since you'll be storing it as text it won't have the huge memory implications that the DOM has. You will literally be replacing the entire DOM in the editor as well as tracking every iteration of a change so it'll still be a hefty project. For my platform it's not necessary right now though I wanted to cover this as some people have mentioned it in the comments.

Burch answered 8/6, 2020 at 16:9 Comment(7)
So basically I should get the current selection and determine whether I need to surround it with e.g. a span-tag (to e.g. set font-weight or text-decoration) or e.g. split the span (by determining any intersections and possibly closing or even removing those as well), right? Another solution might be to wrap each letter in a span-tag, but I suspect there'd be a terrible performance overhead coming along with it - especially on larger texts...Karinekariotta
@Karinekariotta I've updated my answer though no, do not use span elements to implement styles. If you need to strongly word a part of a sentence you should use <strong> in example so screen readers understand and strongly read back for the blind and hard-of-sight. The <span> element still has it's place when used appropriately and there are other types of elements. As far as performance the real issue is dealing with highly nested "styles" (adding or removing an u from the middle or side of <em><strong><u>example</u></strong></em>) or dealing with double-nesting.Burch
Ah - thank you! So I should favor the most precise text such as u, strong etc. over span? I mean .. screen-readers such as JAWS do interpret aria-attributes from what I remember when I was developing applications, that also had to be readable by such programs. And yeah .. the nesting is where the real trouble is.. so I suppose I shouldn't use contentEditable either?Karinekariotta
@Karinekariotta My web platform still uses the original Rich Editor (you can preview on a blog thread page via comments, it does not load unless needed, on-demand JavaScript). The contentEditable attribute is valid. As far as handling complex nesting there are two things to do: 1. set a policy for the maximum complexity and when exceeded instruct users to use XML editor and 2. determine that maximum threshold to make your project release realistic. If you decide to go ape-nuts on complexity mimic the behavior of LibreOffice; I consider it currently the "standard" for handling such complexity.Burch
I might be wrong but execComand gives you undo/redo for free. What would be a good alternative if we go without execCommand?Gliwice
@ChuanqiSun My current best thought is to track changes with a data-edit or similar non-invasive HTML/XML attribute and assign the either a value based on it's relative position among what has been edited and/or what the element previous was. Undo/Redo is an ordered execution so you'd simply iterate forward or backwards on a JavaScript/CSS attribute selector. Since I'm slowly rewriting my Rich Editor on the side I haven't gotten to that point. However I am sure you'd likely be looking at ~800-1,600 bytes of JavaScript if executed competently.Burch
where is the code can you please provide github link the site is so slowJug
D
53

You can use the following:

navigator.clipboard.writeText('text to be copied');

which does the exact same thing as document.execCommand("copy");

Decanter answered 15/3, 2022 at 16:6 Comment(7)
The problem is when you need to write something more than a text, for example, an HTML. execCommand gives you the possibility to attach an event listener to the copy method and this listener gets the ClipboardEvent object which is much more useful.Boloney
@AdrianStanisławski Yeah it suck that they did not replace the execCommand completelyDecanter
The other problem is that the .clipboard needs https. So I've written an exec command to copy, which works on non-https areas. The execCommand is the work-around for the non-https failure. But... what is the work-around for execCommand lol...Licit
@Licit Guess we will never know, there are some solutions but they are rather weird or dumbDecanter
It will only work on Chromium.Figureground
Unfortunately, this is not accurate. They don't do exactly same thing, as I ask in my question.Notus
To anyone who has the same problem as @KJRoelke you can follow this answer to copy HTML not just text.Decanter
T
26

Looks like the alternative will be Input Events Level 2.

While it is very tempting to create a self-made WYSIWYG editor, I personally will stick with execCommand until the new standard arrives. Because we all will just program a whole editor by ourselves, instead of reusing present working solutions.

Transversal answered 21/1, 2021 at 21:4 Comment(3)
Note that Input Events 2 seems to only provide events for custom JS code when user interacts with contenteditable. As far as I can see, it cannot be used to modify the state of the edited content. The Clipboard API seems to have similar problems where it's more info about clipboard than the actual features that can modify the editable content.Pluralize
@MikkoRantalainen Completely agree. It's now 2022 and execCommand is still regarded as deprecated and there is still plan to replace it that provides the same functionality. Even what has been proposed (which doesn't seem to be as useful) doesn't seem to be going anywhere after 6+ years. Meanwhile, all major browsers (at least 15 according to CanIUse.com) continue to support execCommand.Bates
@IainCollins Browser vendors don't really a have a choice because they just cannot go from current "poor rich text editing" to "no rich text editing" while waiting for a sensible spec. I'm disappointed that they just didn't standardize the sensible parts of execCommand() to allow a clean way forward.Pluralize
B
10

The alternative to document.execCommand() is Clipboard API, via navigator.clipboard. Per MDN Web Docs (https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API):

This API is designed to supersede accessing the clipboard using document.execCommand().

EDIT: As mentioned in the comment as well as the quoted text above, this only fulfills a subset of the requirements (clipboard access).

Brawny answered 9/2, 2021 at 6:25 Comment(1)
How do you bold text, undo/redo changes etc. with the clipboard API? This only fulfills a very limited subset of what execCommand does.Coventry
S
8

The conclusion here is that even though the w3C has a warning concerning execCommand(), it is still working and no standard alternative for it yet even now in 2022 unless one results in using javascript edition libraries.

Sandi answered 17/2, 2022 at 7:32 Comment(1)
Can you name a few of such libraries?Ambages
G
5

The reference documentation says it as warning.

It is predicted that in the future both specs will be replaced by Content Editable and Input Events.

And as every prediction, just wait to be sure.

Grigsby answered 20/2, 2021 at 21:23 Comment(2)
The contentEditable spec has been under work between 2015-2020 and the final result is zero lines of spec: w3c.github.io/contentEditable . I wouldn't hold my breath waiting for this spec.Pluralize
Adding to this to say it is now 2022 and we still don't seem any closer to replacing execCommand and nothing that's been vaguely proposed even tries to address the same problems.Bates
F
0

Depends what you want to achieve

As per MDN.

The execCommand method implements multiple different commands. Some of them provide access to the clipboard, while others are for editing form inputs, contenteditable elements or entire documents (when switched to design mode).

To access the clipboard, the newer Clipboard API is recommended over execCommand(). However, there is no replacement for the editing commands: unlike direct DOM manipulation, modifications performed by execCommand() preserve the undo buffer (edit history).

Reference Here

Froebel answered 5/4 at 13:29 Comment(0)
L
-1

It is possible to make a WYSIWYG editor using

window.getSelection()

but it is not a problem to keep on using execCommand for now.

Latonialatoniah answered 18/9, 2022 at 7:57 Comment(0)
N
-1

There is nothing wrong with document.execCommand. The problem is that HTML 4 standard that is used under the hood of this function is not widely supported any more. For example, FontSize command uses <font> tag, which is deprecated. But there are also good news. First one is that document.execCommand will never be removed from javascript and second one - you can use insertHTML command which can insert any HTML you want.

function FontSize40px(){
   var spanString = `
   <span
      style="font-size: 40px;"
   >${ document.getSelection().toString()}
  </span>
   `
   
   document.execCommand('insertHTML', false, spanString);
}

It's just a stupid example :))

Nonparticipating answered 25/3, 2023 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.