25

When implementing a web-based rich-text editor, I read that document.execCommand is useful for performing operations on an HTML document (like making a selection bold). However, I need something a bit better. Specifically, I need to know exactly what text is added or removed from the innerHTML, and in what location (as an offset into the entire document's HTML representation).

I considered using the built in document.execCommand along side DOM4's mutation observer, but execCommand doesn't seem up to the task:

  • I don't see a way to "un-bold" a selection
  • the generated html seems to vary from browser to browser. (I'd like < span > tags not < b >, but consistency is more important)
  • and there's no information on what it does to handle redundantly nested/adjacent < span > tags.

Also, using mutation observer seems a bit overkill based on my needs.

My motivation: I'm trying to transmit document changes to the server periodically without re-transmitting the whole document. I'm sending the data as a collection of insertions and deletions upon the HTML representation. If someone knows a way to get this functionality out of, say, CKEditor (so I don't have to start from scratch), then I would love you forever.

Note: Performing a text diff is not an option, due to its poor performance on very large documents.

Otherwise, I'm not exactly afraid of trying to write something that does this. The methods provided by the DOM's range object would handle a lot of the heavy lifting. I'd also appreciate advice regarding this possibility.

Brent
  • 4,153
  • 4
  • 30
  • 63
  • 3
    execCommand is one of those things that is never going to work the same across all the browsers (purely due to the way it came about), same with the selection handling. So if this project is for an internal system I'd recommend choosing a target browser and building for that... it'll make your task a lot simpler. However if that isn't an option then you're going to have fun.. ;) can you possibly lock edits down to a per line modification and then run a text diff on that? or maybe per paragraph? that's the way I would go for if it were me... – Pebbl Sep 03 '12 at 17:12
  • 1
    It's for a publicly accessible website. Reducing the scope of the diff is an interesting idea, but I'm not sure about it. I could determine the nearest common ancestor of the selection ranges' boundaries, and only check for modifications within it... That's got some caveats though. It doesn't handle changes made by undo/redo, and it's possible to have a document that contains no tags - just a wall of plain text, which wouldn't benefit from the optimization. BTW, I always have fun. :) – Brent Sep 03 '12 at 17:33
  • +1 for always having fun... ah ok, I was thinking more along the lines of the user selects either a portion or a region of a read only document. That region then become editable.. once they are happy they click save and then the difference is calculated and written to the server. That way there shouldn't be any problem with undo/redo if you treat each region as if it were 'a seperate doc' almost. It does depend largely on the ability of those that will be using the system however (and your UI design skills). It might be too clunky for users that expect 'Word style' editing. – Pebbl Sep 03 '12 at 17:42
  • Gotcha. This may sound... strange (even ill advised, but trust me). The interface paradigm I'm working on doesn't even really have a concept of "save". I'd share more, but it's a secret, lol. (and SO has no PM system?) – Brent Sep 03 '12 at 18:01
  • You can check my answer here http://stackoverflow.com/a/12166267/1464696 - I described how we do this in CKEditor, but I'm afraid that you won't be able to take some part of the impl. AFAIK it's against the license if you haven't bought it and I guess it just isn't doable at all - you would have to take 75% of CKEditor's core, for this "simple" job :P. – Reinmar Sep 04 '12 at 13:06
  • Thanks for this info. I've been slowly putting together an idea of how to do this, and for my needs I think it'll be best if I just start from scratch. Considering that I also need strict control of the DOM in a cross-browser-consistent way, I think I'll be intercepting and manually doing everything except cut and paste. For those, I'll be using DOM mutation observers. I'll transparently add nested spans (binary tree) to the displayed DOM, so that copy-paste mutations can be diffed quickly. – Brent Sep 04 '12 at 17:24
  • Possible duplicate of [Alternatives to execCommand](https://stackoverflow.com/questions/3686673/alternatives-to-execcommand) – user Nov 30 '17 at 20:42

3 Answers3

13

There is one alternative to using execCommand - implementing the whole interaction of an editor including blinking of a cursor. And it has been done. Google does it in docs, but there's something free and open-source too. Cloud9 IDE http://c9.io has an implementation. AFAIK, github uses that editor for some time now. And you surely can do anything under that, because there's no native code involved - like in execCommand

The repo is here: https://github.com/ajaxorg/cloud9 (it contains the whole IDE, you will need to find the code for the editor. )

Also - dom mutation events are deprecated. If you can drop support for old browsers, try mutation observer. If not - try to avoid detecting DOM changes at all and intercept changes in the editor's implementation. It might be the way to go for the new browsers too.

naugtur
  • 16,827
  • 5
  • 70
  • 113
  • Thank you. I'll look into this and see if it will suit my needs. Also, I meant mutation observer, honestly! Easy to misuse the jargon I guess. ^^ – Brent Sep 18 '12 at 00:39
  • Finally got some time to look this over. ACE, the editor for cloud9, isn't equipped for rich text editing. It's a great idea, but I'd have to write a renderer. I figure the best thing is to use the browser for the renderer, and "talk" to it via the DOM. – Brent Sep 22 '12 at 23:38
  • It sure is easier, but takes away some control. It'd be nice if you posted another answer here when you succeed. – naugtur Sep 23 '12 at 08:58
  • Well, I've got half of the DOM Level 2 Core implemented. I've found a simple HTML parser (don't need anything too advanced, because the HTML is generated anyway). Implement the parts DOM manipulation stuff that I'll need, and then make an editor that has hooks for insertions and deletions on the source HTML. I'll probably make it LGPL or something. – Brent Sep 23 '12 at 17:34
  • 1
    Might as well update. I never did get the problem solved. In fact, it killed my whole project. Disappointing that something like this should be so hard. – Brent Nov 05 '13 at 19:49
  • That's the whole business-model of CKEditor and many others. BTW. If you enjoy fiddling with code editors using JS, take a look at writing plugins to `brackets` editor. Looks pleasant – naugtur Nov 06 '13 at 10:15
8

There is Trix rich text editor, from their description it looks like avoiding inconsistent execCommand is the whole point of the project.

user
  • 23,260
  • 9
  • 113
  • 101
  • Trix looks really awesome - I integrated it into my project in 15 mins! As of today, it does not support inline images or image resizing. Unfortunately I need these two features. – Martin Meeser Jan 21 '21 at 21:09
4

It seems the new standard will be Input Events Level 2. To me it looks like it will be a revised improved version of execCommand.

Martin Meeser
  • 2,784
  • 2
  • 28
  • 41