<div class="title">
   I am text node
   <a class="edit">Edit</a>
</div>

Deseo obtener el "Soy el nodo de texto", no deseo eliminar la etiqueta "editar" y necesito una solución de navegador cruzado.

106
Val 29 jun. 2011 a las 15:52

9 respuestas

La mejor respuesta
var text = $(".title").contents().filter(function() {
  return this.nodeType == Node.TEXT_NODE;
}).text();

Esto obtiene el contents del elemento seleccionado y le aplica una función de filtro. La función de filtro solo devuelve nodos de texto (es decir, aquellos nodos con nodeType == Node.TEXT_NODE).

76
Habeeb Perwad 20 feb. 2017 a las 06:14

Puede obtener el nodeValue del primer childNode usando

$('.title')[0].childNodes[0].nodeValue

http://jsfiddle.net/TU4FB/

52
Dogbert 29 jun. 2011 a las 11:58

Si quiere obtener el valor del primer nodo de texto en el elemento, este código funcionará:

var oDiv = document.getElementById("MyDiv");
var firstText = "";
for (var i = 0; i < oDiv.childNodes.length; i++) {
    var curNode = oDiv.childNodes[i];
    if (curNode.nodeName === "#text") {
        firstText = curNode.nodeValue;
        break;
    }
}

Puede ver esto en acción aquí: http://jsfiddle.net/ZkjZJ/

14
Shadow Wizard is Ear For You 29 jun. 2011 a las 11:58

Versión ES6 que devuelve el primer contenido del nodo #text

const extract = (node) => {
  const text = Array.from(node.childNodes).find(child => child.NodeType === Node.TEXT_NODE);
  return text && text.textContent.trim();
}
6
TylerH 29 may. 2019 a las 21:56

También puede usar la prueba de nodo text() de XPath para obtener solo los nodos de texto. Por ejemplo

var target = document.querySelector('div.title');
var iter = document.evaluate('text()', target, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
var node;
var want = '';

while (node = iter.iterateNext()) {
    want += node.data;
}
1
doubleDown 9 feb. 2016 a las 10:35

Otra solución nativa de JS que puede ser útil para elementos "complejos" o profundamente anidados es usar NodeIterator. Ponga NodeFilter.SHOW_TEXT como segundo argumento ("whatToShow") e itere solo sobre los nodos de texto secundarios del elemento.

var root = document.querySelector('p'),
    iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT),
    textnode;

// print all text nodes
while (textnode = iter.nextNode()) {
  console.log(textnode.textContent)
}
<p>
<br>some text<br>123
</p>

También puedes usar TreeWalker. La diferencia entre los dos es que NodeIterator es un iterador lineal simple, mientras que TreeWalker también te permite navegar a través de hermanos y antepasados.

11
vsync 3 dic. 2019 a las 11:52

.text() - for jquery

$('.title').clone()    //clone the element
.children() //select all the children
.remove()   //remove all the children
.end()  //again go back to selected element
.text();    //get the text of element
5
Pranay Rana 29 jun. 2011 a las 12:29

Esto también ignorará el espacio en blanco, por lo que nunca obtuvo el código de texto Nodes en blanco ... utilizando el núcleo de Javascript.

var oDiv = document.getElementById("MyDiv");
var firstText = "";
for (var i = 0; i < oDiv.childNodes.length; i++) {
    var curNode = oDiv.childNodes[i];
    whitespace = /^\s*$/;
    if (curNode.nodeName === "#text" && !(whitespace.test(curNode.nodeValue))) {
        firstText = curNode.nodeValue;
        break;
    }
}

Compruébelo en jsfiddle: - http://jsfiddle.net/webx/ZhLep/

2
Nakilon 23 ago. 2017 a las 18:35

JavaScript puro: minimalista

En primer lugar, siempre tenga esto en cuenta cuando busque texto en el DOM.

MDN - Espacio en blanco en el DOM

Este problema le hará prestar atención a la estructura de su XML / HTML.

En este ejemplo puro de JavaScript, considero la posibilidad de múltiples nodos de texto que podrían estar intercalados con otros tipos de nodos . Sin embargo, inicialmente, no juzgo los espacios en blanco, dejando esa tarea de filtrado a otro código.

En esta versión, paso un NodeList desde el código de llamada / cliente.

