Javascript .querySelector znajdź <div> za pomocą innerTEXT


109

Jak mogę znaleźć DIV z określonym tekstem? Na przykład:

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

Próbuję użyć czegoś takiego:

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

Ale oczywiście to nie zadziała. Jak mogę to zrobić?


Nawet gdybyś mógł to zrobić, nie byłoby to szybsze niż pobranie wszystkich elementów div i przefiltrowanie ich przez właściwość innerText. Więc dlaczego nie zrobisz tego ręcznie.
Redu

Odpowiedzi:


100

Pytanie OP dotyczy zwykłego JavaScript, a nie jQuery . Chociaż odpowiedzi jest mnóstwo i podoba mi się odpowiedź @Pawan Nogariya , sprawdź tę alternatywę.

Możesz używać XPATH w JavaScript. Więcej informacji o artykule MDN tutaj .

document.evaluate()Sposób ocenia się zapytania xPath / ekspresji. Możesz więc przekazać tam wyrażenia XPATH, przejść do dokumentu HTML i zlokalizować żądany element.

W XPATH możesz wybrać element za pomocą węzła tekstowego, takiego jak poniżej, który otrzyma ten, divktóry ma następujący węzeł tekstowy.

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

Aby uzyskać element, który zawiera tekst, użyj:

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

Plik contains()Metoda w XPATH zajmuje węzeł jako pierwszy parametr i tekst do wyszukania jako drugi parametr.

Sprawdź ten plunk tutaj , to jest przykład użycia XPATH w JavaScript

Oto fragment kodu:

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

Jak widać, mogę pobrać element HTML i zmodyfikować go tak, jak mi się podoba.


Dziękuję Ci! Działa świetnie! Ale jak „console.log” do „thisHeading.textContent”, jeśli muszę pobrać tylko jedno słowo z tego tekstu? Na przykład: „// div [zawiera (., \ '/ You login (. *) Times this session / \')]”, a następnie alert (thisHeading.textContent. $ 1)
passwd

Ok, robię to w ten sposób:alert(thisHeading.textContent.replace(/.*You have login (.*) times.*/,'$1')) ;
passwd

@passwd, cóż, nie możesz tego zrobić. Regex nie jest obsługiwany w XPATH 1.0 (który .evaluate()używa. Proszę kogoś poprawić, jeśli się mylę), więc po pierwsze, nie możesz wyszukać czegoś, co pasuje do wyrażenia regularnego. Po drugie, .textContentwłaściwość zwraca węzeł tekstowy elementu. Jeśli chcesz pobrać wartość z tego tekstu, powinieneś obsłużyć to jawnie, prawdopodobnie tworząc jakąś funkcję, która pasuje do wyrażenia regularnego i zwraca pasującą wartość w grupie, aby utworzyć nowe pytanie w osobnym wątku.
gdyrrahitis

Internet Explorer: brak wsparcia. Ale obsługiwane w Edge. Nie jestem pewien, co to oznacza, biorąc pod uwagę wersję.
Rolf

jak postąpić z błędem w przypadku braku poszukiwanego elementu?
nenito

72

Możesz użyć tego całkiem prostego rozwiązania:

Array.from(document.querySelectorAll('div'))
  .find(el => el.textContent === 'SomeText, text continues.');
  1. Array.fromPrzekształci liście węzłów do tablicy (istnieją liczne sposoby wykonania tej jak operator do smarowania lub plaster)

  2. Rezultatem jest teraz tablica, która pozwala na użycie Array.findmetody, możesz następnie wstawić dowolny predykat. Możesz również sprawdzić textContent za pomocą wyrażenia regularnego lub cokolwiek chcesz.

Zwróć uwagę, że Array.fromi Array.findsą to funkcje ES2015. Są kompatybilne ze starszymi przeglądarkami, takimi jak IE10 bez transpilera:

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

2
Jeśli chcesz znaleźć wiele elementów, wymienić findsię filter.
RubbelDieKatz

38

Ponieważ poprosiłeś o to w javascript, więc możesz mieć coś takiego

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

A potem nazwij to tak

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
Niby to działa, ale w zamian dostaję tylko to:[object HTMLDivElement],[object HTMLDivElement]
passwd

Tak, będziesz otrzymywać elementy div z pasującym tekstem, a następnie możesz wywołać tam metodę tekstu wewnętrznego, coś takiego foundDivs[0].innerText, tak proste
Pawan Nogariya

20

To rozwiązanie wykonuje następujące czynności:

  • Używa operatora rozproszenia ES6 do konwersji NodeList wszystkich divs na tablicę.

  • Dostarcza dane wyjściowe, jeśli div zawiera ciąg zapytania, a nie tylko jeśli dokładnie jest równy ciągowi zapytania (co ma miejsce w przypadku niektórych innych odpowiedzi). np. Powinien dostarczyć dane wyjściowe nie tylko dla „Jakiś tekst”, ale także dla „Jakiś tekst, kontynuacja tekstu”.

  • Wyprowadza całą divzawartość, a nie tylko ciąg zapytania. np. dla „Jakiś tekst, tekst kontynuowany” powinien wypisać cały ciąg, a nie tylko „Jakiś tekst”.

  • Pozwala na wiele divznaków, aby zawierały ciąg, a nie tylko jeden 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
Uwielbiam to. Czyste, zwięzłe i zrozumiałe - wszystko w tym samym czasie.
ba_ul,

