2008-12-11 18 views
7

Al seleccionar un bloque de texto (posiblemente abarcando muchos nodos DOM), ¿es posible extraer el texto y los nodos seleccionados usando Javascript?Obtener texto seleccionado y nodos seleccionados en una página?

Imagínese este código HTML:

<h1>Hello World</h1><p>Hi <b>there!</b></p> 

Si el usuario inicia un evento mouseDown a partir de las "World ..." y luego un mouseUp incluso justo después de "no!", Espero que volvería :

Text : { selectedText: "WorldHi there!" }, 
Nodes: [ 
    { node: "h1", offset: 6, length: 5 }, 
    { node: "p", offset: 0, length: 16 }, 
    { node: "p > b", offset: 0, length: 6 } 
] 

He intentado poner el HTML en un área de texto pero eso solo me dará el texto seleccionado. No he probado el elemento <canvas>, pero esa puede ser otra opción.

Si no es JavaScript, ¿hay alguna manera de hacerlo posible usando una extensión de Firefox?

Respuesta

12

Usted está en un viaje lleno de baches, pero esto es muy posible. El principal problema es que IE y W3C exponen interfaces completamente diferentes a las selecciones, por lo que si quieres una funcionalidad de navegador cruzado, básicamente debes escribir todo el asunto dos veces. Además, algunas funcionalidades básicas faltan en ambas interfaces.

La conexión de desarrollador de Mozilla tiene la historia en W3C selections. Microsoft tiene su sistema documented on MSDN. Recomiendo comenzar en PPK introduction to ranges.

Estas son algunas de las funciones básicas que creo que el trabajo:

// selection objects will differ between browsers 
function getSelection() { 
    return (msie) 
    ? document.selection 
    : (window.getSelection || document.getSelection)(); 
} 

// range objects will differ between browsers 
function getRange() { 
    return (msie) 
     ? getSelection().createRange() 
     : getSelection().getRangeAt(0) 
} 

// abstract getting a parent container from a range 
function parentContainer (range) { 
    return (msie) 
     ? range.parentElement() 
     : range.commonAncestorContainer; 
} 
+0

¿qué es lo que se pasa a parentContainer tan r? No funcionó (método parentContainer) –

+0

Whoops. No muy claro, pero ese debería ser un rango, he corregido el nombre de la variable. Estaba pensando que podría usarse así: var container = parentContainer (getRange()); Lo que no quiere decir que funcionará al 100%. El código pretende ser un ejemplo del tipo de trabajo que es necesario para hacer esto y puede ser deficiente. Querrá comprender las API con las que está tratando (ver enlaces). – Borgar

+0

'parentContainer()' no es útil: no se garantiza que las dos ramas devuelvan lo mismo porque el método 'parentElement()' de 'TextRange' de IE siempre devolverá un elemento, mientras que' commonAncestorContainer' podría ser un nodo de texto. Además, no es necesario tener ningún navegador de olfateo (como implica el uso de 'msie'): puede detectar fácilmente los objetos y métodos que necesita. –

0

Hay camino mucho más corto si lo que desea el rango.

function getRange(){ 
    return (navigator.appName=="Microsoft Internet Explorer") 
     ? document.selection.createRange().parentElement() 
     : (getSelection||document.getSelection)().getRangeAt(0).commonAncestorContainer 
} 
+1

Esto no es ideal. En primer lugar, el rastreo del navegador no es útil, ya que IE 9 y posterior son compatibles con las API estándar 'Selection' y' Range' y usted puede detectar las funciones que necesita directamente. En segundo lugar, no se garantiza que las dos ramas devuelvan lo mismo: el método 'parentElement()' de 'TextRange' de IE siempre devolverá un elemento, mientras que' commonAncestorContainer' podría ser un nodo de texto. En tercer lugar, la denominación es impar: lo que la función devuelve es un nodo, no un Rango. –

+0

Más corto no es necesariamente mejor. – displayname

7

Mi biblioteca Rangy conseguirá su parte del camino mediante la unificación de las diferentes APIs en IE < 9 y todos los demás navegadores principales, y proporcionando una función getNodes() en sus objetos Rango:

function getSelectedNodes() { 
    var selectedNodes = []; 
    var sel = rangy.getSelection(); 
    for (var i = 0; i < sel.rangeCount; ++i) { 
     selectedNodes = selectedNodes.concat(sel.getRangeAt(i).getNodes()); 
    } 
    return selectedNodes; 
} 

Obtener el texto seleccionado es bastante fácil en todos los navegadores. En Rangy es sólo

var selectedText = rangy.getSelection().toString(); 

Sin Rangy:

