One of the things which I like to have is a text area in which I can input TAB characters using the TAB key.
It breaks the web page of course, because the TAB key is used for navigating from one form
control to the next; but there are circumstances when I want to use a tab. Usually when I am inputting code, as I prefer the TAB character for my indenting.
Opera does not play so well, as preventDefault()
does not seem to work for the tab key.
So lets get stuck in.
The TEXTAREA
Firstly we need to tell the TEXTAREA
to call-up a suitable function in response to a keyboard event. The appropriate keyboard event is onkeydown
.
<textarea onkeydown="HTMLTextArea_InsertTab(event)" style="width:500px; height:200px;"></textarea>
The Event Object
Then in the function, we need to get the event object.
In Internet Explorer the event is accessed by window.event
. In other browsers the event object is either supplied as an argument to the function (if you use addEventListener
) or must be supplied as an argument (for inline events as above).
Thus:
<textarea onkeydown="HTMLTextArea_InsertTab(event)" style="width:500px; height:200px;"></textarea>
function HTMLTextArea_InsertTab(
event /*: Event*/
) /*: void*/
{
event = event || window.event;
...
The Key Code
Next we need to get the key code for the pressed key, so that we respond appropriately if the TAB key was pressed.
The ASCII value for a TAB is 9
.
if (event.keyCode == 9)
{
... do our stuff
}
Inserting the Tab
In order to insert a TAB character, we need to identify where the cursor is in the TEXTAREA, and then proceed to insert the TAB at the appropriate point. Easy, but unfortunately IE and the other browsers vary dramatically in how this can be achieved.
Internet Explorer
Internet Explorer has a non-standard TextRange
object which gives you programmatic control over cursors and text editing. This is accessed through the selection
object on the document
.
To insert the TAB you need the following:-
if (document.selection)
{
var range /*: HostObject(TextRange)*/ = document.selection.createRange();
range.collapse();
range.text = "\t";
range.collapse();
range.select();
}
The document.selection
references a TextRange
which covers a region of text on your web page. This is either an empty region (just the cursor) or a region of text which you have highlighted (i.e. selected).
The range.collapse()
collapses the range to the end of whatever region of text is highlighted. If the range is empty, it does nothing.
The range.select()
method returns focus to the point after the point at which the tab was inserted.
Simple.
Mozilla/Firefox and Opera
The Firefox and Opera textarea
has a selection range, which is represented by the selectionStart
and selectionEnd
properties. These will be the same values if it is just an empty cursor.
It offers the setSelectionRange(start : int, end : int)
method to set the range, and the focus()
method to bring the cursor back to life.
if (typeof textarea.selectionStart !== "undefined")
{
/* Collapse Insertion Point */
textarea.setSelectionRange(textarea.selectionEnd, textarea.selectionEnd);
textarea.focus();
/* Insert Tab */
var selStart /*: int*/ = textarea.selectionStart;
var selEnd /*: int*/ = textarea.selectionEnd;
var currentScroll /*: int*/ = textarea.scrollTop;
var contents /*: String*/ = textarea.value;
textarea.value = contents.substring(0, selStart) + "\t" + contents.substring(selEnd, contents.length);
textarea.scrollTop = currentScroll;
textarea.setSelectionRange(selEnd + 1, selEnd + 1);
textarea.focus();
//window.setTimeout(function(){
// textarea.focus();
//}, 0);
}
The scrollTop
test is needed, because the scroll may reset when you write values back into the textarea.
The timeout may need to be used in order to give the cursor a nudge.
Sometimes this code does not work - no error is thrown, it just randomly fails. Have not worked out why, and failure cannot be reliably reproduced.
Blocking the Default Behaviour
Once we have inserted the TAB, we need to stop the browser from jumping to the next form
control, which is the normal consequence of pressing the TAB character.
Internet Explorer
In IE the event
object has a returnValue
property, so we say the following at the end of our function:-
if (window.event)
{
window.event.returnValue = false;
}
Firefox
In Firefox the event
object has a preventDefault
method, so we say the following at the end of our function:-
if (typeof event.preventDefault != "undefined")
{
event.preventDefault();
}
In Firefox the event
object is supplied as an argument to the function automatically, whereas in IE, the event
object is a property of the window
object.
Opera
Unfortunately, with Opera, if there is another control on the page, the default behaviour (tabbing to another control) fires first, before the keydown
event, so you cannot stop it. This seems like a bug with opera. The focus()
method brings focus back, but not without a jump in the page, if the other control is above or below the current scroll window.
The final code
So here is the final code.
<html>
<head>
<script>
function HTMLTextArea_InsertTab(
event /*: Event*/
) /*: void*/
{
event = event || window.event;
var textarea /*: HTMLTextAreaElement*/ = event.srcElement || event.target;
if (event.keyCode == 9)
{
if (typeof textarea.selectionStart !== "undefined")
{
/* Collapse Insertion Point */
textarea.setSelectionRange(textarea.selectionEnd, textarea.selectionEnd);
textarea.focus();
/* Insert Tab */
var selStart /*: int*/ = textarea.selectionStart;
var selEnd /*: int*/ = textarea.selectionEnd;
var currentScroll /*: int*/ = textarea.scrollTop;
var contents /*: String*/ = textarea.value;
textarea.value = contents.substring(0, selStart) + "\t" + contents.substring(selEnd, contents.length);
textarea.scrollTop = currentScroll;
textarea.setSelectionRange(selEnd + 1, selEnd + 1);
textarea.focus();
//window.setTimeout(function(){
// textarea.focus();
//}, 0);
event.preventDefault();
}
else if (document.selection)
{
var range /*: HostObject(TextRange)*/ = document.selection.createRange();
range.collapse();
range.text = String.fromCharCode(9);
range.collapse();
range.select();
event.returnValue = false;
}
}
}
</script>
</head>
<body>
<textarea onkeydown="HTMLTextArea_InsertTab(event)" style="width:500px; height:200px;"></textarea>
<input value="An input to test whether focus shifts">
</body>
</html>
Note finally that the IE test comes second. This is because for some odd reason Opera returns true
for a test on document.selection
.
Sorry, comments have been suspended. Too much offensive comment spam is causing the site to be blocked by firewalls (which ironically therefore defeats the point of posting spam in the first place!). I don't get that many comments anyway, so I am going to look at a better way of managing the comment spam before reinstating the comments.