2
Z pewnością strasznie nieefektywne? Pomyśl, jak duże innerHTMLsą Twoje najlepsze strony <div>. divNajpierw należy odfiltrować pliki zawierające dzieci. Podejrzany również document.getElementsByTagName('div')może być szybszy, ale dla pewności wykonałbym test porównawczy.
Timmmm

To dla mnie super, mogę ustawić dobry selektor na początek bo już wiem, że może być tylko w stole, super, dzięki
gsalgadotoledo

10

Najlepiej sprawdzić, czy masz element nadrzędny elementu div, o który pytasz. Jeśli tak, pobierz element nadrzędny i wykonaj polecenie element.querySelectorAll("div"). Po otrzymaniu nodeListzastosuj na nim filtr na innerTextwłaściwości. Załóżmy, że element nadrzędny elementu div, którego dotyczy zapytanie, ma wartość idof container. Zwykle możesz uzyskać dostęp do kontenera bezpośrednio z identyfikatora, ale zróbmy to we właściwy sposób.

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

Więc to jest to.


To zadziałało dla mnie, ale z innerHTML zamiast innerText
Chase Sandmann

5

Jeśli nie chcesz używać jquery lub czegoś podobnego, możesz spróbować tego:

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

Gdy masz już węzły w tablicy zawierającej tekst, możesz coś z nimi zrobić. Powiadom każdego lub wydrukuj na konsoli. Jedynym zastrzeżeniem jest to, że niekoniecznie musi to przechwytywać elementy div jako takie, spowoduje to przechwycenie rodzica węzła tekstowego, który zawiera tekst, którego szukasz.


3

Ponieważ nie ma ograniczeń co do długości tekstu w atrybucie danych, użyj atrybutów danych! Następnie możesz użyć zwykłych selektorów css, aby wybrać element (y) tak, jak chce 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>

Idealnie byłoby zrobić część ustawiania atrybutu danych podczas ładowania dokumentu i nieco zawęzić selektor querySelectorAll dla wydajności.


2

Google ma to jako najlepszy wynik dla tych, którzy muszą znaleźć węzeł z określonym tekstem. W ramach aktualizacji lista węzłów jest teraz iterowalna w nowoczesnych przeglądarkach bez konieczności konwertowania jej na tablicę.

Rozwiązanie może używać forEach w ten sposób.

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

Pomogło mi to w znalezieniu / zamianie tekstu na liście węzłów, gdy normalny selektor nie mógł wybrać tylko jednego węzła, więc musiałem filtrować każdy węzeł jeden po drugim, aby sprawdzić go pod kątem igły.


2

Użyj XPath i document.evaluate () i upewnij się, że używasz text (), a nie. dla argumentu zawiera (), w przeciwnym razie dopasowany zostanie cały kod HTML lub najbardziej zewnętrzny element div.

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

lub zignoruj ​​początkowe i końcowe spacje

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

lub dopasuj wszystkie typy tagów (div, h1, p itp.)

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

Następnie wykonaj iterację

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

Czy ta metoda może służyć do dodawania klasy do elementu? np.thisheading.setAttribute('class', "esubject")
Matthew

Kiedy już masz element, jasne. Jednak lepiej jest użyć element.classList.add („esubject”) :)
Steven Spungin,

1

Oto podejście XPath, ale z minimalnym żargonem XPath.

Zwykły wybór na podstawie wartości atrybutów elementu (do porównania):

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

Wybór XPath na podstawie tekstu w elemencie.

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

A oto z rozróżnianiem wielkości liter, ponieważ tekst jest bardziej zmienny:

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

Miałem podobny problem.

Funkcja zwracająca wszystkie elementy zawierające tekst z arg.

To działa dla mnie:

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

Jest tu już wiele świetnych rozwiązań. Jednak, aby zapewnić bardziej usprawnione rozwiązanie i jeszcze jedno zgodne z ideą zachowania i składni querySelector, zdecydowałem się na rozwiązanie, które rozszerza Object o kilka funkcji prototypowych. Obie te funkcje używają wyrażeń regularnych do dopasowywania tekstu, jednak jako luźny parametr wyszukiwania można podać ciąg.

Wystarczy zaimplementować następujące funkcje:

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

Po zaimplementowaniu tych funkcji możesz teraz wykonywać połączenia w następujący sposób:

  • document.queryInnerTextAll('div.link', 'go');
    Spowoduje to znalezienie wszystkich elementów div zawierających klasę linków ze słowem go w tekście wewnętrznym (np. Idź w lewo lub GO w dół lub w prawo lub It's Go od )
  • document.queryInnerText('div.link', 'go');
    To działałoby dokładnie tak, jak w powyższym przykładzie, z tą różnicą, że zwróciłoby tylko pierwszy pasujący element.
  • document.queryInnerTextAll('a', /^Next$/);
    Znajdź wszystkie linki z dokładnym tekstem Dalej (z uwzględnieniem wielkości liter). Spowoduje to wykluczenie łączy zawierających słowo Dalej wraz z innym tekstem.
  • document.queryInnerText('a', /next/i);
    Znajdź pierwszy link, który zawiera słowo następne , niezależnie od wielkości liter (np. Następna strona lub Przejdź do następnej )
  • e = document.querySelector('#page');
    e.queryInnerText('button', /Continue/);
    Spowoduje to wyszukanie w elemencie kontenera przycisku zawierającego tekst Kontynuuj (z uwzględnieniem wielkości liter). (np. Kontynuuj lub Przejdź do następnego, ale nie kontynuuj )
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.