JSFiddle - React, Tailwind, and code Playground
by Ben Gillbanks
HTML
<pre class="editor" contenteditable="true" spellcheck="false"></pre>
CSS
.editor {
font-family: 'Roboto Mono', monospace;
font-size: 12px;
outline: none;
overflow-y: auto;
padding-left: 48px;
counter-reset: line;
}
.editor div {
display: block;
position: relative;
white-space: pre-wrap;
}
.editor br::before {
content: counter(line);
counter-increment: line;
position: absolute;
right: calc(100% + 16px);
opacity: 0.5;
}
JavaScript
// Syntax highlight for JS
const js = el => {
const s = el.innerText
.replace(/(\/\/.*)/g, '<em>$1</em>')
.replace(
/\b(new|if|else|do|while|switch|for|in|of|continue|break|return|typeof|function|var|const|let|\.length|\.\w+)(?=[^\w])/g,
'<strong>$1</strong>',
)
.replace(/(".*?"|'.*?'|`.*?`)/g, '<strong><em>$1</em></strong>')
.replace(/\b(\d+)/g, '<em><strong>$1</strong></em>');
el.innerHTML = s.split('\n').join('<br/>');
console.log(el.innerHTML);
};
const editor = (el, highlight = js, tab = ' ') => {
const caret = () => {
const range = window.getSelection().getRangeAt(0);
const prefix = range.cloneRange();
prefix.selectNodeContents(el);
prefix.setEnd(range.endContainer, range.endOffset);
return prefix.toString().length;
};
const setCaret = (pos, parent = el) => {
for (const node of parent.childNodes) {
if (node.nodeType == Node.TEXT_NODE) {
if (node.length >= pos) {
const range = document.createRange();
const sel = window.getSelection();
range.setStart(node, pos);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
return -1;
} else {
pos = pos - node.length;
}
} else {
pos = setCaret(pos, node);
if (pos < 0) {
return pos;
}
}
}
return pos;
};
highlight(el);
el.addEventListener('keydown', e => {
if (e.which === 9) {
const pos = caret() + tab.length;
const range = window.getSelection().getRangeAt(0);
range.deleteContents();
range.insertNode(document.createTextNode(tab));
highlight(el);
setCaret(pos);
e.preventDefault();
}
console.log(e.which);
});
el.addEventListener('keyup', e => {
if (e.keyCode >= 0x30 || e.keyCode == 0x20) {
const pos = caret();
highlight(el);
setCaret(pos);
}
});
};
// Turn div...