I have a contenteditable div I want users to type in. When users type inside the box with onkeyup, I activate a function that changes the color of certain characters:
var lTags = fixedCode.innerHTML.replace(/</gi, "<span style='color:gold;'><</span>");
var rTags = lTags.replace(/>/gi, "<span style='color:gold'>></span>");
fixedCode.innerHTML = rTags;
What this code does is it takes every < sign and every > sign and turns it into a gold color. However, when I do this, I am no longer able to type words into the contenteditable box since the box refreshes itself every time I press a key.
function checkIt(code) {
var fixedCode = document.getElementById(code);
var lTags = fixedCode.innerHTML.replace(/</gi, "<span style='color:gold;'><</span>");
var rTags = lTags.replace(/>/gi, "<span style='color:gold'>></span>");
fixedCode.innerHTML = rTags;
}
<div id="box" contenteditable="true" onkeyup="checkIt(this.id);">See for yourself</div>
To see for yourself, try typing any HTML tag in the box.
First of all, why does it change the color of the left <
of a tag but not the right part of the tag >
? And how can I actually type inside the box without deleting the color-changing stuff. I've seen similar questions, but the answers were Jquery. I do not want to use JQUERY!
You can't actually highlight text in a <textarea> . Any markup you would add to do so would simply display as plain text within the <textarea> . The good news is, with some carefully crafted CSS and JavaScript, you can fake it.
You can add the contenteditable="true" HTML attribute to the element (a <div> for example) that you want to be editable. If you're anticipating a user to only update a word or two within a paragraph, then you could make a <p> itself editable.
contenteditable is an HTML attribute that you can add to any HTML element. If its value is true or an empty string, that means that the user can edit it by clicking the element. For example: <div contenteditable="true"> Change me! </ div>
To edit the content in HTML, we will use contenteditable attribute. The contenteditable is used to specify whether the element's content is editable by the user or not. This attribute has two values. true: If the value of the contenteditable attribute is set to true then the element is editable.
I was too lazy to go hardcore with JavaScript and one idea that popped my mind was to use
PROS
CONS
O(n)
For a beautiful read on optimization head to VSCode Optimizations in Syntax Highlighting
const highLite = (el) => {
el.previousElementSibling.innerHTML = el.innerHTML
.replace(/(<|>)/g, "<span class='hl_angled'>$1</span>")
.replace(/(\{|\})/g, "<span class='hl_curly'>$1</span>");
};
document.querySelectorAll("[contenteditable]").forEach(el => {
el.addEventListener("input", () => highLite(el));
highLite(el);
});
body{margin:0; font:14px/1 sans-serif;}
.highLite{
border: 1px solid #888;
position: relative;
}
.highLite_colors,
.highLite_editable {
padding: 16px;
}
/* THE UNDERLAYING ONE WITH COLORS */
.highLite_colors {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
user-select: none;
}
/* THE OVERLAYING CONTENTEDITABLE WITH TRANSPARENT TEXT */
.highLite_editable {
position: relative;
color: transparent; /* Make text invisible */
caret-color: black; /* But keep caret visible */
}
.hl_angled{ color: turquoise; }
.hl_curly{ color: fuchsia; }
Try to type some angled < > or curly { } brackets
<div class="highLite">
<div class="highLite_colors">Type <here> {something}</div>
<div class="highLite_editable" contenteditable>Type <here> {something}</div>
</div>
(use at your own risk this was my coffee-time playground)
const lang = {
js: {
equa: /(\b=\b)/g,
quot: /(('.*?')|(".*?")|(".*?(?<!\\)")|('.*?(?<!\\)')|`)/g,
comm: /((\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/)|(\/\/.*))/g,
logi: /(%=|%|\-|\+|\*|&{1,2}|\|{1,2}|<=|>=|<|>|!={1,2}|={2,3})/g,
numb: /(\d+(\.\d+)?(e\d+)?)/g,
func: /(?<=^|\s*)(async|await|console|alert|Math|Object|Array|String|class(?!\s*\=)|function)(?=\b)/g,
decl: /(?<=^|\s*)(var|let|const)/g,
pare: /(\(|\))/g,
squa: /(\[|\])/g,
curl: /(\{|\})/g,
},
html: {
tags: /(?<=<(?:\/)?)(\w+)(?=\s|\>)/g,
// Props order matters! Here I rely on "tags"
// being already applied in the previous iteration
angl: /(<\/?|>)/g,
attr: /((?<=<i class=html_tags>\w+<\/i>)[^<]+)/g,
}
};
const highLite = el => {
const dataLang = el.dataset.lang; // Detect "js", "html", "py", "bash", ...
const langObj = lang[dataLang]; // Extract object from lang regexes dictionary
let html = el.innerHTML;
Object.keys(langObj).forEach(function(key) {
html = html.replace(langObj[key], `<i class=${dataLang}_${key}>$1</i>`);
});
el.previousElementSibling.innerHTML = html; // Finally, show highlights!
};
const editors = document.querySelectorAll(".highLite_editable");
editors.forEach(el => {
el.contentEditable = true;
el.spellcheck = false;
el.autocorrect = "off";
el.autocapitalize = "off";
el.addEventListener("input", () => highLite(el));
highLite(el); // Init!
});
* {margin: 0; box-sizing: boder-box;}
body {
font: 14px/1.4 sans-serif;
background: hsl(220, 16%, 16%);
color: #fff;
padding: 16px;
}
#editor {
display: flex;
}
h2 {
padding: 16px 0;
font-weight: 200;
font-size: 14px;
}
.highLite {
position: relative;
background: hsl(220, 16%, 14%);
}
.highLite_colors,
.highLite_editable {
padding: 16px;
top: 0;
left: 0;
right: 0;
bottom: 0;
white-space: pre-wrap;
font-family: monospace;
font-size: 13px;
}
/* THE OVERLAYING CONTENTEDITABLE WITH TRANSPARENT TEXT */
.highLite_editable {
position: relative;
color: transparent; /* Make text invisible */
caret-color: hsl( 50, 75%, 70%); /* But keep caret visible */
}
.highLite_editable:focus {
outline: 1px solid hsl(220, 16%, 19%);
}
.highLite_editable::selection {
background: hsla( 0, 0%, 90%, 0.2);
}
/* THE UNDERLAYING ONE WITH HIGHLIGHT COLORS */
.highLite_colors {
position: absolute;
user-select: none;
}
.highLite_colors i {
font-style: normal;
}
/* JS */
i.js_quot { color: hsl( 50, 75%, 70%); }
i.js_decl { color: hsl(200, 75%, 70%); }
i.js_func { color: hsl(300, 75%, 70%); }
i.js_pare { color: hsl(210, 75%, 70%); }
i.js_squa { color: hsl(230, 75%, 70%); }
i.js_curl { color: hsl(250, 75%, 70%); }
i.js_numb { color: hsl(100, 75%, 70%); }
i.js_logi { color: hsl(200, 75%, 70%); }
i.js_equa { color: hsl(200, 75%, 70%); }
i.js_comm { color: hsl(200, 10%, 45%); font-style: italic; }
i.js_comm > * { color: inherit; }
/* HTML */
i.html_angl { color: hsl(200, 10%, 45%); }
i.html_tags { color: hsl( 0, 75%, 70%); }
i.html_attr { color: hsl(200, 74%, 70%); }
<h2>HTML</h2>
<div class="highLite">
<div class="highLite_colors"></div>
<div class="highLite_editable" data-lang="html"><h2 class="head">
TODO: HTML is for <b>homework</b>
</h2></div>
</div>
<h2>JAVASCRIPT</h2>
<div class="highLite">
<div class="highLite_colors"></div>
<div class="highLite_editable" data-lang="js">// Type some JavaScript here
const arr = ["high", 'light'];
let n = 2.1 * 3;
if (n < 10) {
console.log(`${n} is <= than 10`);
}
function casual(str) {
str = str || "non\"sense";
alert("Just a casual"+ str +", still many TODOs");
}
casual (arr.join('') +" idea!");
/**
* The code is a proof of concept and far from
* perfect. You should never use regex but create or use a parser.
* Meanwhile, play with it and improve it!
*/</div>
</div>
TODO
given this basic idea, some TODOs I left for the reader:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With