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.