Javascript .querySelector trova <div> per innerTEXT


109

Come posso trovare DIV con un determinato testo? Per esempio:

<div>
SomeText, text continues.
</div>

Cercando di usare qualcosa del genere:

var text = document.querySelector('div[SomeText*]').innerTEXT;
alert(text);

Ma naturalmente non funzionerà. Come posso farlo?


Anche se potessi farlo, non sarebbe più veloce di ottenere tutti i div e filtrarli sulla proprietà innerText. Allora perché non lo fai manualmente.
Redu

Risposte:


100

La domanda di OP riguarda il semplice JavaScript e non jQuery . Sebbene ci siano molte risposte e mi piace la risposta di @Pawan Nogariya , controlla questa alternativa.

Puoi usare XPATH in JavaScript. Maggiori informazioni sull'articolo MDN qui .

Il document.evaluate()metodo valuta una query / espressione XPATH. Quindi puoi passare le espressioni XPATH lì, attraversare il documento HTML e individuare l'elemento desiderato.

In XPATH puoi selezionare un elemento, dal nodo di testo come il seguente, che ottiene il divche ha il seguente nodo di testo.

//div[text()="Hello World"]

Per ottenere un elemento che contiene del testo usa quanto segue:

//div[contains(., 'Hello')]

Il contains()metodo in XPATH accetta un nodo come primo parametro e il testo da cercare come secondo parametro.

Controlla questo plunk qui , questo è un esempio di utilizzo di XPATH in JavaScript

Ecco uno snippet di codice:

var headings = document.evaluate("//h1[contains(., 'Hello')]", document, null, XPathResult.ANY_TYPE, null );
var thisHeading = headings.iterateNext();

console.log(thisHeading); // Prints the html element in console
console.log(thisHeading.textContent); // prints the text content in console

thisHeading.innerHTML += "<br />Modified contents";  

Come puoi vedere, posso prendere l'elemento HTML e modificarlo come mi piace.


Grazie! Funziona alla grande! Ma come "console.log" il "thisHeading.textContent" se ho bisogno di prendere solo una parola da questo testo? Ad esempio: '// div [contains (., \' / You login (. *) Times this session / \ ')]' and then alert (thisHeading.textContent. $ 1)
passwd

Ok, lo faccio in questo modo:alert(thisHeading.textContent.replace(/.*You have login (.*) times.*/,'$1')) ;
passwd

@passwd, beh, non puoi farlo. Regex non è supportato in XPATH 1.0 (che .evaluate()utilizza. Per favore qualcuno mi corregga se sbaglio), quindi in primo luogo, non puoi cercare qualcosa che corrisponda a un'espressione regolare. In secondo luogo, la .textContentproprietà restituisce il nodo di testo dell'elemento. Se vuoi estrarre un valore da questo testo dovresti gestirlo esplicitamente, probabilmente creando una sorta di funzione che corrisponde a un'espressione regolare e restituisce il valore corrispondente nel gruppo.Per questo fai una nuova domanda su un thread separato.
gdyrrahitis

Internet Explorer: nessun supporto. Ma supportato in Edge. Non sono sicuro di cosa significhi, dal punto di vista della versione.
Rolf

come va gestito un errore nel caso in cui l'elemento che sto cercando manca?
nenito

72

Potresti usare questa soluzione piuttosto semplice:

Array.from(document.querySelectorAll('div'))
  .find(el => el.textContent === 'SomeText, text continues.');
  1. Il Array.fromconvertirà il NodeList in un array (ci sono più metodi per farlo come l'operatore spread o slice)

  2. Il risultato che ora è un array consente di utilizzare il Array.findmetodo, quindi è possibile inserire qualsiasi predicato. Puoi anche controllare textContent con una regex o qualsiasi altra cosa tu voglia.

Notare che Array.frome Array.findsono le caratteristiche di ES2015. Essere compatibile con i browser meno recenti come IE10 senza un transpiler:

Array.prototype.slice.call(document.querySelectorAll('div'))
  .filter(function (el) {
    return el.textContent === 'SomeText, text continues.'
  })[0];

2
Se desideri trovare più elementi, sostituisci findcon filter.
RubbelDieKatz

38

Dal momento che l'hai chiesto in javascript, puoi avere qualcosa del genere

function contains(selector, text) {
  var elements = document.querySelectorAll(selector);
  return Array.prototype.filter.call(elements, function(element){
    return RegExp(text).test(element.textContent);
  });
}

E poi chiamalo così

contains('div', 'sometext'); // find "div" that contain "sometext"
contains('div', /^sometext/); // find "div" that start with "sometext"
contains('div', /sometext$/i); // find "div" that end with "sometext", case-insensitive

1
Sembra che funzioni, ma in cambio ricevo solo questo:[object HTMLDivElement],[object HTMLDivElement]
passwd

Sì, riceverai i div con il testo corrispondente e quindi puoi chiamare il metodo del testo interno qualcosa del genere foundDivs[0].innerText, così semplice
Pawan Nogariya

20

Questa soluzione fa quanto segue:

  • Utilizza l'operatore di diffusione ES6 per convertire l'elenco dei nodi di tutti divi messaggi in un array.

  • Fornisce l'output se div contiene la stringa di query, non solo se è esattamente uguale alla stringa di query (cosa che accade per alcune delle altre risposte). Ad esempio, dovrebbe fornire l'output non solo per "SomeText" ma anche per "SomeText, testo continua".

  • Restituisce l'intero divcontenuto, non solo la stringa di query. Ad esempio, per "SomeText, il testo continua" dovrebbe restituire l'intera stringa, non solo "SomeText".

  • Consente a più messaggi divdi contenere la stringa, non solo uno div.

[...document.querySelectorAll('div')]      // get all the divs in an array
  .map(div => div.innerHTML)               // get their contents
  .filter(txt => txt.includes('SomeText')) // keep only those containing the query
  .forEach(txt => console.log(txt));       // output the entire contents of those
<div>SomeText, text continues.</div>
<div>Not in this div.</div>
<div>Here is more SomeText.</div>


3
Amo questo. Pulito, conciso e comprensibile - tutto allo stesso tempo.
ba_ul

2
Orribilmente inefficiente sicuramente? Pensa quanto innerHTMLè grande per i tuoi migliori <div>. Dovresti prima filtrare divi messaggi che contengono bambini. Anche il sospetto document.getElementsByTagName('div')potrebbe essere più veloce, ma farei un benchmark per essere sicuro.
Timmmm

Questo è fantastico per me, posso impostare un buon selettore all'inizio perché so già che può essere solo in un tavolo, bello, grazie
gsalgadotoledo

10

È meglio vedere se hai un elemento genitore del div che stai interrogando. In tal caso, prendi l'elemento genitore ed esegui un file element.querySelectorAll("div"). Una volta ottenuto, nodeListapplica un filtro sulla innerTextproprietà. Supponiamo che un elemento genitore del div che stiamo interrogando abbia un iddi container. Normalmente puoi accedere al container direttamente dall'id, ma facciamolo nel modo corretto.

var conty = document.getElementById("container"),
     divs = conty.querySelectorAll("div"),
    myDiv = [...divs].filter(e => e.innerText == "SomeText");

Quindi è così.


Questo ha funzionato per me, ma con innerHTML invece di innerText
Chase Sandmann

5

Se non vuoi usare jquery o qualcosa del genere, puoi provare questo:

function findByText(rootElement, text){
    var filter = {
        acceptNode: function(node){
            // look for nodes that are text_nodes and include the following string.
            if(node.nodeType === document.TEXT_NODE && node.nodeValue.includes(text)){
                 return NodeFilter.FILTER_ACCEPT;
            }
            return NodeFilter.FILTER_REJECT;
        }
    }
    var nodes = [];
    var walker = document.createTreeWalker(rootElement, NodeFilter.SHOW_TEXT, filter, false);
    while(walker.nextNode()){
       //give me the element containing the node
       nodes.push(walker.currentNode.parentNode);
    }
    return nodes;
}

//call it like
var nodes = findByText(document.body,'SomeText');
//then do what you will with nodes[];
for(var i = 0; i < nodes.length; i++){ 
    //do something with nodes[i]
} 

Una volta che hai i nodi in un array che contiene il testo puoi fare qualcosa con loro. Come avvisare ciascuno o stampare su console. Un avvertimento è che questo potrebbe non necessariamente afferrare i div di per sé, questo afferrerà il genitore del textnode che ha il testo che stai cercando.


3

Poiché non ci sono limiti alla lunghezza del testo in un attributo di dati, usa gli attributi di dati! E poi puoi usare i normali selettori CSS per selezionare i tuoi elementi come vuole l'OP.

for (const element of document.querySelectorAll("*")) {
  element.dataset.myInnerText = element.innerText;
}

document.querySelector("*[data-my-inner-text='Different text.']").style.color="blue";
<div>SomeText, text continues.</div>
<div>Different text.</div>

Idealmente, fai la parte dell'impostazione degli attributi dei dati sul caricamento del documento e restringi un po 'il selettore querySelectorAll per le prestazioni.


2

Google ha questo come risultato principale per coloro che hanno bisogno di trovare un nodo con un determinato testo. A titolo di aggiornamento, un nodelist è ora iterabile nei browser moderni senza doverlo convertire in un array.

La soluzione può usare forEach in questo modo.

var elList = document.querySelectorAll(".some .selector");
elList.forEach(function(el) {
    if (el.innerHTML.indexOf("needle") !== -1) {
        // Do what you like with el
        // The needle is case sensitive
    }
});

Questo ha funzionato per me per trovare / sostituire testo all'interno di un nodelist quando un normale selettore non poteva scegliere un solo nodo, quindi ho dovuto filtrare ogni nodo uno per uno per controllarlo per l'ago.


2

Usa XPath e document.evaluate () e assicurati di usare text () e non. per l'argomento contains (), altrimenti si avrà la corrispondenza dell'intero HTML o dell'elemento div più esterno.

var headings = document.evaluate("//h1[contains(text(), 'Hello')]", document, null, XPathResult.ANY_TYPE, null );

o ignora gli spazi iniziali e finali

var headings = document.evaluate("//h1[contains(normalize-space(text()), 'Hello')]", document, null, XPathResult.ANY_TYPE, null );

o corrisponde a tutti i tipi di tag (div, h1, p, ecc.)

var headings = document.evaluate("//*[contains(text(), 'Hello')]", document, null, XPathResult.ANY_TYPE, null );

Quindi iterare

let thisHeading;
while(thisHeading = headings.iterateNext()){
    // thisHeading contains matched node
}

Questo metodo può essere utilizzato per aggiungere una classe a un elemento? ad esempiothisheading.setAttribute('class', "esubject")
Matteo

Una volta che hai l'elemento, certo. Tuttavia, è meglio usare element.classList.add ("esubject") :)
Steven Spungin

1

Ecco l'approccio XPath ma con un minimo di gergo XPath.

Selezione regolare basata sui valori degli attributi dell'elemento (per confronto):

// for matching <element class="foo bar baz">...</element> by 'bar'
var things = document.querySelectorAll('[class*="bar"]');
for (var i = 0; i < things.length; i++) {
    things[i].style.outline = '1px solid red';
}

Selezione XPath basata sul testo all'interno dell'elemento.

// for matching <element>foo bar baz</element> by 'bar'
var things = document.evaluate('//*[contains(text(),"bar")]',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
for (var i = 0; i < things.snapshotLength; i++) {
    things.snapshotItem(i).style.outline = '1px solid red';
}

Ed ecco la mancanza di distinzione tra maiuscole e minuscole poiché il testo è più volatile:

// for matching <element>foo bar baz</element> by 'bar' case-insensitively
var things = document.evaluate('//*[contains(translate(text(),"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz"),"bar")]',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
for (var i = 0; i < things.snapshotLength; i++) {
    things.snapshotItem(i).style.outline = '1px solid red';
}

0

Ho avuto un problema simile.

Funzione che restituisce tutti gli elementi che includono testo da arg.

Questo funziona per me:

function getElementsByText(document, str, tag = '*') {
return [...document.querySelectorAll(tag)]
    .filter(
        el => (el.text && el.text.includes(str))
            || (el.children.length === 0 && el.outerText && el.outerText.includes(str)))

}


0

Ci sono già molte ottime soluzioni qui. Tuttavia, per fornire una soluzione più snella e un'altra in linea con l'idea del comportamento e della sintassi di querySelector, ho optato per una soluzione che estende Object con un paio di funzioni prototipo. Entrambe queste funzioni utilizzano espressioni regolari per la corrispondenza del testo, tuttavia, una stringa può essere fornita come parametro di ricerca libero.

Implementa semplicemente le seguenti funzioni:

// find all elements with inner text matching a given regular expression
// args: 
//      selector: string query selector to use for identifying elements on which we 
//                should check innerText
//      regex: A regular expression for matching innerText; if a string is provided,
//             a case-insensitive search is performed for any element containing the string.
Object.prototype.queryInnerTextAll = function(selector, regex) {
    if (typeof(regex) === 'string') regex = new RegExp(regex, 'i'); 
    const elements = [...this.querySelectorAll(selector)];
    const rtn = elements.filter((e)=>{
        return e.innerText.match(regex);
    });
    
    return rtn.length === 0 ? null : rtn
}

// find the first element with inner text matching a given regular expression
// args: 
//      selector: string query selector to use for identifying elements on which we 
//                should check innerText
//      regex: A regular expression for matching innerText; if a string is provided,
//             a case-insensitive search is performed for any element containing the string.
Object.prototype.queryInnerText = function(selector, text){
    return this.queryInnerTextAll(selector, text)[0];
}

Con queste funzioni implementate, ora puoi effettuare chiamate come segue:

  • document.queryInnerTextAll('div.link', 'go');
    Questo troverebbe tutti i div contenenti la classe di collegamento con la parola go nel testo interno (es. Vai a sinistra o VAI giù o vai a destra o È Go od )
  • document.queryInnerText('div.link', 'go');
    Funzionerebbe esattamente come nell'esempio precedente, tranne per il fatto che restituirebbe solo il primo elemento corrispondente.
  • document.queryInnerTextAll('a', /^Next$/);
    Trova tutti i collegamenti con il testo esatto Avanti (distingue tra maiuscole e minuscole). Ciò escluderà i collegamenti che contengono la parola Avanti insieme ad altro testo.
  • document.queryInnerText('a', /next/i);
    Trova il primo collegamento che contiene la parola successiva , indipendentemente dal caso (es. Pagina successiva o Vai a successiva )
  • e = document.querySelector('#page');
    e.queryInnerText('button', /Continue/);
    Esegue una ricerca all'interno di un elemento contenitore per un pulsante contenente il testo, Continua ( distingue tra maiuscole e minuscole). (ad es. Continua o Continua fino a Avanti ma non continuare )
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.