JSFiddle - React, Tailwind, and code Playground
HTML
<div contenteditable class="empty"></div>
<table>
<caption>Current Selection Info</caption>
<tr><th><th>Container<th>Offset</tr>
<tr><th>Start<td><td></tr>
<tr><th>End<td><td></tr>
</table>
<b>Instructions</b>:
<ol>
<li>Click in the white box. Note that the input caret is <i>before</i> the “type here” text.
<li>Press the ← and → keys. Note that you cannot move the input caret within the “type here” text.
<li>Type a few characters in the box.
<li>Select all of the text you typed (⌘-A or Ctrl-A).
<li>Press Backspace or Delete.
<li>Note that the input caret is now <i>after</i> the “type here” text and has changed color. You can now move the input caret within the “type here” text using the ← and → keys. Also note that attempting to access information about the selection range throws an exception.
</ol>
CSS
body {
padding: 10px 20px;
background: lightgray;
}
[contenteditable] {
background: white;
}
[contenteditable].empty::before {
content: 'type here';
color: #ccc;
}
[contenteditable]:focus {
outline: none;
}
table { margin: 20px 50px }
caption { margin-bottom: 10px }
th { min-width: 70px }
td { text-align: center }
JavaScript
document.querySelector('[contenteditable]').addEventListener('input', function () {
this.className = this.textContent ? '' : 'empty';
});
var dd = document.getElementsByTagName('td');
function updateSelectionInfo() {
var sel = window.getSelection();
var r = sel.getRangeAt(0);
try {
dd[0].textContent = r.startContainer.tagName || (r.startContainer.nodeType === 3 ? 'TEXT' : '?');
} catch (e) {
dd[0].textContent = e;
}
try {
dd[1].textContent = r.startOffset;
} catch (e) {
dd[1].textContent = e;
}
try {
dd[2].textContent = r.endContainer.tagName || (r.endContainer.nodeType === 3 ? 'TEXT' : '?');
} catch (e) {
dd[2].textContent = e;
}
try {
dd[3].textContent = r.endOffset;
} catch (e) {
dd[3].textContent = e;
}
}
updateSelectionInfo();
setInterval(updateSelectionInfo, 200);