/**
* Gets strings from text nodes. Minimalist. Non-robust. Pre-test loop version.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @param nodeList The child nodes of a Node, as in node.childNodes.
* @param target A positive whole number >= 1
* @return String The text you targeted.
*/
function getText(nodeList, target)
{
    var trueTarget = target - 1,
        length = nodeList.length; // Because you may have many child nodes.

    for (var i = 0; i < length; i++) {
        if ((nodeList[i].nodeType === Node.TEXT_NODE) && (i === trueTarget)) {
            return nodeList[i].nodeValue;  // Done! No need to keep going.
        }
    }

    return null;
}

Por supuesto, al probar node.hasChildNodes() primero, no habría necesidad de usar un bucle de prueba previa for.

/**
* Gets strings from text nodes. Minimalist. Non-robust. Post-test loop version.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @param nodeList The child nodes of a Node, as in node.childNodes.
* @param target A positive whole number >= 1
* @return String The text you targeted.
*/
function getText(nodeList, target)
{
    var trueTarget = target - 1,
        length = nodeList.length,
        i = 0;

    do {
        if ((nodeList[i].nodeType === Node.TEXT_NODE) && (i === trueTarget)) {
            return nodeList[i].nodeValue;  // Done! No need to keep going.
         }

        i++;
    } while (i < length);

    return null;
}

JavaScript puro: robusto

Aquí la función getTextById() usa dos funciones auxiliares: getStringsFromChildren() y filterWhitespaceLines().


getStringsFromChildren ()

/**
* Collects strings from child text nodes.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @version 7.0
* @param parentNode An instance of the Node interface, such as an Element. object.
* @return Array of strings, or null.
* @throws TypeError if the parentNode is not a Node object.
*/
function getStringsFromChildren(parentNode)
{
    var strings = [],
        nodeList,
        length,
        i = 0;

    if (!parentNode instanceof Node) {
        throw new TypeError("The parentNode parameter expects an instance of a Node.");
    }

    if (!parentNode.hasChildNodes()) {
        return null; // We are done. Node may resemble <element></element>
    }

    nodeList = parentNode.childNodes;
    length = nodeList.length;

    do {
        if ((nodeList[i].nodeType === Node.TEXT_NODE)) {
            strings.push(nodeList[i].nodeValue);
         }

        i++;
    } while (i < length);

    if (strings.length > 0) {
        return strings;
    }

    return null;
}

filterWhitespaceLines ()

/**
* Filters an array of strings to remove whitespace lines.
* Generic, cross platform solution.
*
* @author Anthony Rutledge
* @version 6.0
* @param textArray a String associated with the id attribute of an Element.
* @return Array of strings that are not lines of whitespace, or null.
* @throws TypeError if the textArray param is not of type Array.
*/
function filterWhitespaceLines(textArray) 
{
    var filteredArray = [],
        whitespaceLine = /(?:^\s+$)/; // Non-capturing Regular Expression.

    if (!textArray instanceof Array) {
        throw new TypeError("The textArray parameter expects an instance of a Array.");
    }

    for (var i = 0; i < textArray.length; i++) {
        if (!whitespaceLine.test(textArray[i])) {  // If it is not a line of whitespace.
            filteredArray.push(textArray[i].trim());  // Trimming here is fine. 
        }
    }

    if (filteredArray.length > 0) {
        return filteredArray ; // Leave selecting and joining strings for a specific implementation. 
    }

    return null; // No text to return.
}

getTextById ()

/**
* Gets strings from text nodes. Robust.
* Generic, cross platform solution.
*
* @author Anthony Rutledge
* @version 6.0
* @param id A String associated with the id property of an Element.
* @return Array of strings, or null.
* @throws TypeError if the id param is not of type String.
* @throws TypeError if the id param cannot be used to find a node by id.
*/
function getTextById(id) 
{
    var textArray = null;             // The hopeful output.
    var idDatatype = typeof id;       // Only used in an TypeError message.
    var node;                         // The parent node being examined.

    try {
        if (idDatatype !== "string") {
            throw new TypeError("The id argument must be of type String! Got " + idDatatype);
        }

        node = document.getElementById(id);

        if (node === null) {
            throw new TypeError("No element found with the id: " + id);
        }

        textArray = getStringsFromChildren(node);

        if (textArray === null) {
            return null; // No text nodes found. Example: <element></element>
        }

        textArray = filterWhitespaceLines(textArray);

        if (textArray.length > 0) {
            return textArray; // Leave selecting and joining strings for a specific implementation. 
        }
    } catch (e) {
        console.log(e.message);
    }

    return null; // No text to return.
}

