Last active
January 3, 2022 21:00
-
-
Save mzechmeister/d0333d6e46746efc2f06e61547d69047 to your computer and use it in GitHub Desktop.
pseudo editable field (code mirror lite)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <style> | |
| #cursor { | |
| display: inline; | |
| background-color: #acf; | |
| } | |
| #cursor.collapsed { | |
| animation: blink 1s; | |
| outline: 1px solid red; | |
| animation-iteration-count: infinite; | |
| } | |
| span { | |
| color: green; | |
| padding: 5px; | |
| } | |
| span:before { | |
| content: "5"; | |
| vertical-align: super; | |
| font-size: smaller; | |
| } | |
| @keyframes blink { | |
| 50% { outline-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.$ Sometime mouse selection does not work.$</textarea> | |
| <div id="cursor"></div> | |
| <div id="mydiv" contenteditable onclick="let pos = [getPos(sel.anchorNode, sel.anchorOffset), getPos(sel.focusNode, sel.focusOffset)]; ta.setSelectionRange(...pos.sort()); ta.focus()"></div> | |
| <script> | |
| cursor = document.getElementById('cursor') | |
| function parse(t) { | |
| return t.replace(/\$/g, '<span>X</span>') | |
| } | |
| ta.oninput = e => { | |
| mydiv.innerHTML = parse(ta.value) | |
| } | |
| function pseudoCursor(nodeA, posA, node, pos) { | |
| let range = document.createRange() | |
| range.setStart(nodeA, posA) | |
| range.setEnd(node, pos) | |
| cursor.classList.toggle('collapsed', range.collapsed) | |
| range.insertNode(cursor) | |
| mydiv.normalize() | |
| } | |
| function getPos(node, offset) { | |
| let range = document.createRange() | |
| range.setStart(mydiv, 0) | |
| range.setEnd(node, offset) | |
| pos = range.toString().length | |
| return pos | |
| } | |
| function posi(offset) { | |
| mydiv.after(cursor) // avoid miscounting with inline cursor, normalisation can be skipped | |
| for (node of [...mydiv.childNodes]) { | |
| len = (node.nodeType == Node.TEXT_NODE ? node : node.innerText).length | |
| if (offset <= len) | |
| break | |
| offset -= len | |
| } | |
| return [node, offset] | |
| } | |
| function update() { | |
| // map ta selection to div selection | |
| selA = ta.selectionStart | |
| selB = ta.selectionEnd | |
| pseudoCursor(...posi(selA), ...posi(selB)) | |
| } | |
| sel = window.getSelection() | |
| document.addEventListener("selectionchange", function(e) { | |
| cursor.classList.remove("collapsed") | |
| if (document.activeElement == ta) update() | |
| }) | |
| // Firefox ~68 + enable dom.select_events.textcontrols.enabled | |
| ta.addEventListener("selectionchange", function(e) { | |
| update() | |
| e.stopPropagation() | |
| }) | |
| mydiv.innerHTML = parse(ta.value) | |
| </script> | |
| </body> | |
| </html> |
Author
Author
To inspect codemirror search for the textarea element and inspect the attached events.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
preview latest: https://gistpreview.github.io/?d0333d6e46746efc2f06e61547d69047
e.inputTypeto detect various events.insText.cutAandcut.divcutselrenamed tocutpos.sel.anchorNodereturnsbodynottain chrome and edge, instead usedocument.activeElement.range.toString().lengthinstead of node loop.fix backward selection (desktop only), double click selection not working
replaceAllnot working on some android, replaced byreplace+/ /g.mousedown+preventDefaultto keep focus on textarea, andrangeParentinstead ofselection. Now, build-in text selection on mobiles does not popup. There seems to be no redundantselectionchangecalls.onselectionchangeinstead of key events. Now pseudo caret is also moved for spacebar swiping on mobiles.onclickdoes not set the caret (firefox).Problems of contenteditable:
document.execCommandanddocument.execCommand+insertHTMLdoes not work.