w3schools event javascript browser javascript-events cursor words

event - onclick javascript



¿Cómo obtener una palabra debajo del cursor usando JavaScript? (11)

Preámbulo:

Si tiene múltiples tramos y HTML anidado que separa palabras (o incluso caracteres en palabras), todas las soluciones anteriores tendrán problemas para devolver la palabra completa y correcta.

Aquí hay un ejemplo de la pregunta sobre recompensas: Х</span>rт0съ . Cómo devolver apropiadamente Хrт0съ ? Estos problemas no se abordaron en 2010, por lo que presentaré dos soluciones ahora (2015).

Solución 1 - Pele las etiquetas internas, cubra los tramos alrededor de cada palabra completa:

Una solución es eliminar las etiquetas span dentro de los párrafos, pero preservar su texto. Las palabras y frases divididas se unen de nuevo como texto regular. Cada palabra se encuentra mediante la división del espacio en blanco (no solo un espacio), y esas palabras están envueltas en tramos a los que se puede acceder de forma individual.

En la demostración, puede resaltar la palabra completa y así obtener el texto de la palabra completa.

Código:

$(function() { // Get the HTML in #hoverText - just a wrapper for convenience var $hoverText = $("#hoverText"); // Replace all spans inside paragraphs with their text $("p span", $hoverText).each(function() { var $this = $(this); var text = $this.text(); // get span content $this.replaceWith(text); // replace all span with just content }); // Wrap words in spans AND preserve the whitespace $("p", $hoverText).each(function() { var $this = $(this); var newText = $this.text().replace(/([/s])([^/s]+)/g, "$1<span>$2</span>"); newText = newText.replace(/^([^/s]+)/g, "<span>$1</span>"); $this.empty().append(newText); }); // Demo - bind hover to each span $(''#hoverText span'').hover( function() { $(this).css(''background-color'', ''#ffff66''); }, function() { $(this).css(''background-color'', ''''); } ); });

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div id="hoverText"> <p><span class="kinovar"><span id="selection_index3337" class="selection_index"></span>По f7-мъ часЁ твори1тъ сщ7eнникъ начaло съ кади1ломъ и3 со свэщeю, цrкимъ двeремъ tвeрзєннымъ, и3 поeтъ: Х</span>rт0съ воскRсе: <span class="kinovar">со стіхи2. И# по стісёхъ pал0мъ: Б</span>лгcви2 душE моS гDа: <span class="kinovar">И# є3ктеніA. Тaже каfjсма nбhчнаz.</span> </p> </div>

Solución 1 demostración de texto completo

Solución 2: inspección de Caret y cruce de DOM:

Aquí hay una solución más sofisticada. Es una solución algorítmica que utiliza cruce de nodos que captura con precisión la palabra completa y correcta debajo de un cursor en un nodo de texto.

Se encuentra una palabra temporal al marcar la posición de intercalación (usando caretPositionFromPoint o caretRangeFromPoint , acredita la idea a @chrisv). Esta puede o no ser la palabra completa, aún.

Luego se analiza para ver si está en cualquiera de los bordes de su nodo de texto (principio o final). Si es así, se examina el nodo de texto anterior o el siguiente nodo de texto para ver si se debe unir para hacer que esta palabra se fragmente más.

Ejemplo:

Х</span>rт0съ debe devolver Хrт0съ , no Х ni rт0съ .

El árbol DOM se recorre para obtener el siguiente nodo de texto no barrera. Si dos fragmentos de palabras están separados por una <p> o alguna otra etiqueta de barrera, entonces no son adyacentes y, por lo tanto, no forman parte de la misma palabra.

Ejemplo:

њб.)</p><p>Во no debería devolver њб.)Во

En la demostración, el div flotante izquierdo es la palabra debajo del cursor. El div flotante derecho, si es visible, muestra cómo se formó una palabra en un límite. Otras etiquetas se pueden alinear de forma segura con el texto en esta solución.

Código:

$(function() { // Get the HTML in #hoverText - just a wrapper for convenience var $hoverText = $("#hoverText"); // Get the full word the cursor is over regardless of span breaks function getFullWord(event) { var i, begin, end, range, textNode, offset; // Internet Explorer if (document.body.createTextRange) { try { range = document.body.createTextRange(); range.moveToPoint(event.clientX, event.clientY); range.select(); range = getTextRangeBoundaryPosition(range, true); textNode = range.node; offset = range.offset; } catch(e) { return ""; // Sigh, IE } } // Firefox, Safari // REF: https://developer.mozilla.org/en-US/docs/Web/API/Document/caretPositionFromPoint else if (document.caretPositionFromPoint) { range = document.caretPositionFromPoint(event.clientX, event.clientY); textNode = range.offsetNode; offset = range.offset; // Chrome // REF: https://developer.mozilla.org/en-US/docs/Web/API/document/caretRangeFromPoint } else if (document.caretRangeFromPoint) { range = document.caretRangeFromPoint(event.clientX, event.clientY); textNode = range.startContainer; offset = range.startOffset; } // Only act on text nodes if (!textNode || textNode.nodeType !== Node.TEXT_NODE) { return ""; } var data = textNode.textContent; // Sometimes the offset can be at the ''length'' of the data. // It might be a bug with this ''experimental'' feature // Compensate for this below if (offset >= data.length) { offset = data.length - 1; } // Ignore the cursor on spaces - these aren''t words if (isW(data[offset])) { return ""; } // Scan behind the current character until whitespace is found, or beginning i = begin = end = offset; while (i > 0 && !isW(data[i - 1])) { i--; } begin = i; // Scan ahead of the current character until whitespace is found, or end i = offset; while (i < data.length - 1 && !isW(data[i + 1])) { i++; } end = i; // This is our temporary word var word = data.substring(begin, end + 1); // Demo only showBridge(null, null, null); // If at a node boundary, cross over and see what // the next word is and check if this should be added to our temp word if (end === data.length - 1 || begin === 0) { var nextNode = getNextNode(textNode); var prevNode = getPrevNode(textNode); // Get the next node text if (end == data.length - 1 && nextNode) { var nextText = nextNode.textContent; // Demo only showBridge(word, nextText, null); // Add the letters from the next text block until a whitespace, or end i = 0; while (i < nextText.length && !isW(nextText[i])) { word += nextText[i++]; } } else if (begin === 0 && prevNode) { // Get the previous node text var prevText = prevNode.textContent; // Demo only showBridge(word, null, prevText); // Add the letters from the next text block until a whitespace, or end i = prevText.length - 1; while (i >= 0 && !isW(prevText[i])) { word = prevText[i--] + word; } } } return word; } // Return the word the cursor is over $hoverText.mousemove(function(e) { var word = getFullWord(e); if (word !== "") { $("#result").text(word); } }); }); // Helper functions // Whitespace checker function isW(s) { return /[ /f/n/r/t/v/u00A0/u2028/u2029]/.test(s); } // Barrier nodes are BR, DIV, P, PRE, TD, TR, ... function isBarrierNode(node) { return node ? /^(BR|DIV|P|PRE|TD|TR|TABLE)$/i.test(node.nodeName) : true; } // Try to find the next adjacent node function getNextNode(node) { var n = null; // Does this node have a sibling? if (node.nextSibling) { n = node.nextSibling; // Doe this node''s container have a sibling? } else if (node.parentNode && node.parentNode.nextSibling) { n = node.parentNode.nextSibling; } return isBarrierNode(n) ? null : n; } // Try to find the prev adjacent node function getPrevNode(node) { var n = null; // Does this node have a sibling? if (node.previousSibling) { n = node.previousSibling; // Doe this node''s container have a sibling? } else if (node.parentNode && node.parentNode.previousSibling) { n = node.parentNode.previousSibling; } return isBarrierNode(n) ? null : n; } // REF: http://stackoverflow.com/questions/3127369/how-to-get-selected-textnode-in-contenteditable-div-in-ie function getChildIndex(node) { var i = 0; while( (node = node.previousSibling) ) { i++; } return i; } // All this code just to make this work with IE, OTL // REF: http://stackoverflow.com/questions/3127369/how-to-get-selected-textnode-in-contenteditable-div-in-ie function getTextRangeBoundaryPosition(textRange, isStart) { var workingRange = textRange.duplicate(); workingRange.collapse(isStart); var containerElement = workingRange.parentElement(); var workingNode = document.createElement("span"); var comparison, workingComparisonType = isStart ? "StartToStart" : "StartToEnd"; var boundaryPosition, boundaryNode; // Move the working range through the container''s children, starting at // the end and working backwards, until the working range reaches or goes // past the boundary we''re interested in do { containerElement.insertBefore(workingNode, workingNode.previousSibling); workingRange.moveToElementText(workingNode); } while ( (comparison = workingRange.compareEndPoints( workingComparisonType, textRange)) > 0 && workingNode.previousSibling); // We''ve now reached or gone past the boundary of the text range we''re // interested in so have identified the node we want boundaryNode = workingNode.nextSibling; if (comparison == -1 && boundaryNode) { // This must be a data node (text, comment, cdata) since we''ve overshot. // The working range is collapsed at the start of the node containing // the text range''s boundary, so we move the end of the working range // to the boundary point and measure the length of its text to get // the boundary''s offset within the node workingRange.setEndPoint(isStart ? "EndToStart" : "EndToEnd", textRange); boundaryPosition = { node: boundaryNode, offset: workingRange.text.length }; } else { // We''ve hit the boundary exactly, so this must be an element boundaryPosition = { node: containerElement, offset: getChildIndex(workingNode) }; } // Clean up workingNode.parentNode.removeChild(workingNode); return boundaryPosition; } // DEMO-ONLY code - this shows how the word is recombined across boundaries function showBridge(word, nextText, prevText) { if (nextText) { $("#bridge").html("<span class=/"word/">" + word + "</span> | " + nextText.substring(0, 20) + "...").show(); } else if (prevText) { $("#bridge").html("..." + prevText.substring(prevText.length - 20, prevText.length) + " | <span class=/"word/">" + word + "</span>").show(); } else { $("#bridge").hide(); } }

.kinovar { color:red; font-size:20px;}.slavic { color: blue;}#result {top:10px;left:10px;}#bridge { top:10px; right:80px;}.floater { position: fixed; background-color:white; border:2px solid black; padding:4px;}.word { color:blue;}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div id="bridge" class="floater"></div> <div id="result" class="floater"></div> <div id="hoverText"><p><span class="kinovar"><span id="selection_index3337" class="selection_index"></span>По f7-мъ часЁ твори1тъ сщ7eнникъ начaло съ кади1ломъ и3 со свэщeю, цrкимъ двeремъ tвeрзєннымъ, и3 поeтъ: Х</span>rт0съ воскRсе: <span class="kinovar">со стіхи2. И# по стісёхъ pал0мъ: Б</span>лгcви2 душE моS гDа: <span class="kinovar">И# є3ктеніA. Тaже каfjсма nбhчнаz.</span></p><div class="slavic"> <input value="Works around other tags!"><p><span id="selection_index3737" class="selection_index"></span>(л. рo7з њб.)</p><p><span class="kinovar"><span id="selection_index3738" class="selection_index"></span>Во вт0рникъ вeчера</span> </p><p><span class="kinovar"><span id="selection_index3739" class="selection_index"></span>tдaніе прaздника пaсхи.</span></p><p><span class="kinovar"><span id="selection_index3740" class="selection_index"></span>По f7-мъ часЁ твори1тъ сщ7eнникъ начaло съ кади1ломъ и3 со свэщeю, цrкимъ двeремъ tвeрзєннымъ, и3 поeтъ: Х</span>rт0съ воскRсе: <span class="kinovar">со стіхи2. И# по стісёхъ pал0мъ: Б</span>лгcви2 душE моS гDа: <span class="kinovar">И# є3ктеніA. Тaже каfjсма nбhчнаz.<input value="Works around inline tags too"></span></p><p><span class="kinovar"><span id="selection_index3741" class="selection_index"></span>На ГDи воззвaхъ: поeмъ стіхи6ры самоглaсны, слэпaгw, на ѕ7. Глaсъ в7:</span></p></div>

( Nota: me tomé la libertad de aplicar estilos a las etiquetas span que estaban presentes en el HTML de muestra para iluminar dónde están los bordes del nodo de texto).

Solución 2 de demostración de texto completo

(Trabajando en Chrome e IE hasta el momento. Para IE, un método de IERange tuvo que usarse como una cuña para la compatibilidad entre navegadores)

Si yo, por ejemplo, tengo

<p> some long text </p>

en mi página HTML, ¿cómo puedo saber que el cursor del mouse está, por ejemplo, encima de la palabra ''texto''?


Además de las otras dos respuestas, puede dividir sus párrafos en tramos utilizando jQuery (o javascript en general).

De esta forma, no necesitaría pensar en dar salida a su texto con tramos alrededor de las palabras. Deja que tu javascript lo haga por ti.

p.ej

<p>Each word will be wrapped in a span.</p> <p>A second paragraph here.</p> Word: <span id="word"></span> <script type="text/javascript"> $(function() { // wrap words in spans $(''p'').each(function() { var $this = $(this); $this.html($this.text().replace(//b(/w+)/b/g, "<span>$1</span>")); }); // bind to each span $(''p span'').hover( function() { $(''#word'').text($(this).css(''background-color'',''#ffff66'').text()); }, function() { $(''#word'').text(''''); $(this).css(''background-color'',''''); } ); }); </script>

Tenga en cuenta que el código anterior, mientras funciona, eliminará cualquier html dentro de sus etiquetas de párrafo.

Ejemplo jsFiddle


Aquí está la solución para la recompensa.

Según lo sugerido por , puede usar document.caretRangeFromPoint (chrome) o document.caretPositionFromPoint (Firefox). Creo que esta solución responde mejor a su pregunta ya que no altera su texto o el DOM.

Esta función devuelve la palabra debajo del cursor del mouse sin alterar el DOM:

De la document.caretRangeFromPoint documentation :

El método caretRangeFromPoint () de la interfaz del documento devuelve un objeto Range para el fragmento del documento bajo las coordenadas especificadas.

De la document.caretPositionFromPoint documentation :

Este método se usa para recuperar la posición de intercalación en un documento basado en dos coordenadas. Se devuelve una CaretPosition, que contiene el nodo DOM encontrado y el desplazamiento de caracteres en ese nodo.

Las dos funciones son ligeramente diferentes, pero ambas devuelven el nodo que contiene el texto y el desplazamiento del cursor en este texto. Por lo tanto, es fácil usar la palabra debajo del mouse.

Vea el ejemplo completo:

$(function () { function getWordUnderCursor(event) { var range, textNode, offset; if (document.body.createTextRange) { // Internet Explorer try { range = document.body.createTextRange(); range.moveToPoint(event.clientX, event.clientY); range.select(); range = getTextRangeBoundaryPosition(range, true); textNode = range.node; offset = range.offset; } catch(e) { return ""; } } else if (document.caretPositionFromPoint) { // Firefox range = document.caretPositionFromPoint(event.clientX, event.clientY); textNode = range.offsetNode; offset = range.offset; } else if (document.caretRangeFromPoint) { // Chrome range = document.caretRangeFromPoint(event.clientX, event.clientY); textNode = range.startContainer; offset = range.startOffset; } //data contains a full sentence //offset represent the cursor position in this sentence var data = textNode.data, i = offset, begin, end; //Find the begin of the word (space) while (i > 0 && data[i] !== " ") { --i; }; begin = i; //Find the end of the word i = offset; while (i < data.length && data[i] !== " ") { ++i; }; end = i; //Return the word under the mouse cursor return data.substring(begin, end); } //Get the HTML in a div #hoverText and detect mouse move on it var $hoverText = $("#hoverText"); $hoverText.mousemove(function (e) { var word = getWordUnderCursor(e); //Show the word in a div so we can test the result if (word !== "") $("#testResult").text(word); }); }); // This code make it works with IE // REF: https://.com/questions/3127369/how-to-get-selected-textnode-in-contenteditable-div-in-ie function getTextRangeBoundaryPosition(textRange, isStart) { var workingRange = textRange.duplicate(); workingRange.collapse(isStart); var containerElement = workingRange.parentElement(); var workingNode = document.createElement("span"); var comparison, workingComparisonType = isStart ? "StartToStart" : "StartToEnd"; var boundaryPosition, boundaryNode; // Move the working range through the container''s children, starting at // the end and working backwards, until the working range reaches or goes // past the boundary we''re interested in do { containerElement.insertBefore(workingNode, workingNode.previousSibling); workingRange.moveToElementText(workingNode); } while ( (comparison = workingRange.compareEndPoints( workingComparisonType, textRange)) > 0 && workingNode.previousSibling); // We''ve now reached or gone past the boundary of the text range we''re // interested in so have identified the node we want boundaryNode = workingNode.nextSibling; if (comparison == -1 && boundaryNode) { // This must be a data node (text, comment, cdata) since we''ve overshot. // The working range is collapsed at the start of the node containing // the text range''s boundary, so we move the end of the working range // to the boundary point and measure the length of its text to get // the boundary''s offset within the node workingRange.setEndPoint(isStart ? "EndToStart" : "EndToEnd", textRange); boundaryPosition = { node: boundaryNode, offset: workingRange.text.length }; } else { // We''ve hit the boundary exactly, so this must be an element boundaryPosition = { node: containerElement, offset: getChildIndex(workingNode) }; } // Clean up workingNode.parentNode.removeChild(workingNode); return boundaryPosition; }

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <b><div id="testResult"></div></b> <div id="hoverText"> <p><span class="kinovar"><span id="selection_index3337" class="selection_index"></span>По f7-мъ часЁ твори1тъ сщ7eнникъ начaло съ кади1ломъ и3 со свэщeю, цrкимъ двeремъ tвeрзєннымъ, и3 поeтъ: Х</span>rт0съ воскRсе: <span class="kinovar">со стіхи2. И# по стісёхъ pал0мъ: Б</span>лгcви2 душE моS гDа: <span class="kinovar">И# є3ктеніA. Тaже каfjсма nбhчнаz.</span> </p> <div class="slavic"><p><span id="selection_index3737" class="selection_index"></span>(л. рo7з њб.)</p> <p><span class="kinovar"><span id="selection_index3738" class="selection_index"></span>Во вт0рникъ вeчера</span></p> <p><span class="kinovar"><span id="selection_index3739" class="selection_index"></span>tдaніе прaздника пaсхи.</span></p><p><span class="kinovar"><span id="selection_index3740" class="selection_index"></span>По f7-мъ часЁ твори1тъ сщ7eнникъ начaло съ кади1ломъ и3 со свэщeю, цrкимъ двeремъ tвeрзєннымъ, и3 поeтъ: Х</span>rт0съ воскRсе: <span class="kinovar">состіхи2. И# по стісёхъ pал0мъ: Б</span>лгcви2 душE моS гDа: <span class="kinovar">И# є3ктеніA. Тaже каfjсма nбhчнаz.</span> </p><p><span class="kinovar"><span id="selection_index3741" class="selection_index"></span>На ГDи воззвaхъ: поeмъ стіхи6ры самоглaсны, слэпaгw, на ѕ7. Глaсъ в7:</span> </p><p><span class="kinovar"><span id="selection_index3742" class="selection_index"></span>С</span>лэпhй роди1выйсz, въ своeмъ п0мыслэ глаг0лаше: є3дA ѓзъ грBхъ рaди роди1тельныхъ роди1хсz без8 џчію; (л. рo7и) є3дA ѓзъ за невёріе kзhкwвъ роди1хсz во њбличeніе; не домышлsюсz вопрошaти: когдA н0щь, когдA дeнь; не терпи1та ми2 н0зэ кaменнагw претыкaніz, не ви1дэхъ сlнца сіsюща, нижE во џбразэ менE создaвшагw. но молю1 ти сz хrтE б9е, при1зри на мS, и3 поми1луй мS.</p></div></div>


Aquí hay una solución simple que funciona en Chrome para la mayoría de los casos:

function getWordAtPoint(x, y) { var range = document.caretRangeFromPoint(x, y); if (range.startContainer.nodeType === Node.TEXT_NODE) { range.expand(''word''); return range.toString().trim(); } return null; }

Dejo de filtrar la puntuación y manejo correctamente las palabras con guiones como un ejercicio para el lector :).


Aw yiss! Aquí está ho!

Simple como es y sin Jquery o cualquier otro marco Fiddle: https://jsfiddle.net/703c96dr/

Pondrá tramos en cada palabra y agregará una función onmouseover y onomouseout. Podría crear una clase simple para que sea más útil, pero el código es tan simple que cualquiera puede editarlo y usarlo.

<p>This is my text example of word highlighting or, if you want, word hovering</p> <p>This is another text example of word highlighting or, if you want, word hovering</p>

Código simple

function onmouseoverspan(){ this.style.backgroundColor = "red"; } function onmouseoutspan(){ this.style.backgroundColor = "transparent"; } var spans,p = document.getElementsByTagName("p"); for(var i=0;i<p.length;i++) { if(p[i]==undefined) continue; p[i].innerHTML = p[i].innerHTML.replace(//b(/w+)/b/g, "<span>$1</span>"); spans = p[i].getElementsByTagName("span") for(var a=0;a<spans.length;a++) { spans[a].onmouseover = onmouseoverspan; spans[a].onmouseout = onmouseoutspan; } }


En Firefox puedes conectar el evento mousemove. La devolución de llamada tiene un argumento, e. En la devolución de llamada, haz esto:

var range = HTTparent.ownerDocument.createRange(); range.selectNode(e.rangeParent); var str = range.toString(); range.detach();

Ahora str tiene todo el texto que el mouse había terminado. e.rangeOffset es la ubicación del mousepointer dentro de esa cadena. En su caso, str sería "texto largo" y e.rangeOffset sería 11 si estuviera por encima de la "e" en "texto".

Este código se confundirá un poco si se encuentra en los márgenes, por ejemplo, cuando el puntero del mouse está en la misma línea que el texto, pero después de que termina. Para solucionar esto, debe verificar que esté realmente en la parte superior del texto. Aquí está la prueba:

if(e && e.rangeParent && e.rangeParent.nodeType == e.rangeParent.TEXT_NODE && e.rangeParent.parentNode == e.target)

Esta técnica funciona en Firefox. No funciona en Chrome


Hay una API para esto en el borrador actual de la vista de CSSOM : document.caretPositionFromPoint(x,y)

Sin embargo, debería comprobar qué navegador es compatible con esto. Parece que Firefox 7 no lo admite en absoluto, mientras que los informes de errores indican que Firefox 9 lo hará. Chrome 14 es compatible con caretRangeFromPoint(x,y) que es esencialmente el mismo, pero desde un borrador de CSSOM anterior.


Mi otra respuesta solo funciona en Firefox. Esta respuesta funciona en Chrome. (Podría funcionar en Firefox, también, no lo sé)

function getWordAtPoint(elem, x, y) { if(elem.nodeType == elem.TEXT_NODE) { var range = elem.ownerDocument.createRange(); range.selectNodeContents(elem); var currentPos = 0; var endPos = range.endOffset; while(currentPos+1 < endPos) { range.setStart(elem, currentPos); range.setEnd(elem, currentPos+1); if(range.getBoundingClientRect().left <= x && range.getBoundingClientRect().right >= x && range.getBoundingClientRect().top <= y && range.getBoundingClientRect().bottom >= y) { range.expand("word"); var ret = range.toString(); range.detach(); return(ret); } currentPos += 1; } } else { for(var i = 0; i < elem.childNodes.length; i++) { var range = elem.childNodes[i].ownerDocument.createRange(); range.selectNodeContents(elem.childNodes[i]); if(range.getBoundingClientRect().left <= x && range.getBoundingClientRect().right >= x && range.getBoundingClientRect().top <= y && range.getBoundingClientRect().bottom >= y) { range.detach(); return(getWordAtPoint(elem.childNodes[i], x, y)); } else { range.detach(); } } } return(null); }

En su manejador de mousemove, llame a getWordAtPoint(e.target, ex, ey);


Probablemente tendrías que dividir el párrafo para que cada palabra estuviera contenida dentro de su propio elemento <span> y luego agregar atributos de evento onmouseover a cada uno de ellos.

..Y creo que te refieres a "<p> texto extenso </ p>"; las barras invertidas no son parte de HTML.


Que yo sepa, no puedes.

Lo único que se me ocurre es poner cada una de las palabras en su propio elemento, luego aplicar el mouse sobre los eventos a esos elementos.

<p><span>Some</span> <span>long</span> <span>text</span></p> <script> $(document).ready(function () { $(''p span'').bind(''mouseenter'', function () { alert($(this).html() + " is what you''re currently hovering over!"); }); }); </script>


function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/"/g, "&quot;") .replace(/''/g, "&#039;"); } // REF: http://.com/questions/3127369/how-to-get-selected-textnode-in-contenteditable-div-in-ie function getChildIndex(node) { var i = 0; while( (node = node.previousSibling) ) { i++; } return i; } // All this code just to make this work with IE, OTL // REF: http://.com/questions/3127369/how-to-get-selected-textnode-in-contenteditable-div-in-ie function getTextRangeBoundaryPosition(textRange, isStart) { var workingRange = textRange.duplicate(); workingRange.collapse(isStart); var containerElement = workingRange.parentElement(); var workingNode = document.createElement("span"); var comparison, workingComparisonType = isStart ? "StartToStart" : "StartToEnd"; var boundaryPosition, boundaryNode; // Move the working range through the container''s children, starting at // the end and working backwards, until the working range reaches or goes // past the boundary we''re interested in do { containerElement.insertBefore(workingNode, workingNode.previousSibling); workingRange.moveToElementText(workingNode); } while ( (comparison = workingRange.compareEndPoints( workingComparisonType, textRange)) > 0 && workingNode.previousSibling); // We''ve now reached or gone past the boundary of the text range we''re // interested in so have identified the node we want boundaryNode = workingNode.nextSibling; if (comparison == -1 && boundaryNode) { // This must be a data node (text, comment, cdata) since we''ve overshot. // The working range is collapsed at the start of the node containing // the text range''s boundary, so we move the end of the working range // to the boundary point and measure the length of its text to get // the boundary''s offset within the node workingRange.setEndPoint(isStart ? "EndToStart" : "EndToEnd", textRange); boundaryPosition = { node: boundaryNode, offset: workingRange.text.length }; } else { // We''ve hit the boundary exactly, so this must be an element boundaryPosition = { node: containerElement, offset: getChildIndex(workingNode) }; } // Clean up workingNode.parentNode.removeChild(workingNode); return boundaryPosition; } function onClick(event) { var elt = document.getElementById(''info''); elt.innerHTML = ""; var textNode; var offset; // Internet Explorer if (document.body.createTextRange) { elt.innerHTML = elt.innerHTML+("*************** IE **************<br/>"); range = document.body.createTextRange(); range.moveToPoint(event.clientX, event.clientY); range.select(); range = getTextRangeBoundaryPosition(range, true); textNode = range.node; offset = range.offset; elt.innerHTML = elt.innerHTML + "IE ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; } // Internet Explorer method 2 if (document.body.createTextRange) { elt.innerHTML = elt.innerHTML+("*************** IE, Method 2 **************<br/>"); range = document.body.createTextRange(); range.moveToPoint(event.clientX, event.clientY); range.select(); var sel = document.getSelection(); textNode = sel.anchorNode; offset = sel.anchorOffset; elt.innerHTML = elt.innerHTML + "IE M2 ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; } // Firefox, Safari // REF: https://developer.mozilla.org/en-US/docs/Web/API/Document/caretPositionFromPoint if (document.caretPositionFromPoint) { elt.innerHTML = elt.innerHTML+("*************** Firefox, Safari **************<br/>"); range = document.caretPositionFromPoint(event.clientX, event.clientY); textNode = range.offsetNode; offset = range.offset; elt.innerHTML = elt.innerHTML + "caretPositionFromPoint ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; // Chrome // REF: https://developer.mozilla.org/en-US/docs/Web/API/document/caretRangeFromPoint } if (document.caretRangeFromPoint) { elt.innerHTML = elt.innerHTML+("*************** Chrome **************<br/>"); range = document.caretRangeFromPoint(event.clientX, event.clientY); textNode = range.startContainer; offset = range.startOffset; elt.innerHTML = elt.innerHTML + "caretRangeFromPoint ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; } } document.addEventListener(''click'', onClick);

#info { position: absolute; bottom: 0; background-color: cyan; }

<div class="parent"> <div class="child">SPACE&nbsp;SPACE Bacon ipsum dolor amet <span>SPAN SPANTT SPOOR</span> meatball bresaola t-bone tri-tip brisket. Jowl pig picanha cupim SPAXE landjaeger, frankfurter spare ribs chicken. Porchetta jowl pancetta drumstick shankle cow spare ribs jerky tail kevin biltong capicola brisket venison bresaola. Flank sirloin jowl andouille meatball venison salami ground round rump boudin turkey capicola t-bone. Sirloin filet mignon tenderloin beef, biltong doner bresaola brisket shoulder pork loin shankle turducken shank cow. Bacon ball tip sirloin ham. </div> <div id="info">Click somewhere in the paragraph above</div> </div>

Mi respuesta se deriva de la "Solución 2" de Drakes: inspección de Caret y cruce de DOM. ¡Muchas gracias a Drakes por señalar esta solución!

Sin embargo, hay dos problemas con la solución 2 de Drakes cuando se trabaja en IE. (1) el desplazamiento calculado es incorrecto, y (2) demasiado complejo, muchos códigos.

Vea mi demostración en JSFiddle here .

Para el problema 1, si hace clic en algún lugar aproximadamente en la última línea del texto, por ejemplo, en "hombro, lomo de cerdo, turducken, pierna, vaca, tocino, bola, punta, solomillo, jamón", puede notar que el cálculo de compensación es diferente con IE (original solución) e IE método 2 (mi solución). Además, los resultados del método IE 2 (mi solución) y de Chrome, Firefox son los mismos.

Mi solución también es mucho más simple. El truco es, después de utilizar TextRange para hacer la selección en la posición X / Y absoluta, obtener un tipo de IHTMLSelection llamando a document.getSelection ().Esto no funciona para IE <9, pero si eso está bien para ti, este método es mucho más simple. Otra advertencia es que, con IE, el efecto secundario del método (igual que el método original) es el cambio de selección (es decir, perder la selección original del usuario).

// Internet Explorer method 2 if (document.body.createTextRange) { elt.innerHTML = elt.innerHTML+("*************** IE, Method 2 **************<br/>"); range = document.body.createTextRange(); range.moveToPoint(event.clientX, event.clientY); range.select(); var sel = document.getSelection(); textNode = sel.anchorNode; offset = sel.anchorOffset; elt.innerHTML = elt.innerHTML + "IE M2 ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; }