Mimicng caret in a textarea
caret, javascript, richtextbox, textarea
Solution
Doesn't an editable Div element solve the entire problem?
Code that does the highlighting:
http://jsfiddle.net/masbicudo/XYGgz/3/
var prevText = "";
var isHighlighting = false;
$("#textarea").bind("paste drop keypress input textInput DOMNodeInserted", function (e){
if (!isHighlighting)
{
var currentText = $(this).text();
if (currentText != prevText)
{
doSave();
isHighlighting = true;
$(this).html(currentText
.replace(/\bcolored\b/g, "<font color=\"red\">colored</font>")
.replace(/\bhighlighting\b/g, "<span style=\"background-color: yellow\">highlighting</span>"));
isHighlighting = false;
prevText = currentText;
doRestore();
}
}
});
Unfortunately, this made some editing functions to be lost, like Ctrl + Z... and when pasting text, the caret stays at the beginning of the pasted text.
I have combined code from other answers to produce this code, so please, give them credit.
- How do I make an editable DIV look like a text field?
- Get a range's start and end offset's relative to its parent container
EDIT: I have discovered something interesting... the native caret appears if you use a contentEditable element, and inside of it you use another element with the invisible font:
<div id="textarea" contenteditable style="color: red"><div style="color: transparent; background-color: transparent;">This is some hidden text.</div></div>
http://jsfiddle.net/masbicudo/qsRdg/4/
Problem
I am trying to mimic the caret of a textarea for the purpose of creating a very light-weight rich-textarea. I don't want to use something like codemirror or any other massive library because I will not use any of their features. I have a `<pre>` positioned behind a textarea with a transparent background so i can simulate a highlighting effect in the text. However, I also want to be able to change the font color (so its not always the same). So I tried `color: transparent` on the textarea which allows me to style the text in any way I want because it only appears on the `<pre>` element behind the textarea, but the caret disappears. I have gotten it to work fairly well, although it is not perfect. The main problem is that when you hold down a key and spam that character, the caret seems to always lag one character behind. Not only that, it seems to be quite resource heavy.. If you see any other things in the code that need improvement, feel free to comment on that too! Here's a fiddle with the code: http://jsfiddle.net/2t5pu/25/ And for you who don't want to visit jsfiddle for whatever reason, here's the entire code: CSS: ``` textarea, #fake_area { position: absolute; margin: 0; padding: 0; height: 400px; width: 600px; font-size: 16px; font: 16px "Courier New", Courier, monospace; white-space: pre; top: 0; left: 0; resize: none; outline: 0; border: 1px solid orange; overflow: hidden; word-break: break-word; padding: 5px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; } #fake_area { /* hide */ opacity: 0; } #caret { width: 1px; height: 18px; position: absolute; background: #f00; z-index: 100; } ``` HTML: ``` <div id="fake_area"><span></span></div> <div id="caret"></div> <textarea id="textarea">test</textarea> ``` JAVASCRIPT: ``` var fake_area = document.getElementById("fake_area").firstChild; var fake_caret = document.getElementById("caret"); var real_area = document.getElementById("textarea"); $("#textarea").on("input keydown keyup propertychange click", function () { // Fill the clone with textarea content from start to the position of the caret. // The replace /\n$/ is necessary to get position when cursor is at the beginning of empty new line. doStuff(); }); var timeout; function doStuff() { if(timeout) clearTimeout(timeout); timeout=setTimeout(function() { fake_area.innerHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001'); setCaretXY(fake_area, real_area, fake_caret, getPos("textarea")); }, 10); } function getCaretPosition(el) { if (el.selectionStart) return el.selectionStart; else if (document.selection) { //el.focus(); var r = document.selection.createRange(); if (r == null) return 0; var re = el.createTextRange(), rc = re.duplicate(); re.moveToBookmark(r.getBookmark()); rc.setEndPoint('EndToStart', re); return rc.text.length; } return 0; } function setCaretXY(elem, real_element, caret, offset) { var rects = elem.getClientRects(); var lastRect = rects[rects.length - 1]; var x = lastRect.left + lastRect.width - offset[0] + document.body.scrollLeft, y = lastRect.top - real_element.scrollTop - offset[1] + document.body.scrollTop; caret.style.cssText = "top: " + y + "px; left: " + x + "px"; //console.log(x, y, offset); } function getPos(e) { e = document.getElementById(e); var x = 0; var y = 0; while (e.offsetParent !== null){ x += e.offsetLeft; y += e.offsetTop; e = e.offsetParent; } return [x, y]; } ``` Thanks in advance!