Structured Editing: Cross-browser javascript text editor
Summary
I've written a proof-of-concept rich text editor using cross-browser javascript which just modifies the
DOM. It doesn't need any special editing support from the browser, just a decent implementation of javascript
and working DOM manipulation.
Because I'm not a javascript expert, and this is my first 'advanced' use of it, I'm hoping that some javascript gurus will know how to finish it.
All the code is licensed under the BSD license. My email address is ben@fluffy.co.uk, but do avoid HTML email if you want
to get past my spam filters.
Background
I'm writing a web app which really needs some nice rich text editing, but in a structured way. A load of random tags which apply visual style isn't really good enough.
I looked at contentEditable and designMode, but both seemed rather nasty with annoying APIs
and horrendous markup to tidy. It sounded like it would be less effort to write my own text editor.
That gave me an idea.
It turns out it's possible to write a text editor with nothing more than a spot of javascript making
extensive modifcations to the DOM tree. Here's my proof-of-concept editor. Click in it to edit.
An example editor
It doesn't use contentEditable or designMode, just a bit of javascript which modifies the web page. But because it's just a proof of concept, it has some limitations. The most annoying is that you can only the left and right arrows for now.
Type and the text is inserted, and the box will expand. You can even paste text in.
When inserting new paragraphs, HTML elements like bold and italic are handled correctly, even when nested, but the UI can only set paragraph level styles for now.
This seems to work well enough to show what's possible. And it works with the same
code in Firefox, Safari, Opera and Internet Explorer (at least). IE does have the odd quirk which needs
sorting out, but it's very close.
Show HTML output from the editor above.
Currently the editor implements:
- Limited cursor movement: left and right arrow only
- Minimal editing: Insert characters, new paragraphs with enter, delete text and merge paragraphs with backspace.
- Pasting text works.
- Apply paragraph styles.
- Works with a top level DIV containing P, H1, H2, H3, H4 only. Those may contain markup like B and I, but not SPAN.
but it does support all the CSS styles you can throw at it. Note that clicks can sometimes be a bit hit and miss, especially with Safari.
To use the editor, include the javascript and css files. It's based on Prototype, so you'll need that too. Then, to make a DIV editable, just give it a class="editable" attribute.
To retrieve the HTML from the editor, use StructuredEditing.getHTMLFromEditor('editor'); where 'editor' is the id
attribute of the parent DIV.
Editor requirements
When it's finished it should provide...
- Structured editing, with proper style based markup
- Clean HTML output
- Ability to mark some bits within the editor as non-editable. These can have different display and HTML output for ease of coding.
- No funny text area boxes; it should expand and contract neatly.
- You should be able to overlay stuff on top. I have a feeling that using an IFRAME would result in all sorts of
interesting browser bugs.
- Multiple editable bits on one page.
- Keyboard shortcuts to apply styles.
- Work in any browser released this century. (I can dream.)
I'd image it'd be a nightmare to do that with contentEditable or designMode, needing two completely different bits of complicated code, and a set of random workarounds for each browser.
I was expecting browser hell in doing this, but it was surprisingly pleasant. The odd bit of
fun and games, but nothing too much. There's no testing of functionality and checking for different
features, just the odd 'unnecessary' bit of code to make Safari behave.
Speaking of multiple editors on a page, here's another to play with.
Another editor
This is a second editor. Go on, edit the text!
How it works
It's not too scary. To find the mouse click and to insert text nicely, all the text is
split into lots of spans. It uses a sort of binary search algorithm to minimise the number used,
so it's not as bad as it sounds. Later I'll add code to go through things in the background
and remove unnecessary spans.
A hidden TEXTAREA to the left of the page border is used to collect the text input. This simplifies
working out which character should be inserted, and gives us pasting support for free.
To avoid the page scrolling randomly, it's is moved to same vertical position as text being edited.
There are some nasty little edge cases which have to be handled. Having the ability to have the
caret positioned right at the left of the paragraph is surprisingly involved. And because top
level elements aren't displayed without content, you've got to put something in them and then
pretend it isn't there.
But that's basically it.
Questions
There's quite a bit to be done. And already, I have some questions for the experts.
- Safari doesn't give keyup event for pasting. Is there any way to get this?
- For IE compatibility, is it OK to use the constant '3' instead of it's proper name, Node.TEXT_NODE?
- Safari doesn't always send mouse click events. Any way around that?
- Could a zero-width inline element be used for the caret somehow?
- Is it necessary to delete DOM objects which are removed? (Or: am I leaking memory here?)
Important things needing doing
There's quite a way to go.
- Selection handling, and application of styles to arbitrary bits of text. Using the browser's
selection facility isn't going to work; not only do some not provide enough information about where
it is, but manipulating the DOM under it clears the selection.
- Undo system; use standard method of action objects with do and undo
- With a few tweeks it should also be able to be used for more inline editing. It assumes it's
working within DIVs a little too much.
- Be a bit clever about the edited hierarchy, so editing lists is possible. At the moment the code
assumes a single level of editable nodes.
Bugs so far
- Needs to handle spaces better -- their non-visibility doesn't help matters!
- Quirks in Internet Explorer -- when moving objects around the DOM, they seem to change into new ones sometime.
- Some fonts (eg Lucida Grande) change spacing when you add spans into the mix.
- Sometimes in Safari, stuff in other documents can stop the textarea getting focus. This is perhaps
a bigger bug in the browser than anything I can work around. (Triggered by some stuff at the
TFL journey planner.)
Feedback please!
Please send me feedback about this editor.
- Is the concept any good?
- Is it worth continuing with this, or is the approach all wrong?
- What browsers did you try?
- How well did it work?
- How responsive was it?
- How dodgy is my code?
But of course, the most important thing is whether or not you'd be interested in helping me out with it!
Send me email at ben@fluffy.co.uk, but do avoid HTML email if you want
to get past my spam filters.
Related projects
None of these support "all" browsers, and all use the built in editing features of those browser they do support.
widgEditor A nice try, but it doesn't work
on all browsers, particularly falling foul of some Safari bugs. Oh well.
Interesting how much code there is to clean up mess, which can't be future proof. This was the project which
suggested there could be a better way.
FCKeditor Woo! Features!
Dojo Editor Good project goals, apart from the
bit about not being a structured editor.