Skip to content

Instantly share code, notes, and snippets.

@mzechmeister
Last active January 3, 2022 21:00
Show Gist options
  • Save mzechmeister/d0333d6e46746efc2f06e61547d69047 to your computer and use it in GitHub Desktop.
Save mzechmeister/d0333d6e46746efc2f06e61547d69047 to your computer and use it in GitHub Desktop.
pseudo editable field (code mirror lite)
<!DOCTYPE html>
<html>
<head>
<style>
#cursor {
border-left: 1px solid red;
animation: blink 1s;
margin-left: -1px;
animation-iteration-count: infinite;
}
span {
color: green;
}
span:not(#cursor):before {
content: "5";
vertical-align: super;
font-size: smaller;
}
@keyframes blink {
50% { border-color: #ffffff00; } }}
</style>
</head>
<body>
<textarea id="ta" style="height:150px">The top element is a textarea and the div at the bottom a pseudo editable field. Type to see how the caret follows. As before, click sets the caret properly (curpos computed with node loop; would allow to corrected of string length changes by parser). Still, text selection are now deleted. Since the parse changes the string length at dollar this needs to be taken into account.</textarea>
<div id="mydiv" onclick="ta.selectionEnd = getSelectionPosition(); ta.selectionStart = ta.selectionEnd; ta.focus(); update()"></div>
<script>
function getSelectionPosition() {
var sel = window.getSelection()
var node = sel.focusNode
var pos = sel.focusOffset
while (node = node.previousSibling)
pos += (node.nodeType == Node.TEXT_NODE ? node : node.innerText).length
return pos
}
function parse(t) {
return t.replaceAll('$', '<span>XX</span>')
}
function update() {
curpos = ta.selectionEnd
mydiv.innerHTML = parse(ta.value.slice(0, curpos)+"<span id='cursor'></span>"+ta.value.slice(curpos))
}
ta.onkeydown = update
ta.oninput = update
ta.onclick = update
ta.onkeyup = update
update()
</script>
</body>
</html>
@mzechmeister
Copy link
Author

mzechmeister commented Nov 14, 2021

preview latest: https://gistpreview.github.io/?d0333d6e46746efc2f06e61547d69047

  • Rev 41 mouseup to focus ta (should be better than Rev 39)
  • Rev 40 Cleaned.
  • Rev 39 fix: text selection with mouse difficult (focus went to ta).
  • Rev 38 feat: Undo/redo button.
  • Rev 37 Cleaned.
  • Rev 36: fix: edge requires clipboard for dataTransfer.
  • Rev 35: fix: Handling of backward selection.
  • Rev 34 refact: Using e.inputType to detect various events.
  • Rev 33 fix: oninput fires again after drop.
  • Rev 32 chore: New variable name insText.
  • Rev 31 chore: New variable names cutA and cut.
  • Rev 30 fix: No pseudocursor for del key.
  • Rev 29 chore: divcutsel renamed to cutpos.
  • Rev 28 Better handling of drop (selection history).
  • Rev 27 Better handling of drop (selection history).
  • Rev 26 Fix drop not working properly anymore.
  • Rev 25 Space formatting.
  • Rev 24 Handle input in editable div.
  • Rev 23 Simplifications; update function removed.
  • Rev 22 Handback textarea selection to div selection.
  • Rev 21 A try to handle drag and drop. Simplier but now textarea and div must have the same content. Undo with ctrl-Z ends up with selection all.
  • Rev 20 Redirect drop from mydiv to textarea.
  • Rev 19 Cleaning + simplification.
  • Rev 18 getClientRects.
  • Rev 17 fix minor caret misbehaviour (jump at pos 0 -> 1, hide caret when focus out).
  • Rev 16 sel.anchorNode returns body not ta in chrome and edge, instead use document.activeElement.
  • Rev 15 range.toString().length instead of node loop.
  • Rev 14: div is contenteditable, but only to simplify position detection.
  • Rev 13: refact: Cursor as relative div. But: textnode dissociates, extended range not marked anymore.
  • Rev 12: fix: caret behaviour at nested span (located at middle start or end).
  • Rev 11: refact: overlay cursor the absolute.
  • Rev 10: last version with inline span for caret;
    fix backward selection (desktop only), double click selection not working
  • Rev 9: fix mouse selection does not work.
  • Rev 8: replaceAll not working on some android, replaced by replace + / /g.
  • Rev 7: mousedown+preventDefault to keep focus on textarea, and rangeParent instead of selection. Now, build-in text selection on mobiles does not popup. There seems to be no redundant selectionchange calls.
  • Rev 6: refact: onselectionchange instead of key events. Now pseudo caret is also moved for spacebar swiping on mobiles. onclick does not set the caret (firefox).
  • Rev 5: feat: Text selection with keyboard.
  • Rev 4: As before, click sets the caret properly (curpos computed with node loop; would allow to corrected of string length changes by parser)
  • Rev 3: Fixed: Click sets now the caret properly. However, text selection are now deleted. Since the parse changes the string length at dollar, this needs to be taken into account.
  • Rev 0 (https://gist.github.com/mzechmeister/d0333d6e46746efc2f06e61547d69047/e4732a0a72b6bea5923081af66e7134dc0ca211a)

Problems of contenteditable:

  • mixes content and layout, we want to edit to content only, the rendering should be done by parsing.
  • edit history tricky, needs obsolete document.execCommand and document.execCommand + insertHTML does not work.
  • different browser behaviour for caret position

@mzechmeister
Copy link
Author

To inspect codemirror search for the textarea element and inspect the attached events.

https://github.com/codemirror/CodeMirror/blob/c1941628cdda92c32977b3a26b0a0b70c9a8f9b5/src/input/TextareaInput.js#L224

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment