Skip to content

Instantly share code, notes, and snippets.

@mzechmeister
Created October 7, 2021 13:08
Show Gist options
  • Save mzechmeister/e0c55a237f15071575a6b482785ee3e9 to your computer and use it in GitHub Desktop.
Save mzechmeister/e0c55a237f15071575a6b482785ee3e9 to your computer and use it in GitHub Desktop.
handling caret contenteditable
<!DOCTYPE html>
<html>
<style>
div {background-color: #dfd; width:300px;}
span {background-color: #fbb;}
span:nth-child(even) {background-color: #ccf;}
input {width:90%; font-size: 11px;}
</style>
<body>
Works good with firefox. Android chrome has another behaviour.
<div id="div1" contenteditable onkeydown="check_caret(event)" onkeyup="focusToChild(event)"><span>START</span>Here <span>mark_active()+check_caret()</span> lets appear the span focussed <span>set</span><span>caret</span> (indicated by the border when active). <span>END</span></div>
<script>
for (x of document.getElementById("div1").children)
x.tabIndex = -1
function focusToChild(e) {
// focus to child
var sel = window.getSelection()
var foc = sel.focusNode.parentElement
if (foc.tagName == "SPAN") // && foc.parentElement==e.target)
foc.focus()
}
function check_caret(e) {
var sel = window.getSelection()
var foc = sel.focusNode.parentElement
if (e.key == 'ArrowRight') {
if (sel.focusOffset == sel.focusNode.length-1) {
// work around that the last element is not covered
e.preventDefault() // do not blur
setCaret(sel.focusNode, sel.focusNode.length)
} else if (sel.focusOffset == sel.focusNode.length) {
if (foc.nextSibling == foc.nextElementSibling) {
// next element is again a contain not text
// focus between the two elements first
childnum = [...foc.parentNode.childNodes].indexOf(foc)
setCaret(div1, childnum+1)
e.preventDefault()
} else
e.target.parentElement.focus()
}
} else if (e.key == 'ArrowLeft') {
if (sel.focusOffset == 0) {
if (foc.previousSibling == foc.previousElementSibling) {
// next element is again a contain not text
// focus between the two elements first
childnum = [...foc.parentNode.childNodes].indexOf(foc)
setCaret(div1, childnum)
//setCaret(sel.focusNode.parentNode, 1)
//e.target.parentElement.focus()
e.preventDefault()
} else
e.target.parentElement.focus()
}
}
}
var selection = window.getSelection()
function setCaret(elem, pos=0) {
var range = document.createRange() // Create a range (a range is a like the selection but invisible)
range.selectNodeContents(elem) // Select the entire contents of the element with the range
range.setStart(elem, pos)
range.collapse(true) // Collapse the range to the end point. false means collapse to end rather than the start 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
elem.parentElement.focus()
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment