Javascript .querySelector find <div> von innerTEXT

108

Wie finde ich DIV mit einem bestimmten Text? Beispielsweise:

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

Ich versuche so etwas zu benutzen:

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

Aber natürlich wird es nicht funktionieren. Wie kann ich es tun?

passwd
quelle
Selbst wenn Sie dies tun könnten, wäre es nicht schneller, als alle Divs abzurufen und über die innerText-Eigenschaft zu filtern. Warum machst du es nicht manuell?
Reduzieren Sie

Antworten:

99

Die Frage von OP bezieht sich auf einfaches JavaScript und nicht auf jQuery . Obwohl es viele Antworten gibt und ich die Antwort von @Pawan Nogariya mag, probieren Sie bitte diese Alternative aus.

Sie können XPATH in JavaScript verwenden. Weitere Infos zum MDN-Artikel hier .

Die document.evaluate()Methode wertet eine XPATH-Abfrage / einen XPATH-Ausdruck aus. So können Sie dort XPATH-Ausdrücke übergeben, in das HTML-Dokument wechseln und das gewünschte Element suchen.

In XPATH können Sie ein Element über den Textknoten wie folgt auswählen, das das Element mit dem folgenden Textknoten erhält div.

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

Verwenden Sie Folgendes, um ein Element zu erhalten, das Text enthält:

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

Die contains()Methode in XPATH verwendet einen Knoten als ersten Parameter und den zu suchenden Text als zweiten Parameter.

Überprüfen Sie diesen Punkt hier , dies ist ein Beispiel für die Verwendung von XPATH in JavaScript

Hier ist ein Code-Snippet:

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";  

Wie Sie sehen können, kann ich das HTML-Element greifen und es nach Belieben ändern.

Gdyrrahitis
quelle
Danke dir! Funktioniert super! Aber wie kann man den "thisHeading.textContent" "console.log", wenn ich nur ein Wort aus diesem Text herausholen muss? Zum Beispiel: '// div [enthält (., \' / Sie melden sich (. *) Mal in dieser Sitzung an / \ ')]' und alarmieren dann (thisHeading.textContent. $ 1)
passwd
Ok, ich mache es so:alert(thisHeading.textContent.replace(/.*You have login (.*) times.*/,'$1')) ;
passwd
@passwd, das kannst du nicht machen. Regex wird in XPATH 1.0 nicht unterstützt (das .evaluate()verwendet. Bitte korrigiert mich jemand, wenn ich falsch liege). Erstens können Sie nicht nach etwas suchen, das einem regulären Ausdruck entspricht. Zweitens gibt die .textContentEigenschaft den Textknoten des Elements zurück. Wenn Sie einen Wert aus diesem Text herausholen möchten, sollten Sie ihn explizit behandeln, wahrscheinlich indem Sie eine Funktion erstellen, die einem regulären Ausdruck entspricht und den übereinstimmenden Wert in group zurückgibt. Dazu stellen Sie eine neue Frage in einem separaten Thread.
Gdyrrahitis
Internet Explorer: Keine Unterstützung. Wird aber in Edge unterstützt. Ich bin mir nicht sicher, was das in Bezug auf die Version bedeutet.
Rolf
Wie soll ein Fehler behandelt werden, wenn das gesuchte Element fehlt?
Nenito
69

Sie könnten diese ziemlich einfache Lösung verwenden:

Array.from(document.querySelectorAll('div'))
  .find(el => el.textContent === 'SomeText, text continues.');
  1. Das Array.fromkonvertiert die NodeList in ein Array (es gibt mehrere Methoden, um dies zu tun, wie den Spread-Operator oder das Slice).

  2. Das Ergebnis, das jetzt ein Array ist, ermöglicht die Verwendung der Array.findMethode. Sie können dann ein beliebiges Prädikat eingeben. Sie können den textContent auch mit einem regulären Ausdruck oder was auch immer Sie möchten überprüfen.

Beachten Sie, dass Array.fromund Array.findES2015-Funktionen sind. Sie sind kompatibel mit älteren Browsern wie IE10 ohne Transpiler:

Array.prototype.slice.call(document.querySelectorAll('div'))
  .filter(function (el) {
    return el.textContent === 'SomeText, text continues.'
  })[0];
Niels
quelle
1
Wenn Sie mehrere Elemente finden möchten, ersetzen Sie diese finddurch filter.
RubbelDieKatz
38

Da Sie es in Javascript gefragt haben, können Sie so etwas haben

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

Und dann nenne es so

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
Pawan Nogariya
quelle
1
Scheint so zu funktionieren, aber im Gegenzug bekomme ich nur folgendes:[object HTMLDivElement],[object HTMLDivElement]
passwd
Ja, Sie werden die Divs mit passendem Text erhalten und dann können Sie dort die innere foundDivs[0].innerText
Textmethode so
20

Diese Lösung führt Folgendes aus:

  • Verwendet den ES6-Spread-Operator, um die NodeList aller divs in ein Array zu konvertieren .

  • Bietet Ausgabe, wenn die Abfragezeichenfolge div enthält , nicht nur, wenn sie genau der Abfragezeichenfolge entspricht (was bei einigen anderen Antworten der Fall ist). zB Es sollte nicht nur für 'SomeText', sondern auch für 'SomeText, Text wird fortgesetzt' ausgegeben.

  • Gibt den gesamten divInhalt aus, nicht nur die Abfragezeichenfolge. Beispiel: Für 'SomeText, Text wird fortgesetzt' sollte die gesamte Zeichenfolge ausgegeben werden, nicht nur 'SomeText'.

  • Ermöglicht, dass mehrere divs die Zeichenfolge enthalten, nicht nur eine einzelne 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>

Andrew Willems
quelle
3
Ich liebe es. Sauber, prägnant und verständlich - alles zur gleichen Zeit.
Ba_ul
2
Schrecklich ineffizient, sicher? Überlegen Sie, wie groß innerHTMLIhre Top- <div>s sind. Sie sollten zuerst divs herausfiltern, die untergeordnete Elemente enthalten. Auch verdächtig document.getElementsByTagName('div')kann schneller sein, aber ich würde Benchmark, um sicher zu sein.
Timmmm
Das ist großartig für mich, ich kann am Anfang einen guten Selektor einstellen, weil ich bereits weiß, dass es nur in einem Tisch sein kann, cool, danke
gsalgadotoledo
10

Sie sehen am besten, ob Sie ein übergeordnetes Element des Div haben, das Sie abfragen. Wenn ja, holen Sie sich das übergeordnete Element und führen Sie eine aus element.querySelectorAll("div"). Sobald Sie das erhalten, nodeListwenden Sie einen Filter auf die innerTextEigenschaft an. Angenommen, ein übergeordnetes Element des Div, das wir abfragen, hat ein idvon container. Normalerweise können Sie direkt von der ID aus auf den Container zugreifen, aber machen wir es richtig.

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

So, das war es.

Reduzieren
quelle
Dies funktionierte für mich aber mit innerHTML anstelle von innerText
Chase Sandmann
5

Wenn Sie jquery oder ähnliches nicht verwenden möchten, können Sie Folgendes versuchen:

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]
} 

Sobald Sie die Knoten in einem Array haben, die den Text enthalten, können Sie etwas damit tun. Als ob Sie jeden alarmieren oder auf die Konsole drucken würden. Eine Einschränkung ist, dass dies nicht unbedingt Divs per se erfasst, sondern das übergeordnete Element des Textknotens, der den gesuchten Text enthält.

Steve Botello
quelle
3

Verwenden Sie Datenattribute, da die Länge des Texts in einem Datenattribut unbegrenzt ist! Und dann können Sie normale CSS-Selektoren verwenden, um Ihre Elemente wie vom OP gewünscht auszuwählen.

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>

Idealerweise führen Sie den Teil zum Einstellen der Datenattribute beim Laden des Dokuments aus und schränken den Selector querySelectorAll für die Leistung ein wenig ein.

Keymap
quelle
2

