Utilizzo di .text () per recuperare solo testo non nidificato nei tag figlio


386

Se ho HTML come questo:

<li id="listItem">
    This is some text
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>

Sto cercando di usare .text()per recuperare solo la stringa "Questo è un po 'di testo", ma se dovessi dire $('#list-item').text(), ottengo "Questo è un po' di testoPrima span textSeconda span text".

C'è un modo per ottenere (e possibilmente rimuovere, tramite qualcosa di simile .text("")) solo il testo libero all'interno di un tag e non il testo all'interno dei tag secondari?

L'HTML non è stato scritto da me, quindi questo è ciò con cui devo lavorare. So che sarebbe semplice avvolgere il testo nei tag quando si scrive l'html, ma di nuovo, l'html è pre-scritto.


Perché non ho ancora abbastanza reputazione per commentare e non desidero che la conoscenza vada persa (speriamo che aiuti qualcun altro), una combinazione di macio.Jun 'risposta , un RegExp e la risposta di iStranger per sostituire un textNode con HTML in Javascript? mi ha permesso di cercare una stringa di nodi di solo testo e sostituire tutte le occorrenze con collegamenti.
JDQ,

Risposte:


509

Mi è piaciuta questa implementazione riutilizzabile basata sul clone()metodo trovato qui per ottenere solo il testo all'interno dell'elemento genitore.

Codice fornito per un facile riferimento:

$("#foo")
    .clone()    //clone the element
    .children() //select all the children
    .remove()   //remove all the children
    .end()  //again go back to selected element
    .text();

5
Con questa soluzione ottieni solo il testo senza il bambino, ma non puoi sostituire solo il testo.
BenRoe,

1
Non capisco 1 cosa: se .end () ritorna all'elemento selezionato, allora text () dovrebbe copiare il testo originale con elementi figlio. Ma in pratica vedo che il testo del nostro clone manipolato viene copiato. Quindi end () torna a clone ()?

68
Questo è un modo davvero inefficiente per farlo
billyonecan

5
@billyonecan, puoi suggerire un metodo più efficiente? Questo è attraente perché è "pulito" e "corto". Che cosa suggerisci?
derekmx271,

1
@ derekmx271 dai un'occhiata alla risposta di Stuart
billyonecan,

364

Risposta semplice:

$("#listItem").contents().filter(function(){ 
  return this.nodeType == 3; 
})[0].nodeValue = "The text you want to replace with" 

38
Non capisco perché le risposte efficienti (che non generano strutture di dati estranee) non vengano votate tanto quanto le risposte che sembrano meno spaventose. +5 se potessi.
Steven Lu,

16
la risposta semplice ed efficace
Paul Carroll,

9
Questo non è solo più efficiente ma anche corretto! Questa soluzione è adatta a situazioni in cui il testo è sparso tra elementi figlio. +5
Kyryll Tenin Baum

15
Per essere ancora più chiari, se usi IE8 +, puoi usare this.nodeType == Node.TEXT_NODEinvece di this.nodeType == 3. Più facile da leggere e comprendere IMO.
NorTicUs

8
Questo si interromperà se lo usi su qualcosa senza testo. Se si utilizza questa funzione come funzione e si ha uno scenario in cui è possibile o meno avere del testo, è sufficiente acquisire la .contents().filter(...)chiamata in una variabile locale e verificarne la lunghezza, ad es. var text = $(this).contents().filter(...); if (text.length) { return text[0].nodeValue; } return "";
Carl Bussema,

158

Mi sembra un caso di uso eccessivo di jquery. Quanto segue prenderà il testo ignorando gli altri nodi:

document.getElementById("listItem").childNodes[0];

Dovrai tagliarlo, ma ti darà quello che vuoi in una linea semplice.

MODIFICARE

Quanto sopra otterrà il nodo di testo . Per ottenere il testo effettivo, utilizzare questo:

document.getElementById("listItem").childNodes[0].nodeValue;

31
La migliore risposta, non dovresti aver bisogno di un plugin per questo o una catena di 10 chiamate jQuery. $('.foo')[0].childNodes[0].nodeValue.trim()
raine

5
cosa succede se il contenuto del testo è suddiviso in più nodi (come una sequenza di crlf, text, crlf)? ci sono (rael-life) garanzie che il dom costruito dall'UA utilizzerà la struttura più semplice?
collapsar

5
Totalmente la migliore risposta ... perché altre persone a volte usano jQuery?
ncubica,

11
Funziona solo nel caso di <div id = "listItem"> testo che vuoi <span> altro </span> </div>. Non funzionerà per <div id = "listItem"> <span> altro </span> testo che desideri </div>
Spencer

1
A volte non hai document. Sono venuto qui usando cheerio.
lampeggia il

67

Più facile e veloce:

$("#listItem").contents().get(0).nodeValue

Questo browser è compatibile?
Rajat Gupta,

Ovviamente recupera uno degli elementi corrispondenti all'oggetto jQuery fornito dall'indice: Jquery Docs .get () .
WakeupMorning

1
@Nate Nel caso sia necessario utilizzarlo su un tag <br/>, è possibile utilizzare la risposta di macio.Jun .
WakeupMorning,

Questa dovrebbe essere la risposta accettata.
Danny,

2
Perché get(0)invece di solo [0]?
Clonkex,

28

Simile alla risposta accettata, ma senza clonazione:

$("#foo").contents().not($("#foo").children()).text();

Ed ecco un plugin jQuery per questo scopo:

$.fn.immediateText = function() {
    return this.contents().not(this.children()).text();
};

Ecco come utilizzare questo plugin:

$("#foo").immediateText(); // get the text without children

Che cos'è t in t.children ()?
FrEaKmAn

Questa è una soluzione duplicata di quella che pbjk ha scritto nel 15 gennaio ... tuttavia - sembra carino.
Oskar Holmkratz,

1
Non proprio, @Oskar. La .contents()parte è fondamentale qui!
DUzun,

Soluzione errata se i nodi non utilizzano ID.
AndroidDev

3
@AndroidDev Puoi sempre sostituire il selettore con qualsiasi cosa funzioni per te. Questo è solo per illustrare la tecnica! Ho anche aggiunto una versione Plugin per mostrare che funziona anche senza ID
DUzun,

8

non è il codice:

var text  =  $('#listItem').clone().children().remove().end().text();

stai diventando jQuery per il bene di jQuery? Quando le operazioni semplici comportano tanti comandi concatenati e un'elaborazione così (non necessaria), forse è tempo di scrivere un'estensione jQuery:

(function ($) {
    function elementText(el, separator) {
        var textContents = [];
        for(var chld = el.firstChild; chld; chld = chld.nextSibling) {
            if (chld.nodeType == 3) { 
                textContents.push(chld.nodeValue);
            }
        }
        return textContents.join(separator);
    }
    $.fn.textNotChild = function(elementSeparator, nodeSeparator) {
    if (arguments.length<2){nodeSeparator="";}
    if (arguments.length<1){elementSeparator="";}
        return $.map(this, function(el){
            return elementText(el,nodeSeparator);
        }).join(elementSeparator);
    }
} (jQuery));

chiamare:

var text = $('#listItem').textNotChild();

gli argomenti sono nel caso in cui si verifichi uno scenario diverso, come ad esempio

<li>some text<a>more text</a>again more</li>
<li>second text<a>more text</a>again more</li>

var text = $("li").textNotChild(".....","<break>");

il testo avrà valore:

some text<break>again more.....second text<break>again more

1
Bello. Che ne dici di rendere questa una richiesta pull per la prossima versione di jQuery?
Jared Tomaszewski,

8

Prova questo:

$('#listItem').not($('#listItem').children()).text()

6

Dovrà essere qualcosa su misura per le esigenze, che dipendono dalla struttura che ti viene presentata. Nell'esempio che hai fornito, funziona:

$(document).ready(function(){
     var $tmp = $('#listItem').children().remove();
     $('#listItem').text('').append($tmp);
});

Demo: http://jquery.nodnod.net/cases/2385/run

Ma dipende abbastanza dal fatto che il markup sia simile a quello che hai pubblicato.


2
Lettore futuro attenzione: il codice in questa risposta uccide i bambini nell'elemento reale. Uno dovrebbe usare il clonemetodo qui se non è l'effetto desiderato.
Mahn

@ La risposta di DotNetWala, sotto, e dovrebbe essere usata al posto di questa. O almeno, utilizzare il .detach()metodo anziché .remove().
Don McCurdy,


4
jQuery.fn.ownText = function () {
    return $(this).contents().filter(function () {
        return this.nodeType === Node.TEXT_NODE;
    }).text();
};

1
Grazie per questo frammento di codice, che può fornire un aiuto immediato. Una spiegazione adeguata migliorerebbe notevolmente il suo valore educativo mostrando perché questa è una buona soluzione al problema e la renderebbe più utile ai futuri lettori con domande simili, ma non identiche. Si prega di modificare la risposta di aggiungere una spiegazione, e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
Toby Speight,

3

Questa è una vecchia domanda ma la risposta migliore è molto inefficiente. Ecco una soluzione migliore:

$.fn.myText = function() {
    var str = '';

    this.contents().each(function() {
        if (this.nodeType == 3) {
            str += this.textContent || this.innerText || '';
        }
    });

    return str;
};

E fai solo questo:

$("#foo").myText();

3

Presumo che questa sarebbe un'ottima soluzione anche - se si desidera ottenere il contenuto di tutti i nodi di testo che sono figli diretti dell'elemento selezionato.

$(selector).contents().filter(function(){ return this.nodeType == 3; }).text();

Nota: la documentazione di jQuery utilizza un codice simile per spiegare la funzione dei contenuti: https://api.jquery.com/contents/

PS C'è anche un modo un po 'più brutto per farlo, ma questo mostra più in profondità come funzionano le cose e consente un separatore personalizzato tra i nodi di testo (forse vuoi un'interruzione di riga lì)

$(selector).contents().filter(function(){ return this.nodeType == 3; }).map(function() { return this.nodeValue; }).toArray().join("");

1

Propongo di utilizzare createTreeWalker per trovare tutti gli elementi di testo non associati agli elementi html (questa funzione può essere utilizzata per estendere jQuery):

function textNodesOnlyUnder(el) {
  var resultSet = [];
  var n = null;
  var treeWalker  = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, function (node) {
    if (node.parentNode.id == el.id && node.textContent.trim().length != 0) {
      return NodeFilter.FILTER_ACCEPT;
    }
    return NodeFilter.FILTER_SKIP;
  }, false);
  while (n = treeWalker.nextNode()) {
    resultSet.push(n);
  }
  return resultSet;
}



window.onload = function() {
  var ele = document.getElementById('listItem');
  var textNodesOnly = textNodesOnlyUnder(ele);
  var resultingText = textNodesOnly.map(function(val, index, arr) {
    return 'Text element N. ' + index + ' --> ' + val.textContent.trim();
  }).join('\n');
  document.getElementById('txtArea').value = resultingText;
}
<li id="listItem">
    This is some text
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>
<textarea id="txtArea" style="width: 400px;height: 200px;"></textarea>


1

Se la posizione indexdel nodo di testo è fissa tra i suoi fratelli, è possibile utilizzare

$('parentselector').contents().eq(index).text()

1

Non sei sicuro di quanto sia flessibile o di quanti casi hai bisogno per coprire, ma per il tuo esempio, se il testo viene sempre prima dei primi tag HTML, perché non dividere semplicemente l'html interno nel primo tag e prendere il primo:

$('#listItem').html().split('<span')[0]; 

e se ne hai bisogno più largo, forse solo

$('#listItem').html().split('<')[0]; 

e se hai bisogno del testo tra due marcatori, come dopo una cosa ma prima di un'altra, puoi fare qualcosa di simile (non testato) e usare le istruzioni if ​​per renderlo abbastanza flessibile da avere un marcatore di inizio o fine o entrambi, evitando errori di riferimento null :

var startMarker = '';// put any starting marker here
var endMarker = '<';// put the end marker here
var myText = String( $('#listItem').html() );
// if the start marker is found, take the string after it
myText = myText.split(startMarker)[1];        
// if the end marker is found, take the string before it
myText = myText.split(endMarker)[0];
console.log(myText); // output text between the first occurrence of the markers, assuming both markers exist.  If they don't this will throw an error, so some if statements to check params is probably in order...

Generalmente creo funzioni di utilità per cose utili come questa, le faccio libere da errori e quindi faccio affidamento su di esse una volta solide, piuttosto che riscrivere sempre questo tipo di manipolazione di stringhe e rischiare riferimenti null ecc. In questo modo, puoi riutilizzare la funzione in molti progetti e non deve mai perdere altro tempo a eseguire il debug perché un riferimento a stringa ha un errore di riferimento indefinito. Potrebbe non essere il codice a 1 riga più corto di sempre, ma dopo avere la funzione di utilità, è una riga da allora in poi. Nota che la maggior parte del codice sta semplicemente gestendo i parametri presenti o meno per evitare errori :)

Per esempio:

/**
* Get the text between two string markers.
**/
function textBetween(__string,__startMark,__endMark){
    var hasText = typeof __string !== 'undefined' && __string.length > 0;
    if(!hasText) return __string;
    var myText = String( __string );
    var hasStartMarker = typeof __startMark !== 'undefined' && __startMark.length > 0 && __string.indexOf(__startMark)>=0;
    var hasEndMarker =  typeof __endMark !== 'undefined' && __endMark.length > 0 && __string.indexOf(__endMark) > 0;
    if( hasStartMarker )  myText = myText.split(__startMark)[1];
    if( hasEndMarker )    myText = myText.split(__endMark)[0];
    return myText;
}

// now with 1 line from now on, and no jquery needed really, but to use your example:
var textWithNoHTML = textBetween( $('#listItem').html(), '', '<'); // should return text before first child HTML tag if the text is on page (use document ready etc)

se è necessario sostituire il testo, basta usare $('#listItem').html( newHTML ); dove newHTML è una variabile che ha già il testo ridotto.
OG Sean,


0

Ho trovato una soluzione specifica che dovrebbe essere molto più efficiente della clonazione e della modifica del clone. Questa soluzione funziona solo con le seguenti due riserve, ma dovrebbe essere più efficiente della soluzione attualmente accettata:

  1. Stai ricevendo solo il testo
  2. Il testo che desideri estrarre è prima degli elementi figlio

Detto questo, ecco il codice:

// 'element' is a jQuery element
function getText(element) {
  var text = element.text();
  var childLength = element.children().text().length;
  return text.slice(0, text.length - childLength);
}

0

Proprio come la domanda, stavo cercando di testo estratto al fine di fare un po 'regex sostituzione del testo, ma è stato sempre problemi in cui i miei elementi interni (es: <i>, <div>, <span>, etc.) sono stati ottenendo anche rimossi.

Il seguente codice sembra funzionare bene e ha risolto tutti i miei problemi.

Utilizza alcune delle risposte fornite qui, ma in particolare sostituirà il testo solo quando l'elemento è di nodeType === 3.

$(el).contents().each(function() { 
  console.log(" > Content: %s [%s]", this, (this.nodeType === 3));

  if (this.nodeType === 3) {
    var text = this.textContent;
    console.log(" > Old   : '%s'", text);

    regex = new RegExp("\\[\\[" + rule + "\\.val\\]\\]", "g");
    text = text.replace(regex, value);

    regex = new RegExp("\\[\\[" + rule + "\\.act\\]\\]", "g");
    text = text.replace(regex, actual);

    console.log(" > New   : '%s'", text);
    this.textContent = text;
  }
});

Ciò che fa sopra è passare in rassegna tutti gli elementi del dato el(che è stato semplicemente ottenuto con $("div.my-class[name='some-name']");. Per ogni elemento interno, sostanzialmente li ignora. Per ogni porzione di testo (come determinato da if (this.nodeType === 3)) applicherà la sostituzione regex solo a quegli elementi .

La this.textContent = textporzione sostituisce semplicemente il testo sostituito, che nel mio caso, ero alla ricerca di gettoni come [[min.val]], [[max.val]]ecc

Questo estratto di codice breve aiuterà chiunque cerchi di fare ciò che la domanda stava ponendo ... e un po 'di più.


-1

inseriscilo semplicemente in <p>o <font>e prendi $ ('# listItem font'). text ()

La prima cosa che mi è venuta in mente

<li id="listItem">
    <font>This is some text</font>
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>

6
Non ho il controllo sul mettere il testo libero nei tag, perché il codice su cui sto lavorando non è stato creato da me. Se potessi prendere solo quel testo, potrei rimuoverlo e sostituirlo con tag attorno ad esso, o fare tutto quello che voglio. Ma ancora una volta, l'html è già pre-scritto.
MegaMatt

Ah ok. Quindi penso che dovrai filtrare i risultati: S scusa.
Dorjan,

-1

Puoi provare questo

alert(document.getElementById('listItem').firstChild.data)

-2

Utilizzare una condizione aggiuntiva per verificare se innerHTML e innerText sono uguali. Solo in questi casi, sostituisci il testo.

$(function() {
$('body *').each(function () {
    console.log($(this).html());
    console.log($(this).text());
    if($(this).text() === "Search" && $(this).html()===$(this).text())  {
        $(this).html("Find");
    }
})
})

http://jsfiddle.net/7RSGh/


-2

Per poter tagliare il risultato, usa DotNetWala in questo modo:

$("#foo")
    .clone()    //clone the element
    .children() //select all the children
    .remove()   //remove all the children
    .end()  //again go back to selected element
    .text()
    .trim();

Ho scoperto che l'uso della versione più breve come document.getElementById("listItem").childNodes[0]non funzionerà con trim () di jQuery.


3
Questo perché document.getElementById("listItem").childNodes[0]javascript è semplice, dovresti inserirlo nella funzione jQuery$(document.getElementById("listItem").childNodes[0]).trim()
Red Taz,

Va bene ha senso. Haha. Grazie!
Marion Go,

1
Questo è quasi identico alla risposta di DotNetWala . Tutto quello che hai fatto è stato aggiunto .trim()alla fine. Questa risposta è necessaria?
Tutti i lavoratori sono essenziali il

-3

Non sono un esperto di jquery, ma che ne dici di

$('#listItem').children().first().text()

1
Se noti un esperto jquery, allora perché non diventare più esperto leggendo prima le altre risposte? ... Uno di loro è accaduto praticamente uguale a quello che hai scritto, con i commenti qui sotto che spiegano perché non lo è una buona idea.
Oskar Holmkratz,

-4

Questo non testato, ma penso che potresti essere in grado di provare qualcosa del genere:

 $('#listItem').not('span').text();

http://api.jquery.com/not/


3
Perché è lo stesso di $('#listItem').text(). #listItemnon è quindi un <span>aggiunta not('span')non fa nulla.
Thomas Higginbotham,
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.