A continuación, el valor de retorno (Array o nulo) se envía al código del cliente donde debe manejarse. Con suerte, la matriz debería tener elementos de cadena de texto real, no líneas de espacios en blanco.

Las cadenas vacías ("") no se devuelven porque necesita un nodo de texto para indicar correctamente la presencia de texto válido. Volver ("") puede dar la falsa impresión de que existe un nodo de texto, lo que lleva a alguien a suponer que puede alterar el texto cambiando el valor de .nodeValue. Esto es falso, porque no existe un nodo de texto en el caso de una cadena vacía.

Ejemplo 1 :

<p id="bio"></p> <!-- There is no text node here. Return null. -->

Ejemplo 2 :

<p id="bio">

</p> <!-- There are at least two text nodes ("\n"), here. -->

El problema surge cuando desea que su HTML sea fácil de leer separándolo. Ahora, aunque no hay texto válido legible por humanos, todavía hay nodos de texto con caracteres de nueva línea ("\n") en sus propiedades .nodeValue.

Los humanos ven los ejemplos uno y dos como funcionalmente equivalentes: elementos vacíos que esperan ser llenados. El DOM es diferente al razonamiento humano. Es por esto que la función getStringsFromChildren() debe determinar si existen nodos de texto y reunir los valores .nodeValue en una matriz.

for (var i = 0; i < length; i++) {
    if (nodeList[i].nodeType === Node.TEXT_NODE) {
            textNodes.push(nodeList[i].nodeValue);
    }
}

En el ejemplo dos, existen dos nodos de texto y getStringFromChildren() devolverá el .nodeValue de ambos ("\n"). Sin embargo, filterWhitespaceLines() usa una expresión regular para filtrar líneas de caracteres de espacio en blanco puro.

¿Devolver los caracteres null en lugar de los de nueva línea ("\n") es una forma de mentirle al cliente / código de llamada? En términos humanos, no. En términos DOM, sí. Sin embargo, el problema aquí es obtener texto, no editarlo. No hay texto humano para volver al código de llamada.

Uno nunca puede saber cuántos caracteres de nueva línea pueden aparecer en el HTML de alguien. Crear un contador que busque el "segundo" carácter de nueva línea no es confiable. Puede que no exista.

Por supuesto, más adelante, el problema de editar texto en un elemento <p></p> vacío con espacios en blanco adicionales (ejemplo 2) podría significar destruir (tal vez, omitir) todos los nodos de texto menos uno las etiquetas de un párrafo para garantizar que el elemento contenga exactamente lo que se supone que debe mostrar.

Independientemente, a excepción de los casos en los que está haciendo algo extraordinario, necesitará una forma de determinar qué propiedad de nodo de texto .nodeValue tiene el texto verdadero y legible por humanos que desea editar. filterWhitespaceLines nos lleva a la mitad del camino.

var whitespaceLine = /(?:^\s+$)/; // Non-capturing Regular Expression.

for (var i = 0; i < filteredTextArray.length; i++) {
    if (!whitespaceLine.test(textArray[i])) {  // If it is not a line of whitespace.
        filteredTextArray.push(textArray[i].trim());  // Trimming here is fine. 
    }
}

En este punto, puede tener una salida similar a esta:

["Dealing with text nodes is fun.", "Some people just use jQuery."]

No hay garantía de que estas dos cadenas sean adyacentes entre sí en el DOM, por lo que unirlas con .join() podría hacer una composición antinatural. En cambio, en el código que llama a getTextById(), debe elegir con qué cadena desea trabajar.

Probar la salida.

try {
    var strings = getTextById("bio");

    if (strings === null) {
        // Do something.
    } else if (strings.length === 1) {
        // Do something with strings[0]
    } else { // Could be another else if
        // Do something. It all depends on the context.
    }
} catch (e) {
    console.log(e.message);
}

Se podría agregar .trim() dentro de getStringsFromChildren() para eliminar los espacios en blanco iniciales y finales (o para convertir un montón de espacios en una cadena de longitud cero (""), pero ¿cómo puede saber un a priori, lo que cada aplicación puede necesitar que le suceda al texto (cadena) una vez que se encuentra.

Puede haber ocasiones en las que este nivel de especificidad (target y tal) no sea obligatorio. Eso es genial Use una solución simple en esos casos. Sin embargo, un algoritmo generalizado le permite acomodar situaciones simples y complejas.

9
Anthony Rutledge 2 feb. 2020 a las 05:56