Google hat dies als Top-Ergebnis für diejenigen, die einen Knoten mit einem bestimmten Text suchen müssen. Durch die Aktualisierung kann eine Knotenliste jetzt in modernen Browsern iteriert werden, ohne dass sie in ein Array konvertiert werden muss.

Die Lösung kann für jeden so verwenden.

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
    }
});

Dies funktionierte für mich, um einen Text zum Suchen / Ersetzen in einer Knotenliste zu erstellen, wenn ein normaler Selektor nicht nur einen Knoten auswählen konnte, sodass ich jeden Knoten einzeln filtern musste, um ihn auf die Nadel zu überprüfen.

Vigilante
quelle
2

Verwenden Sie XPath und document.evaluate () und stellen Sie sicher, dass Sie text () verwenden und nicht. für das enthält () -Argument, oder Sie haben den gesamten HTML-Code oder das äußerste div-Element abgeglichen.

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

oder ignorieren Sie führende und nachfolgende Leerzeichen

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

oder mit allen Tag-Typen übereinstimmen (div, h1, p usw.)

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

Dann iterieren

let thisHeading;
while(thisHeading = headings.iterateNext()){
    // thisHeading contains matched node
}
Steven Spungin
quelle
Kann diese Methode verwendet werden, um einem Element eine Klasse hinzuzufügen? zBthisheading.setAttribute('class', "esubject")
Matthew
Sobald Sie das Element haben, sicher. Es ist jedoch besser, element.classList.add ("esubject") zu verwenden :)
Steven Spungin
1

Hier ist der XPath-Ansatz, jedoch mit einem Minimum an XPath-Jargon.

Regelmäßige Auswahl anhand von Elementattributwerten (zum Vergleich):

// 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';
}

XPath-Auswahl basierend auf Text innerhalb des Elements.

// 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';
}

Und hier ist die Groß- und Kleinschreibung nicht zu beachten, da der Text volatiler ist:

// 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';
}
Jan Kyu Peblik
quelle
0

Ich hatte ein ähnliches Problem.

Funktion, die alle Elemente zurückgibt, die Text von arg enthalten.

Das funktioniert bei mir:

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)))

}}

Paweł Zieliński
quelle
0

Hier gibt es bereits viele großartige Lösungen. Um jedoch eine optimierte Lösung und eine weitere Lösung zu finden, die der Idee eines Verhaltens und einer Syntax von querySelector entspricht, habe ich mich für eine Lösung entschieden, die Object um einige Prototypfunktionen erweitert. Beide Funktionen verwenden reguläre Ausdrücke zum Abgleichen von Text. Es kann jedoch eine Zeichenfolge als loser Suchparameter bereitgestellt werden.

Implementieren Sie einfach die folgenden Funktionen:

// 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];
}

Mit diesen implementierten Funktionen können Sie jetzt wie folgt Anrufe tätigen:

  • document.queryInnerTextAll('div.link', 'go');
    Dies würde alle Divs finden, die die Link- Klasse mit dem Wort go im innerText enthalten (z. B. Go Left oder GO down oder go right oder It's Go od ).
  • document.queryInnerText('div.link', 'go');
    Dies würde genau wie im obigen Beispiel funktionieren, außer dass nur das erste übereinstimmende Element zurückgegeben wird.
  • document.queryInnerTextAll('a', /^Next$/);
    Finden Sie alle Links mit dem genauen Text Weiter (Groß- und Kleinschreibung beachten). Dies schließt Links aus, die das Wort Weiter zusammen mit anderem Text enthalten.
  • document.queryInnerText('a', /next/i);
    Suchen Sie den ersten Link, der das Wort next enthält , unabhängig von der Groß- / Kleinschreibung (z. B. Next Page oder Go to next ).
  • e = document.querySelector('#page');
    e.queryInnerText('button', /Continue/);
    Dies führt eine Suche innerhalb eines Containerelements nach einer Schaltfläche durch, die den Text Weiter enthält (Groß- und Kleinschreibung beachten). (zB Weiter oder Weiter mit Weiter, aber nicht fortfahren )
b_laoshi
quelle