Wednesday, November 9, 2011

Cursor Positions, Selected Text, and A Simple HTML Editor


It would be nice if we have a HTML text editor that doing a simple task of HTML editor for us. It works just like HTML view of WordPress admin page when we write a post. We select the text press specific button and then the selected text will be enclosed with appropriate format. I Test the code in Mozilla Firefox and Internet Explorer, so in both browsers this code should works fine. Here the screenshoots:
Screenshoots page.
We need to prepare the prerequisites.
As prerequisites, at least we need two things. First, we need to know how to define the cursor position inside a <textarea> element, second we need to know how to retrieve a selected text inside a <textarea> element. So, for the first step, I will talk about the both things shortly.
Every browser brings the different behaviour. The browser quirk will make our code a little bit longer because we need to specify different code for different browser. Defining cursor position (current position) is trivial in Mozilla Firefox, but we need a little trick in Internet Explorer. If we want to retrieve selected text in a <textarea> element, both (Forefox and IE) has their own way too.
Defining Cursor Position Inside a <textarea> Element
In Firefox we just need the following code to determine the current position of cursor in a <textarea> element:
var currentPosition = txtArea.selectionStart;
txtArea is a <textarea> element.
Internet Explorer (I use IE 6) doesn’t have selectionStart and selectionEnd property, so we need a little more effort to determine start and end position of the cursor (this is a tricky way).

var range = document.selection.createRange();
var drange = range.duplicate();
drange.moveToElementText(txtArea);
drange.setEndPoint("EndToEnd", range);
var currentPosition = drange.text.length - range.text.length;
var endPosition = drange.text.length - currentPosition;
Defining Selected Text Inside a <textarea> Element
In Firefox we need more effort to retrieve selected text inside a <textarea> element, we get selected text with the help of selectionStart and selectionEnd property. Then split <textarea> value using selectionStart and selectionEnd values.

var txtArea = document.getElementById("txtArea");
var text = txtArea.value;
var selectedText = text.substring(txtArea.selectionStart,txtArea.selectionEnd);

In Internet Explorer (IE) we need the following code to get the selected text:

var selectedText = document.selection.createRange().text;
Simple HTML Text Editor
Now we will combine these two things, and get ready for bigger challenge! We will create a simple HTML text editor.
It just works as is, we type something in textarea, select the text and press one of button above the textarea to insert appropriate tag. This simple HTML text editor also work fine with new line and paragraph. So if you place a new line in textarea (by press ‘Enter’ button) it would be displayed as new line too. The same thing happen for paragraph.
Here a screenshoot of my HTML text editor:
A simple HTML text editor.
Now, lets take a look at the code. I use HTML file for presentation and Javascript code for hadling what should happen under the hood.
    <html><head><title>HTML Editor</title><script lang=”javascript” type=”text/javascript” src=”htmleditor.js”></script></head><body><input type=”button” id=”btnBold” value=”B” onclick=”insertsBoldTag()” /><input type=”button” id=”btnItalic” value=”I” onclick=”insertsItalicTag()” /><input type=”button” id=”btnUnderline” value=”U” onclick=”insertsUnderlineTag()” /><input type=”button” id=”btnDelete” value=”Del” onclick=”insertsDeleteTag()” /><br/><textarea id=”area” cols=”35″ rows=”12″></textarea><p><input type=”button” id=”btn” value=”Publish” onclick=”publish()” /><p><div id=”showDiv”></div></body></html>

I think the HTML page is quite simple, it draws four buttons (bold, italic, underline, and del button respectively), a textarea, and a publish button.
All processing handled by the following Javascript file:
var area;
var showDiv;
var formattedStr = "";
startPosition = 0;
var endPosition = 0;

window.onload = function()
{
area = document.getElementById("area");
showDiv = document.getElementById("showDiv");
};

function publish()
{
formattedStr = "<pre>"+document.getElementById("area").value+"</pre>";
showDiv.innerHTML = formattedStr;
}

function insertsBoldTag()
{
findPositions();

var text = area.value;
if (startPosition!=endPosition) {
var sbStr = text.substring(startPosition,endPosition);
sbStr = "<b>"+sbStr+"</b>";

fillsFormattedString(text,sbStr);
}
}

function insertsItalicTag()
{
findPositions();

var text = area.value;
if (startPosition!=endPosition) {
var sbStr = text.substring(startPosition,endPosition);
sbStr = "<i>"+sbStr+"</i>";

fillsFormattedString(text,sbStr);
}
}

function insertsUnderlineTag()
{
findPositions();

var text = area.value;
if (startPosition!=endPosition) {
var sbStr = text.substring(startPosition,endPosition);
sbStr = "<u>"+sbStr+"</u>";

fillsFormattedString(text,sbStr);
}
}

function insertsDeleteTag()
{
findPositions();

var text = area.value;
if (startPosition!=endPosition) {
var sbStr = text.substring(startPosition,endPosition);
sbStr = "<del>"+sbStr+"</del>";

fillsFormattedString(text,sbStr);
}
}

function findPositions()
{
var text = area.value;

if (document.selection) {
// Internet Explorer
var range = document.selection.createRange();
var dpl = range.duplicate();
if (range.text.length > 0) {
dpl.moveToElementText(area);
dpl.setEndPoint("EndToEnd", range);
startPosition = dpl.text.length-range.text.length;
endPosition = startPosition + range.text.length;
}
}
else {
// Mozilla Firefox
startPosition = area.selectionStart;
endPosition = area.selectionEnd;
}
}

function fillsFormattedString(text, selectedText)
{
// split textarea value into three pieces: before startPosition,
// startPosition until endPosition, and after endPosition
var str1 = text.substring(0,startPosition);
var str2 = text.substring(startPosition,endPosition);
var str3 = text.substring(endPosition,text.length);

// replace str2 with formatted substring (selectedText)
str2 = selectedText;
// form the new string
formattedStr = str1+str2+str3;
area.value = formattedStr;
}

To apply HTML text editor, we need to know how to get cursor positions and how to retrieve selected text.
Lets take a look at Javascript code. In window.onload (happen when our page is load) I retrieve <textarea> and <div> element
window.onload = function()
{
area = document.getElementById("area");
showDiv = document.getElementById("showDiv");
};
When we click bold button (‘B‘) the cursor positions (startPosition and endPosition) will be filled with appropriate value depend on what we have already selected. Split the string value using startPosition and endPosition, then enclose the string with <b> tag.

findPositions();


var text = area.value;
if (startPosition!=endPosition) {
var sbStr = text.substring(startPosition,endPosition);
sbStr = "<b>"+sbStr+"</b>";
The next step, I split the string into three pieces: first character to startPosition, between startPosition and endPosition, endPosition until last character.

var str1 = text.substring(0,startPosition);
var str2 = text.substring(startPosition,endPosition);
var str3 = text.substring(endPosition,text.length);
Then I replace the second substring (str2) with selectedText (text that we have already inserted with the appropriate tag). So the result is new string with html tag inside it, and gives it back to textarea.

// replace str2 with formatted substring (selectedText)
str2 = selectedText;
// form the new string
formattedStr = str1+str2+str3;
area.value = formattedStr;
The same thing also valid for italic, underline, and del button but we insert <i>, <u>, <del>, for italic, underline, and del button respectively.
When we click ‘Publish’ button, we retrieve value of textarea and pass it as innerHTML of showDiv element.

formattedStr = "<pre>"+document.getElementById("area").value+"</pre>";
showDiv.innerHTML = formattedStr;