Mostrar un símbolo de intercalación en un área de texto personalizada sin mostrar su texto

Tengo un área de texto personalizado. En este ejemplo, hace que las letras sean rojas o verdes, al azar.

var mydiv = document.getElementById('mydiv'), myta = document.getElementById('myta'); function updateDiv() { var fc; while (fc = mydiv.firstChild) mydiv.removeChild(fc); for (var i = 0; i < myta.value.length; i++) { var span = document.createElement('span'); span.className = Math.random() < 0.5 ? 'green' : 'red'; span.appendChild(document.createTextNode(myta.value[i])); mydiv.appendChild(span); } }; myta.addEventListener('input', updateDiv); 
 body { position: relative } div, textarea { -webkit-text-size-adjust: none; width: 100%; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; font: 1rem sans-serif; padding: 2px; margin: 0; border-radius: 0; border: 1px solid #000; resize: none; } textarea { position: absolute; top: 0; color: transparent; background: transparent; } .red { color: #f00 } .green { color: #0f0 } 
 

Hay un div de salida con un área de texto sobre él. Por lo tanto, el área de texto no cubre ninguna de las cosas coloridas debajo de él, su color y fondo se establecen en transparente. Todo funciona aquí, excepto que el cursor (el cursor parpadeante proporcionado por el agente de usuario) es transparente.

¿Hay alguna manera de mostrar el cursor sin hacer visible el texto de la tabla de texto?

Si hago el div encima del textarea en su lugar y le doy el pointer-events: none , el textarea todavía es visible debajo. Estos arreglos también dificultan el desplazamiento suave, por lo que no funciona para mí.

¡Solo inserta tu propio cursor!

 function blink() { document.getElementById('caret').hidden ^= 1; blinkTimeout = setTimeout(blink, 500); } var mydiv = document.getElementById('mydiv'), myta = document.getElementById('myta'), blinkTimeout = setTimeout(blink, 500), lastSelectionStart = 0, lastSelectionEnd = 0, whichSelection = true; function updateDiv() { var fc; while (fc = mydiv.firstChild) mydiv.removeChild(fc); if (myta.selectionStart != lastSelectionStart) { lastSelectionStart = myta.selectionStart; whichSelection = false; } if (myta.selectionEnd != lastSelectionEnd) { lastSelectionEnd = myta.selectionEnd; whichSelection = true; } var cursorPos = whichSelection ? myta.selectionEnd : myta.selectionStart; for (var i = 0; i < myta.value.length; i++) { if (i == cursorPos) { var caret = document.createElement('span'); caret.id = 'caret'; caret.appendChild(document.createTextNode('\xA0')); mydiv.appendChild(caret); clearTimeout(blinkTimeout); blinkTimeout = setTimeout(blink, 500); } var span = document.createElement('span'); span.className = Math.random() < 0.5 ? 'green' : 'red'; span.appendChild(document.createTextNode(myta.value[i])); mydiv.appendChild(span); } if (myta.value.length == cursorPos) { var caret = document.createElement('span'); caret.id = 'caret'; caret.appendChild(document.createTextNode('\xA0')); mydiv.appendChild(caret); clearTimeout(blinkTimeout); blinkTimeout = setTimeout(blink, 500); } }; myta.addEventListener('input', updateDiv); myta.addEventListener('focus', updateDiv); myta.addEventListener('mousedown', function() { setTimeout(updateDiv, 0); }); myta.addEventListener('keydown', function() { setTimeout(updateDiv, 0); }); myta.addEventListener('blur', function() { document.getElementById('caret').hidden = true; clearTimeout(blinkTimeout); }); 
 body { position: relative } div, textarea { -webkit-text-size-adjust: none; width: 100%; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; font: 1rem sans-serif; padding: 2px; margin: 0; border-radius: 0; border: 1px solid #000; resize: none; } textarea { position: absolute; top: 0; color: transparent; background: transparent; } .red { color: #f00 } .green { color: #0f0 } #caret { display: inline-block; position: absolute; width: 1px; background: #000; } #caret[hidden] { display: none } 
 
 

¿Por qué no simplemente usar un

lugar ? Con esto no necesitas el área de textarea adicional. Vea una demostración aquí .

HTML:

 

JavaScript:

 var myta = document.getElementById('myta'); function updateDiv() { var fc; var text = myta.innerText || myta.textContent; while (fc = myta.firstChild) myta.removeChild(fc); for (var i = 0; i < text.length; i++) { var span = document.createElement('span'); span.className = Math.random() < 0.5 ? 'green' : 'red'; span.appendChild(document.createTextNode(text[i])); myta.appendChild(span); } placeCaretAtEnd(myta); }; myta.addEventListener('input', updateDiv); 

Además, para mover el símbolo de intercalación al final cuando colocas el nuevo texto dentro del div , utilicé esa función a partir de esta respuesta :

 function placeCaretAtEnd(el) { el.focus(); if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") { var range = document.createRange(); range.selectNodeContents(el); range.collapse(false); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } else if (typeof document.body.createTextRange != "undefined") { var textRange = document.body.createTextRange(); textRange.moveToElementText(el); textRange.collapse(false); textRange.select(); } }