2008-11-20 16 views
14

Estoy tratando de encontrar la manera de javascript para resaltar el texto que el usuario selecciona cuando hacen clic en un botón de resaltado impar (como en < span style = "background-color: yellow" > texto resaltado </lapso >). Sólo se tiene que trabajar, ya sea con WebKit o Firefox, pero parece ser casi imposible, ya que tiene que trabajar en los siguientes casos:javascript selección de usuario que resalta

<p>this is text</p> 
<p>I eat food</p> 

Cuando el usuario selecciona "es el texto" a través de "Yo como" en el navegador (no puede poner un lapso allí).

y este caso:

<span><span>this is text</span>middle text<span>this is text</span></span> 

Cuando el usuario selecciona "es el texto" a "esto es" en el navegador (aunque se puede envolver su punto culminante se extiende alrededor de cada elemento en la selección, I' Me gustaría ver cómo intentas resaltar ese texto intermedio.

Este problema no parece estar resuelto en ningún lado, francamente dudo que sea posible.

Sería posible si pudiera obtener el rango que obtiene de la selección como una cadena completa con html que podría ser analizada y luego reemplazada, pero por lo que puedo ver, no puede obtener el html crudo de un Rango ... lástima.

Respuesta

4
<html> 
<head> 
<script type="text/javascript"> 
function load(){ 
    window.document.designMode = "On"; 
    //run this in a button, will highlight selected text 
    window.document.execCommand("hiliteColor", false, "#000"); 
} 
</script> 
</head> 
<body contentEditable="true" onload="load()"> 
    this is text 
</body> 
</html> 
+0

Esto es fácilmente la mejor idea. Buena respuesta, pero realmente sería una idea volver a desactivar 'designMode' después. –

+0

Aunque las publicaciones son bastante antiguas, todavía veo que esta es la mejor solución que puedo encontrar en este momento – raoulinski

8

Bueno, puedes hacerlo usando DOM. Esto funciona en Firefox:

var selection = window.getSelection(); 
var range = selection.getRangeAt(0); 
var newNode = document.createElement("span"); 
newNode.setAttribute("style", "background-color: pink;"); 
range.surroundContents(newNode); 

Parece que funciona en la versión actual de Safari también. Ver https://developer.mozilla.org/en/DOM/range.surroundContents y http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html

+3

Esto no funciona si la selección cruza los límites del elemento (por ejemplo, si abarca varios párrafos). –

32

Esta respuesta es probablemente unos años demasiado tarde para ti, pero me enfrenté a un problema similar y quería documentarlo aquí, ya que es el primer éxito en google.

Para reiterar, la problema es que le gustaría simplemente capturar el objeto rango de la selección del usuario y lo rodean con un div estilo, así:

function highlightSelection() { 
    var userSelection = window.getSelection().getRangeAt(0); 
    highlightRange(userSelection); 

} 

function highlightRange(range) { 
    var newNode = document.createElement("div"); 
    newNode.setAttribute(
     "style", 
     "background-color: yellow; display: inline;" 
    ); 
    range.surroundContents(newNode); 
} 

Pero como estados parental original, esto es inseguro Funcionará si la selección no cruza los límites del elemento, pero arrojará un DOM eror si el rango creado por la selección del usuario es un rango inseguro que cruza los límites de las etiquetas HTML.


La solución es producir una matriz de objetos rango más pequeño, ninguna de las cuales cruza individualmente una barrera elemento, pero que cubren colectivamente el rango seleccionado por el usuario. Cada uno de estos rangos seguros se puede resaltar como arriba.

function getSafeRanges(dangerous) { 
    var a = dangerous.commonAncestorContainer; 
    // Starts -- Work inward from the start, selecting the largest safe range 
    var s = new Array(0), rs = new Array(0); 
    if (dangerous.startContainer != a) 
     for(var i = dangerous.startContainer; i != a; i = i.parentNode) 
      s.push(i) 
    ; 
    if (0 < s.length) for(var i = 0; i < s.length; i++) { 
     var xs = document.createRange(); 
     if (i) { 
      xs.setStartAfter(s[i-1]); 
      xs.setEndAfter(s[i].lastChild); 
     } 
     else { 
      xs.setStart(s[i], dangerous.startOffset); 
      xs.setEndAfter(
       (s[i].nodeType == Node.TEXT_NODE) 
       ? s[i] : s[i].lastChild 
      ); 
     } 
     rs.push(xs); 
    } 

    // Ends -- basically the same code reversed 
    var e = new Array(0), re = new Array(0); 
    if (dangerous.endContainer != a) 
     for(var i = dangerous.endContainer; i != a; i = i.parentNode) 
      e.push(i) 
    ; 
    if (0 < e.length) for(var i = 0; i < e.length; i++) { 
     var xe = document.createRange(); 
     if (i) { 
      xe.setStartBefore(e[i].firstChild); 
      xe.setEndBefore(e[i-1]); 
     } 
     else { 
      xe.setStartBefore(
       (e[i].nodeType == Node.TEXT_NODE) 
       ? e[i] : e[i].firstChild 
      ); 
      xe.setEnd(e[i], dangerous.endOffset); 
     } 
     re.unshift(xe); 
    } 

    // Middle -- the uncaptured middle 
    if ((0 < s.length) && (0 < e.length)) { 
     var xm = document.createRange(); 
     xm.setStartAfter(s[s.length - 1]); 
     xm.setEndBefore(e[e.length - 1]); 
    } 
    else { 
     return [dangerous]; 
    } 

    // Concat 
    rs.push(xm); 
    response = rs.concat(re);  

    // Send to Console 
    return response; 
} 

Es entonces posible (parecen) resaltar la selección del usuario, con este código modificado:

function highlightSelection() { 
    var userSelection = window.getSelection().getRangeAt(0); 
    var safeRanges = getSafeRanges(userSelection); 
    for (var i = 0; i < safeRanges.length; i++) { 
     highlightRange(safeRanges[i]); 
    } 
} 

Tenga en cuenta que '' probable que tenga un poco de CSS más elegante para hacer los muchos elementos dispares que un usuario podría verse bien juntos. ¡Espero que eventualmente esto ayude a otra alma cansada en internet!

+1

Gran pensamiento y código de ayuda +1 –

+1

Ojalá pudiera darte dos votos favorables por tu arduo trabajo en esto. –

1

Estaba teniendo el mismo problema hoy, destacando las etiquetas seleccionadas que abarcan varias etiquetas. La solución:

  1. Encontrar una manera de extraer la parte seleccionada junto con las etiquetas HTML.
  2. Envuelva la parte extraída con un elemento span y vuelva a colocarla en DOM.

Consulte el siguiente código para obtener más aclaraciones.

function getRangeObject(selectionObject){ 
    try{ 
     if(selectionObject.getRangeAt) 
      return selectionObject.getRangeAt(0); 
    } 
    catch(ex){ 
     console.log(ex); 
    } 
} 
document.onmousedown = function(e){ 
    var text; 
    if (window.getSelection) { 
     /* get the Selection object */ 
     userSelection = window.getSelection() 

     /* get the innerText (without the tags) */ 
     text = userSelection.toString(); 

     /* Creating Range object based on the userSelection object */ 
     var rangeObject = getRangeObject(userSelection); 

     /* 
      This extracts the contents from the DOM literally, inclusive of the tags. 
      The content extracted also disappears from the DOM 
     */ 
     contents = rangeObject.extractContents(); 

     var span = document.createElement("span"); 
     span.className = "highlight"; 
     span.appendChild(contents); 

     /* Insert your new span element in the same position from where the selected text was extracted */ 
     rangeObject.insertNode(span); 

    } else if (document.selection && document.selection.type != "Control") { 
      text = document.selection.createRange().text; 
    } 
}; 
4

Esta es mi primera vez no publicar aquí, pero mirando a través de sus respuestas, sería algo así como este trabajo? Tengo una muestra aquí: http://henriquedonati.com/projects/Extension/extension.html

function highlightSelection() { 
    var userSelection = window.getSelection(); 
    for(var i = 0; i < userSelection.rangeCount; i++) { 
     highlightRange(userSelection.getRangeAt(i)); 
    } 

} 

function highlightRange(range) { 
    var newNode = document.createElement("span"); 
    newNode.setAttribute(
     "style", 
     "background-color: yellow; display: inline;" 
    ); 
    range.surroundContents(newNode); 
} 
2

Aquí es un código completo para resaltar y dehighlight el texto

<!DOCTYPE html> 
    <html> 
     <head> 
      <style type="text/css"> 
       .highlight 
       { 
        background-color: yellow; 
       } 
       #test-text::-moz-selection { /* Code for Firefox */ 

        background: yellow; 
       } 

       #test-text::selection { 

        background: yellow; 
       } 

      </style> 
     </head> 

     <body> 
      <div id="div1" style="border: 1px solid #000;"> 
       <div id="test-text"> 
        <h1> Hello How are you </h1> 
        <p > 
         Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. 
        </p> 
       </div> 
      </div> 
      <br /> 

     </body> 
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> 
      <script type="text/javascript"> 
       mouseXPosition = 0; 
       $(document).ready(function() { 

        $("#test-text").mousedown(function (e1) { 
         mouseXPosition = e1.pageX;//register the mouse down position 
        }); 

        $("#test-text").mouseup(function (e2) { 
         var highlighted = false; 
         var selection = window.getSelection(); 
         var selectedText = selection.toString(); 
         var startPoint = window.getSelection().getRangeAt(0).startOffset; 
         var endPoint = window.getSelection().getRangeAt(0).endOffset; 
         var anchorTag = selection.anchorNode.parentNode; 
         var focusTag = selection.focusNode.parentNode; 
         if ((e2.pageX - mouseXPosition) < 0) { 
          focusTag = selection.anchorNode.parentNode; 
          anchorTag = selection.focusNode.parentNode; 
         } 
         if (selectedText.length === (endPoint - startPoint)) { 
          highlighted = true; 

          if (anchorTag.className !== "highlight") { 
           highlightSelection(); 
          } else { 
           var afterText = selectedText + "<span class = 'highlight'>" + anchorTag.innerHTML.substr(endPoint) + "</span>"; 
           anchorTag.innerHTML = anchorTag.innerHTML.substr(0, startPoint); 
           anchorTag.insertAdjacentHTML('afterend', afterText); 
          } 

         }else{ 
          if(anchorTag.className !== "highlight" && focusTag.className !== "highlight"){ 
           highlightSelection(); 
           highlighted = true; 
          } 

         } 


         if (anchorTag.className === "highlight" && focusTag.className === 'highlight' && !highlighted) { 
          highlighted = true; 

          var afterHtml = anchorTag.innerHTML.substr(startPoint); 
          var outerHtml = selectedText.substr(afterHtml.length, selectedText.length - endPoint - afterHtml.length); 
          var anchorInnerhtml = anchorTag.innerHTML.substr(0, startPoint); 
          var focusInnerHtml = focusTag.innerHTML.substr(endPoint); 
          var focusBeforeHtml = focusTag.innerHTML.substr(0, endPoint); 
          selection.deleteFromDocument(); 
          anchorTag.innerHTML = anchorInnerhtml; 
          focusTag.innerHTml = focusInnerHtml; 
          var anchorafterHtml = afterHtml + outerHtml + focusBeforeHtml; 
          anchorTag.insertAdjacentHTML('afterend', anchorafterHtml); 


         } 

         if (anchorTag.className === "highlight" && !highlighted) { 
          highlighted = true; 
          var Innerhtml = anchorTag.innerHTML.substr(0, startPoint); 
          var afterHtml = anchorTag.innerHTML.substr(startPoint); 
          var outerHtml = selectedText.substr(afterHtml.length, selectedText.length); 
          selection.deleteFromDocument(); 
          anchorTag.innerHTML = Innerhtml; 
          anchorTag.insertAdjacentHTML('afterend', afterHtml + outerHtml); 
         } 

         if (focusTag.className === 'highlight' && !highlighted) { 
          highlighted = true; 
          var beforeHtml = focusTag.innerHTML.substr(0, endPoint); 
          var outerHtml = selectedText.substr(0, selectedText.length - beforeHtml.length); 
          selection.deleteFromDocument(); 
          focusTag.innerHTml = focusTag.innerHTML.substr(endPoint); 
          outerHtml += beforeHtml; 
          focusTag.insertAdjacentHTML('beforebegin', outerHtml); 


         } 
         if (!highlighted) { 
          highlightSelection(); 
         } 
         $('.highlight').each(function(){ 
          if($(this).html() == ''){ 
           $(this).remove(); 
          } 
         }); 
         selection.removeAllRanges(); 
        }); 
       }); 

       function highlightSelection() { 
        var selection; 

        //Get the selected stuff 
        if (window.getSelection) 
         selection = window.getSelection(); 
        else if (typeof document.selection != "undefined") 
         selection = document.selection; 

        //Get a the selected content, in a range object 
        var range = selection.getRangeAt(0); 

        //If the range spans some text, and inside a tag, set its css class. 
        if (range && !selection.isCollapsed) { 
         if (selection.anchorNode.parentNode == selection.focusNode.parentNode) { 
          var span = document.createElement('span'); 
          span.className = 'highlight'; 
          span.textContent = selection.toString(); 
          selection.deleteFromDocument(); 
          range.insertNode(span); 
    //      range.surroundContents(span); 
         } 
        } 
       } 

      </script> 
    </html> 

https://jsfiddle.net/Bilalchk123/1o4j0w2v/

Cuestiones relacionadas