function getSelectedText() { 
    var sel, text = ""; 
    if (window.getSelection) { 
     text = "" + window.getSelection(); 
    } else if ((sel = document.selection) && sel.type == "Text") { 
     text = sel.createRange().text; 
    } 
    return text; 
} 

En cuanto a los desplazamientos de caracteres, puede hacer algo como esto para cualquier nodo node en la selección. Tenga en cuenta que esto no representa necesariamente el texto visible del documento porque no tiene en cuenta los espacios colapsados, el texto oculto mediante CSS, el texto colocado fuera del flujo normal de documentos mediante CSS, los saltos de línea implícitos en <br> y los elementos de bloque, más otras sutilezas.

var sel = rangy.getSelection(); 
var selRange = sel.getRangeAt(0); 
var rangePrecedingNode = rangy.createRange(); 
rangePrecedingNode.setStart(selRange.startContainer, selRange.startOffset); 
rangePrecedingNode.setEndBefore(node); 
var startIndex = rangePrecedingNode.toString().length; 
rangePrecedingNode.setEndAfter(node); 
var endIndex = rangePrecedingNode.toString().length; 
alert(startIndex + ", " + endIndex); 
3

Esto devuelve los nodos seleccionados como yo lo entiendo: Cuando tengo

<p> ... </p><p> ... </p><p> ... </p><p> ... </p><p> ... </p>... 
<p> ... </p><p> ... </p><p> ... </p><p> ... </p><p> ... </p> 

una gran cantidad de nodos y selecciono sólo unos pocos entonces quiero sólo estos nodos estén en la lista.

function getSelectedNodes() { 
    // from https://developer.mozilla.org/en-US/docs/Web/API/Selection 
    var selection = window.getSelection(); 
    if (selection.isCollapsed) { 
    return []; 
    }; 
    var node1 = selection.anchorNode; 
    var node2 = selection.focusNode; 
    var selectionAncestor = get_common_ancestor(node1, node2); 
    if (selectionAncestor == null) { 
    return []; 
    } 
    return getNodesBetween(selectionAncestor, node1, node2); 
} 

function get_common_ancestor(a, b) 
{ 
    // from http://stackoverflow.com/questions/3960843/how-to-find-the-nearest-common-ancestors-of-two-or-more-nodes 
    $parentsa = $(a).parents(); 
    $parentsb = $(b).parents(); 

    var found = null; 

    $parentsa.each(function() { 
     var thisa = this; 

     $parentsb.each(function() { 
      if (thisa == this) 
      { 
       found = this; 
       return false; 
      } 
     }); 

     if (found) return false; 
    }); 

    return found; 
} 

function isDescendant(parent, child) { 
    // from http://stackoverflow.com/questions/2234979/how-to-check-in-javascript-if-one-element-is-a-child-of-another 
    var node = child; 
    while (node != null) { 
     if (node == parent) { 
      return true; 
     } 
     node = node.parentNode; 
    } 
    return false; 
} 

function getNodesBetween(rootNode, node1, node2) { 
    var resultNodes = []; 
    var isBetweenNodes = false; 
    for (var i = 0; i < rootNode.childNodes.length; i+= 1) { 
    if (isDescendant(rootNode.childNodes[i], node1) || isDescendant(rootNode.childNodes[i], node2)) { 
     if (resultNodes.length == 0) { 
     isBetweenNodes = true; 
     } else { 
     isBetweenNodes = false; 
     } 
     resultNodes.push(rootNode.childNodes[i]); 
    } else if (resultNodes.length == 0) { 
    } else if (isBetweenNodes) { 
     resultNodes.push(rootNode.childNodes[i]); 
    } else { 
     return resultNodes; 
    } 
    }; 
if (resultNodes.length == 0) { 
    return [rootNode]; 
    } else if (isDescendant(resultNodes[resultNodes.length - 1], node1) || isDescendant(resultNodes[resultNodes.length - 1], node2)) { 
    return resultNodes; 
    } else { 
    // same child node for both should never happen 
    return [resultNodes[0]]; 
    } 
} 

El código debe estar disponible en: https://github.com/niccokunzmann/spiele-mit-kindern/blob/gh-pages/javascripts/feedback.js

he publicado esta respuesta aquí porque me hubiera gustado encontrar aquí.

0

Todos los códigos que cumplen con los estándares que funcionan en IE11 +.

cadena de texto

window.getSelection().getRangeAt(0).toString() 

El nodo inicio (incluso si el texto se selecciona al revés):

window.getSelection().anchorNode 

El nodo final (incluso si el texto se selecciona al revés):

window.getSelection().focusNode 

¿Desea saber más? Selecciona un texto y ejecute el siguiente JavaScript en la consola:

console.log(window.getSelection()); 
console.log(window.getSelection().getRangeAt(0)); 
Cuestiones relacionadas