(010923) Added support for XHTML. Some fixes and other minor features were also added.
(020905) Added two new methods, getRange and surroundSelection.

By demand I've extracted and encapsulated the rich edit control from the webboard to make it easy to reuse in your own web applications. Go directly to the demo or read how the control was implemented.

Design Mode

Internet Explorer has a not so well known feature that allows a document to be edited like a normal WYSIWYG (What You See Is What You Get) editor. (IE5.5 has changed the mechanism to do this.) To allow a normal page to be edited the property designMode of the document object is set to "On". This property cannot be changed from within a page so another page needs to do this. The easiest way to achieve this is to create a frame and then set the property of the document of the frame.

frameWindow.document.designMode = "On";

Now we have our editor. That's it! To make it useful we can modify the selection inside the frame by manipulating text ranges. A text range is a special object that represents a range of text and elements inside a document. This has been available since IE4 and is a part of the proposed W3C DOM level 2 standard.

var range = frameWindow.document.selection.createRange();

execCommand

The ranges in IE have a special method called execCommand that allows changes to the current range. The method takes a command as the first argument and two optional arguments. The second is whether to use the UI of IE and the third is used for color and font names and similar. These commands work very similar as invoking the same command in a text editor. For example there is a a command called "bold" that toggles bold for the current selection.

range.execCommand("Bold");
range.execCommand("ForeColor", false, "red");

For a complete reference over the available commands see MSDN.

Component

To include the Rich Edit Component you create an iframe with the class name richEdit and include a javscript file in the usual way. For this component to work you also need to set the id attribute on the iframe. The components are initiated when the window loads so for IE4 you need to make sure that this does not override any other initializations.

<script type="text/javascript" src="richedit.js"></script>
...
<iframe id="edit" frameborder="0" class="richEdit" style="border: 1px solid #eeeeee;">
   <h1>My start content</h1>
</iframe>

Below is a component with the same declaration. Notice how the editor is initated with the content.

How the component was created...

At load all iframes in the current document are checked if they are supposed to be edit components. If an edit component is found then the current content of the iframe (the tag in this case) is copied to an expando property called value (one of the main reason for this is that the innerHTML is read only in an iframe). After this the designMode property is set to "On". Now we only need to write the start content of the frame and this is done with the old document.write() method.

function initRichEdit(el) {
   if (el.id) {   // needs an id to be accessible in the frames collection
      el.frameWindow = document.frames[el.id];
      if (el.value == null)
         el.value = el.innerHTML;
      
      var d = el.frameWindow.document;
      d.designMode = "On";
      d.open();
      d.write(el.value);
      d.close();
	  
      ...
   }
}

Adding methods...

I've defined some simple methods on the component so that it can easier be used in your web page. First I've added some methods to change the content at runtime. This can be done using the expando methods setHTML and setText or by manipulating the DOM tree that I've made a shortcut to.

document.all.edit.setHTML("<p>foo<b>bar</b></p>");
document.all.edit.setText("foo\nbar");
document.all.edit.frameWindow.document.body.style.color = "red";

There is also the equivalent getHTML and getText methods. Notice that in IE4 one can only get the HTML code for the body content and not the entire HTML tree so getHTML only returns the content of the body...

The other thing I've added are shortcuts to the execCommand so that one does not need to mess with ranges. I also added shortcuts to some common editor commands like setBold etc.

Method Description
setHTML(sHTMLText) Sets the content of the editor component. This string is parsed as HTML. Notice that the old content (and DOM tree) is replaced.
setText(sPlainText) Sets the content of the editor component. This string is not parsed so <b> will turn up as <b>. Notice that the content is replaced here as well.
getHTML() Returns the HTML code for the content of the body node inside the editor
getText() Returns the text content of the component. HTML formatting is removed in the result
execCommand(sCommand [, sValue]) Executes a command on the selection (or at the cursor position).
setBold() Makes current selection bold or the new written text bold depending on if anything is selected when this is executed.
setItalic() Makes current selection italic or the new written text italic depending on if anything is selected when this is executed.
setUnderline() Underlines the current selection or makes new written text underlined depending on if anything is selected when this is executed.
setBackgroundColor(sColor) Changes the background color of the current selection or the new written text.
setColor(sColor) Changes the color of the current selected text or the new written text.
getRange() Returns the range assosciated with the selection. Returns null if the editor hasn't got focus and no selection is made (no cursor present).
surroundSelection(sBefore, sAfter) Surrounds the selection with HTML code. sBefore is inserted before the current selected HTML code and sAfter after.

Remember that you have to use document.all to access the iframe element.

Demo

I've set together a small little demo with a few buttons. Try it or view the code for the demo.

Rich Edit Component
XHTML and more...
Demo

Author: Erik Arvidsson