Come posso selezionare i nodi di testo con jQuery?


388

Vorrei ottenere tutti i nodi di testo discendenti di un elemento, come una raccolta jQuery. Qual è il modo migliore per farlo?

Risposte:


261

jQuery non ha una funzione conveniente per questo. È necessario combinare contents(), che darà solo nodi figlio ma include nodi di testo, con find(), che fornisce tutti gli elementi discendenti ma nessun nodo di testo. Ecco cosa mi è venuto in mente:

var getTextNodesIn = function(el) {
    return $(el).find(":not(iframe)").addBack().contents().filter(function() {
        return this.nodeType == 3;
    });
};

getTextNodesIn(el);

Nota: se si utilizza jQuery 1.7 o precedente, il codice sopra non funzionerà. Per risolvere questo problema, sostituirlo addBack()con andSelf(). andSelf()è deprecato a favore addBack()dell'1,8 in poi.

Questo è in qualche modo inefficiente rispetto ai metodi DOM puri e deve includere una brutta soluzione per il sovraccarico della sua contents()funzione di jQuery (grazie a @rabidsnail nei commenti per averlo sottolineato), quindi ecco una soluzione non jQuery che utilizza una semplice funzione ricorsiva. Il includeWhitespaceNodesparametro controlla se i nodi di testo degli spazi bianchi sono inclusi nell'output (in jQuery vengono automaticamente filtrati).

Aggiornamento: corretto bug quando includeWhitespaceNodes è errato.

function getTextNodesIn(node, includeWhitespaceNodes) {
    var textNodes = [], nonWhitespaceMatcher = /\S/;

    function getTextNodes(node) {
        if (node.nodeType == 3) {
            if (includeWhitespaceNodes || nonWhitespaceMatcher.test(node.nodeValue)) {
                textNodes.push(node);
            }
        } else {
            for (var i = 0, len = node.childNodes.length; i < len; ++i) {
                getTextNodes(node.childNodes[i]);
            }
        }
    }

    getTextNodes(node);
    return textNodes;
}

getTextNodesIn(el);

L'elemento passato, può essere il nome di un div?
crosenblum,

@crosenblum: potresti chiamare per document.getElementById()primo, se è questo che intendi:var div = document.getElementById("foo"); var textNodes = getTextNodesIn(div);
Tim Down

A causa di un bug in jQuery se hai degli iframe in el dovrai usare .find (': not (iframe)') invece di .find ('*').
bobpoekert,

@rabidsnail: Penso che l'uso di .contents()comunque implica che cercherà anche attraverso l'iframe. Non vedo come potrebbe essere un bug.
Robin Maben,

bugs.jquery.com/ticket/11275 Se questo è effettivamente un bug sembra essere oggetto di dibattito, ma bug o no se si chiama find ('*'). contents () su un nodo che contiene un iframe che non ha stato aggiunto al dom otterrai un'eccezione in un punto indefinito.
bobpoekert,

209

Jauco ha pubblicato una buona soluzione in un commento, quindi la sto copiando qui:

$(elem)
  .contents()
  .filter(function() {
    return this.nodeType === 3; //Node.TEXT_NODE
  });

34
attualmente $ (elem) .contents () .filter (function () {return this.nodeType == Node.TEXT_NODE;}); è abbastanza
Jauco,

37
IE7 non definisce il nodo globale, quindi è necessario utilizzare this.nodeType == 3, purtroppo: stackoverflow.com/questions/1423599/node-textnode-and-ie7
Christian Oudard

17
Questo non restituisce solo i nodi di testo che sono i figli diretti dell'elemento piuttosto che i discendenti dell'elemento come richiesto dall'OP?
Tim Down,

7
questo non funzionerà quando il nodo di testo è annidato in profondità all'interno di altri elementi, poiché il metodo contents () restituisce solo i nodi secondari
minhajul

1
@Jauco, no, non abbastanza! come .contents () restituisce solo i nodi figlio immediati
minhajul,

17
$('body').find('*').contents().filter(function () { return this.nodeType === 3; });

6

jQuery.contents()può essere utilizzato con jQuery.filterper trovare tutti i nodi di testo figlio. Con una piccola svolta, puoi trovare anche i nodi di testo dei nipoti. Nessuna ricorsione richiesta:

$(function() {
  var $textNodes = $("#test, #test *").contents().filter(function() {
    return this.nodeType === Node.TEXT_NODE;
  });
  /*
   * for testing
   */
  $textNodes.each(function() {
    console.log(this);
  });
});
div { margin-left: 1em; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="test">
  child text 1<br>
  child text 2
  <div>
    grandchild text 1
    <div>grand-grandchild text 1</div>
    grandchild text 2
  </div>
  child text 3<br>
  child text 4
</div>

jsFiddle


4

Stavo ottenendo molti nodi di testo vuoti con la funzione di filtro accettata. Se sei interessato solo a selezionare nodi di testo che contengono spazi non bianchi, prova ad aggiungere un nodeValuecondizionale alla tua filterfunzione, come un semplice $.trim(this.nodevalue) !== '':

$('element')
    .contents()
    .filter(function(){
        return this.nodeType === 3 && $.trim(this.nodeValue) !== '';
    });

http://jsfiddle.net/ptp6m97v/

O per evitare strane situazioni in cui il contenuto appare come uno spazio bianco, ma non lo è (ad esempio il &shy;carattere trattino morbido , nuove righe \n, tabulazioni, ecc.), Puoi provare a usare un'espressione regolare. Ad esempio, \Scorrisponderà a tutti i caratteri non bianchi:

$('element')
        .contents()
        .filter(function(){
            return this.nodeType === 3 && /\S/.test(this.nodeValue);
        });

3

Se puoi supporre che tutti i bambini siano nodi Element o Text Nodes, allora questa è una soluzione.

Per ottenere tutti i nodi di testo figlio come una raccolta jquery:

$('selector').clone().children().remove().end().contents();

Per ottenere una copia dell'elemento originale con elementi secondari non di testo rimossi:

$('selector').clone().children().remove().end();

1
Ho appena notato il commento di Tim Down su un'altra risposta. Questa soluzione ottiene solo i bambini diretti, non tutti i discendenti.
Colllin,

2

Per qualche motivo contents()non ha funzionato per me, quindi se non ha funzionato per te, ecco una soluzione che ho creato, ho creato jQuery.fn.descendantscon l'opzione di includere nodi di testo o meno

uso


Ottieni tutti i discendenti inclusi nodi di testo e nodi di elementi

jQuery('body').descendants('all');

Ottieni tutti i discendenti che restituiscono solo nodi di testo

jQuery('body').descendants(true);

Ottieni tutti i discendenti che restituiscono solo nodi di elementi

jQuery('body').descendants();

Coffeescript Original :

jQuery.fn.descendants = ( textNodes ) ->

    # if textNodes is 'all' then textNodes and elementNodes are allowed
    # if textNodes if true then only textNodes will be returned
    # if textNodes is not provided as an argument then only element nodes
    # will be returned

    allowedTypes = if textNodes is 'all' then [1,3] else if textNodes then [3] else [1]

    # nodes we find
    nodes = []


    dig = (node) ->

        # loop through children
        for child in node.childNodes

            # push child to collection if has allowed type
            nodes.push(child) if child.nodeType in allowedTypes

            # dig through child if has children
            dig child if child.childNodes.length


    # loop and dig through nodes in the current
    # jQuery object
    dig node for node in this


    # wrap with jQuery
    return jQuery(nodes)

Inserisci nella versione Javascript

var __indexOf=[].indexOf||function(e){for(var t=0,n=this.length;t<n;t++){if(t in this&&this[t]===e)return t}return-1}; /* indexOf polyfill ends here*/ jQuery.fn.descendants=function(e){var t,n,r,i,s,o;t=e==="all"?[1,3]:e?[3]:[1];i=[];n=function(e){var r,s,o,u,a,f;u=e.childNodes;f=[];for(s=0,o=u.length;s<o;s++){r=u[s];if(a=r.nodeType,__indexOf.call(t,a)>=0){i.push(r)}if(r.childNodes.length){f.push(n(r))}else{f.push(void 0)}}return f};for(s=0,o=this.length;s<o;s++){r=this[s];n(r)}return jQuery(i)}

Versione Javascript non modificata: http://pastebin.com/cX3jMfuD

Questo è cross browser, un piccolo Array.indexOfpolyfill è incluso nel codice.


1

Può anche essere fatto in questo modo:

var textContents = $(document.getElementById("ElementId").childNodes).filter(function(){
        return this.nodeType == 3;
});

Il codice sopra filtra i textNodes dai nodi figlio figlio diretti di un dato elemento.


1
... ma non tutti i nodi figlio discendenti (ad esempio un nodo di testo che è figlio di un elemento che è figlio dell'elemento originale).
Tim Down,

0

se vuoi rimuovere tutti i tag, prova questo

funzione:

String.prototype.stripTags=function(){
var rtag=/<.*?[^>]>/g;
return this.replace(rtag,'');
}

utilizzo:

var newText=$('selector').html().stripTags();

0

Per me, il vecchio .contents()sembra funzionare per restituire i nodi di testo, basta fare attenzione con i selettori in modo da sapere che saranno nodi di testo.

Ad esempio, questo ha avvolto tutto il contenuto testuale dei TD nella mia tabella con pretag e non ha avuto problemi.

jQuery("#resultTable td").content().wrap("<pre/>")

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.