Reputation: 348
I'm trying to make a simple syntax highlighter with JavaScript, but I always end up having the same problem. The program works as follows: as the user types enter (without the shift key) the program will replace the keyword var with another one with red color (this is still so basic). The problem is that whenever you press enter, the text gets highlighted but the cursor returns to the first word of the first line. How do you think I can prevent this from happening?
<div class="container">
<pre class="text"><code contenteditable="true" id="format">
</code></pre>
</div>
JS
var editor = document.getElementById('format');
var npatt = / *var +/igm
editor.addEventListener('keyup', highlight);
function highlight(e){
var content = editor.innerHTML;
if(e.which === 13 && e.shiftKey===false){
editor.innerHTML = content.replace(npatt, '<span style="color:red">var</span> ');
console.log(editor.innerHTML);
}
}
Upvotes: 1
Views: 984
Reputation: 8482
Moving the cursor to the end of a contenteditable
element can be done according to the method in this answer. That approach uses the window.getSelection()
method to find the cursor position.
I made a few changes to your code.
test
check to see if the regular expression even matches the content to avoid calling replace
and setting editor.innerHTML
on every Enter keystroke as the original code did.cursorManager.setEndOfContenteditable
method (from the answer referenced above) to reset the cursor to the end of the editor after the replace
operation.Here is the updated code.
var editor = document.getElementById('format');
var npatt = / *var +/igm;
editor.addEventListener('keyup', highlight);
function highlight(e){
var content = editor.innerHTML;
if(e.which === 13 && e.shiftKey === false && npatt.test(content)) {
editor.innerHTML = content.replace(npatt, '<span style="color:red">var</span> ');
cursorManager.setEndOfContenteditable(editor);
}
}
And here is a working example.
var editor = document.getElementById('format');
var npatt = / *var +/igm;
editor.addEventListener('keyup', highlight);
function highlight(e){
var content = editor.innerHTML;
if(e.which === 13 && e.shiftKey === false && npatt.test(content)) {
editor.innerHTML = content.replace(npatt, '<span style="color:red">var</span> ');
cursorManager.setEndOfContenteditable(editor);
}
}
//Code to set the cursor position modified from this answer: https://stackoverflow.com/a/19588665/830125
//Namespace management idea from http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/
(function( cursorManager ) {
//From: http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
var voidNodeTags = ['AREA', 'BASE', 'BR', 'COL', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR', 'BASEFONT', 'BGSOUND', 'FRAME', 'ISINDEX'];
//From: https://stackoverflow.com/questions/237104/array-containsobj-in-javascript
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
//Basic idea from: https://stackoverflow.com/questions/19790442/test-if-an-element-can-contain-text
function canContainText(node) {
if(node.nodeType == 1) { //is an element node
return !voidNodeTags.contains(node.nodeName);
} else { //is not an element node
return false;
}
};
function getLastChildElement(el){
var lc = el.lastChild;
while(lc && lc.nodeType != 1) {
if(lc.previousSibling)
lc = lc.previousSibling;
else
break;
}
return lc;
}
//Based on Nico Burns's answer
cursorManager.setEndOfContenteditable = function(contentEditableElement)
{
var range,selection;
if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
{
range = document.createRange();//Create a range (a range is a like the selection but invisible)
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection();//get the selection object (allows you to change selection)
selection.removeAllRanges();//remove any selections already made
selection.addRange(range);//make the range you have just created the visible selection
}
else if(document.selection)//IE 8 and lower
{
range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
range.select();//Select the range (make it the visible selection
}
}
}( window.cursorManager = window.cursorManager || {}));
<div class="container">
<pre class="text"><code contenteditable="true" id="format">
</code></pre>
</div>
Upvotes: 2