How to Create a WYSIWYG Rich Text Editor in JavaScript. Pt. 2
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;
}
[next] |
Created:
March 27, 2003
Revised: February 9, 2005
URL: https://webreference.com/programming/javascript/gr/column12/1