How to Create a WYSIWYG Rich Text Editor in JavaScript. Pt. 2 | WebReference

How to Create a WYSIWYG Rich Text Editor in JavaScript. Pt. 2

current pageTo page 2
[next]

How to Create a WYSIWYG Rich Text Editor in JavaScript. Pt. 2

Last week, we looked at the basic concepts for creating a Rich Text Editor in JavaScript. This week, we'll continue that exploration by working with Keyboard Input and the RichEdit Control.

Keyboard input is handled by two functions; onKeyPress() and onKeyDown(). In Internet Explorer, these functions are attached to the <div> as the key-press and key-down event handlers while in Netscape, only the key-press event is handled (on the text box) and the onKeyDown() function is called explicitly from there.

The onKeyPress() function takes each character entered by the user and inserts it into the document.

 

RichEdit.prototype.onKeyPress = function(evt)

{

  // handle keyboard events

 

  // fetch event object

  var evt = e ? e : window.event;

 

  // in Netscape, handle movement keys here

  if ( bIsNetscape && this.onKeyDown(evt) ) return;

 

  var keyCode = evt.keyCode;

 

  if ( keyCode == 13 ) // return

  {

     // insert a line break before the cursor

    this.insertNode(document.createElement('br'));

  }

  else

  {

     // insert the character before the cursor

    this.insertText(String.fromCharCode(bIsNetscape ? evt.which : evt.keyCode))

  }

  // keep the cursor in view.

  this.oCursor.scrollIntoView();

}


The onKeyDown() function is used to handle the control key input like arrows, backspace, delete and so on. For the sake of brevity, some of the code has been removed from this function. Readers interested in more details can check the source code in the demo. Since this function may be called from the onKeyPress() function, it returns true or false to indicate whether it handled the user input.

 

RichEdit.prototype.onKeyDown = function(e)

{

  // fetch event object

  var evt = e ? e : window.event;

 

  var bRet = true;

  var keyCode = evt.keyCode;

 

  // find the cursor

  var oCursor = this.oCursor;

  var oPos = this.getCursorPos();

 

  var oPrev = oPos.prev;

  var oNext = oPos.next;

  var oNext2, oPrev2;

  var nLeft = oCursor.offsetLeft;

  var nTop = oCursor.offsetTop;

  switch ( keyCode )

  {

  case 37: // left arrow

     if ( oPrev ) this.setCursorPos(oPrev);

     else this.setCursorPos(this.oDiv.firstChild);

    break;

  case 39: // right arrow

    this.setCursorPos(oNext);

    break;

  case 38: // up arrow

     ...

    break;

  case 40: // down arrow

     ...

    break;

  case 8 : // backsp

     if ( oPrev )

     {

       this.oDiv.removeChild(oPrev);

       this.assimilateStyle(oPos.current);

     }

    break;

  case 46: // del

     if ( oPos.current )

     {

       this.oDiv.removeChild(oPos.current);

       this.setCursorPos(oPos.next);

     }

    break;

  case 36: // home

  case 35: // end

  case 33: // page up

  case 34: // page down

    break;

  default:

     bRet = false;

    break;

  }

 

  this.seeCursor();

  if ( bIsNetscape ) return bRet;

}


Quite often actions performed in the RichEdit control will cause the cursor to move off the visible section of the <div> element so there needs to be some way to keep the cursor visible.

 

RichEdit.prototype.seeCursor = function()

{

  var oPos = this.getCursorPos();

  if ( !oPos.insert ) return;

 

  var sh = this.oDiv.scrollHeight;

  var st = this.oDiv.scrollTop;

  var ot = oPos.insert.offsetTop;

  var dh = this.oDiv.offsetHeight;

  var oh = this.oCursor.offsetHeight;

 

  // st should be less than ot

  // and greater than ot + oh - dh

  if ( st > ot ) this.oDiv.scrollTop = ot;

  else if ( st < (ot + oh - dh) ) this.oDiv.scrollTop = ot + oh - dh;

}

The RichEdit control needs a function to provide access to its document data. The getHTML() function provides this information in a string, much the same as the innerHTML property of HTML elements. However, the internal document of the RichEdit control is not very efficient for transmitting the data around as each character is contained within its own <span> element and has its own style properties. As a result, the getHTML() function compresses its output by merging <span> elements that have the same styles.

 

RichEdit.prototype.getHTML = function()

{

  // return a compact version of HTML contents in text form

 

  // remove the cursor first

  var oCursorPos = this.getCursorPos();

  if ( this.bInFocus )

  {

    this.oDiv.removeChild(this.oCursor);

  }

 

  // create a <div> scratchpad to build the new resulting document

  var oPad = this.getScratchPad();

 

  // iterate through each character-element or node

  // and merge into the scratch-pad

  var oElt = this.oDiv.firstChild;

  var oPost = null;

  while ( oElt )

  {

     if ( oPost &&

       (oPost.tagName.toLowerCase() == 'span') &&

       (oElt.tagName.toLowerCase() == 'span') &&

       this.compareStyle(oPost,oElt) )

     {

       // styles are the same, so merge

       oPost.innerHTML += oElt.innerHTML;

     }

     else

     {

       oPost = oElt.cloneNode(true);

       oPad.appendChild(oPost);

     }

     oElt = oElt.nextSibling;

  }

 

  if ( this.bInFocus ) this.setCursorPos(oCursorPos.current);

 

  return oPad.innerHTML;

}


 

current pageTo page 2
[next]

Created: March 27, 2003
Revised: February 9, 2005

URL: http://webreference.com/programming/javascript/gr/column12/1