Javascript .querySelector find <div> by innerTEXT


109

Як я можу знайти DIV з певним текстом? Наприклад:

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

Намагайтеся використовувати щось подібне:

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

Але звичайно це не вийде. Як я можу це зробити?


Навіть якби ви могли це зробити, це не буде швидше, ніж отримати всі divs і відфільтрувати їх по властивості innerText. То чому б не зробити це вручну.
Скорочення

Відповіді:


100

Питання OP стосується простого JavaScript, а не jQuery . Хоча є багато відповідей , і я як @Pawan Nogariya відповідь , будь ласка , перевірте цей альтернативний аут.

Ви можете використовувати XPATH в JavaScript. Більше інформації про статтю MDN тут .

document.evaluate()Метод обчислює XPATH запиту / вираз. Таким чином, ви можете передати там вирази XPATH, перейти в документ HTML і знайти потрібний елемент.

У XPATH ви можете обрати елемент за текстовим вузлом, як показано нижче, що отримує той, divщо має такий текстовий вузол.

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

Щоб отримати елемент, який містить деякий текст, використовуйте наступне:

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

contains()Метод XPATH займає вузол в якості першого параметра і тексту для пошуку в якості другого параметра.

Перевірте цей планк тут , це приклад використання XPATH в JavaScript

Ось фрагмент коду:

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

Як бачите, я можу схопити HTML-елемент і змінити його так, як мені подобається.


Дякую! Чудово працює! Але як "console.log" "thisHeading.textContent", якщо мені потрібно схопити лише одне слово з цього тексту? Наприклад: '// div [містить (., \' / Ви входите (. *) Разів у цей сеанс / \ ')]', а потім попереджуєте (thisHeading.textContent. $ 1)
passwd

Гаразд, я роблю це так:alert(thisHeading.textContent.replace(/.*You have login (.*) times.*/,'$1')) ;
passwd

@passwd, ну ти цього не можеш зробити. Regex не підтримується в XPATH 1.0 (який .evaluate()використовує. Будь ласка, хтось виправить мене, якщо я помиляюся), тому, по-перше, ви не можете шукати те, що відповідає звичайному виразу. По-друге, .textContentвластивість повертає текстовий вузол елемента. Якщо ви хочете витягнути значення з цього тексту, вам слід попрацювати з ним явно, мабуть, створивши якусь функцію, яка відповідає регексу та повертає відповідне значення в групі. Для цього створіть нове запитання в окремому потоці.
gdyrrahitis

Internet Explorer: Немає підтримки. Але підтримується в Edge. Я не впевнений, що це означає, версійно.
Рольф

як слід поводитися з помилкою, якщо елемент, який я шукаю, відсутній?
неніто

72

Ви можете використовувати це досить просте рішення:

Array.from(document.querySelectorAll('div'))
  .find(el => el.textContent === 'SomeText, text continues.');
  1. Array.fromПеретворює NodeList в масив (є кілька способів зробити це як оператор поширення або шматочком)

  2. Отриманий в результаті масив дозволяє використовувати Array.findметод, потім ви можете ввести будь-який предикат. Ви також можете перевірити textContent за допомогою регулярного виразу або будь-якого іншого.

Зауважте, що це Array.fromі Array.findє функції ES2015. Вони повинні бути сумісні зі старими браузерами, такими як IE10, без транспілятора:

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

2
Якщо ви хочете знайти кілька елементів, замініть findна filter.
RubbelDieKatz

38

Оскільки ви просили це у JavaScript, то ви можете мати щось подібне

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

А потім називайте це так

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
Схоже, це працює, але взамін я отримую лише це:[object HTMLDivElement],[object HTMLDivElement]
passwd

Так, ви отримаєте діви з відповідним текстом, і тоді ви можете зателефонувати туди методом внутрішнього тексту щось подібне foundDivs[0].innerText, таке просте
Pawan Nogariya

20

Це рішення робить наступне:

  • Використовує оператор розширення ES6 для перетворення NodeList всіх divs в масив.

  • Забезпечує вихід, якщо div містить рядок запиту, а не лише якщо він точно дорівнює рядку запиту (що відбувається для деяких інших відповідей). Наприклад, він повинен забезпечити вихід не лише для 'SomeText', але і для 'SomeText, текст продовжується'.

  • Виводить весь divвміст, а не лише рядок запиту. наприклад, для 'SomeText текст продовжується', він повинен виводити цілу рядок, а не лише 'SomeText'.

  • Дозволяє для декількох divs містити рядок, а не лише один 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
Я обожнюю це. Чисте, лаконічне і зрозуміле - все одночасно.
ba_ul

2
Страшенно неефективна напевно? Подумайте, наскільки великий innerHTMLдля ваших найпопулярніших <div>. divСпершу слід відфільтрувати s, які містять дітей. Також підозрюваний document.getElementsByTagName('div')може бути швидшим, але я хотів би бути орієнтовним, щоб бути впевненим.
Timmmm

Це для мене чудово, я можу встановити хорошого селектора на початку, тому що я вже знаю, що це може бути лише в таблиці, круто, спасибі
gsalgadotoledo

10

Ви найкраще бачите, чи є у вас батьківський елемент діла, який ви запитуєте. Якщо так, отримайте батьківський елемент і виконайте element.querySelectorAll("div"). Після отримання nodeListзастосуйте до нього фільтр над innerTextвластивістю. Припустимо , що батьківський елемент DIV , який ми запитуєте має idв container. Ви можете звичайно отримати доступ до контейнера безпосередньо з ідентифікатора, але давайте зробимо це належним чином.

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

Так ось і все.


Це працювало для мене, але з innerHTML замість innerText
Чейз Сандман

5

Якщо ви не хочете використовувати jquery чи щось подібне, ви можете спробувати це:

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

Коли у вас є вузли в масиві, що містять текст, ви можете зробити щось з ними. Як сповіщення кожного або надрукуйте на консолі. Одне застереження полягає в тому, що це не обов'язково може захоплювати divs per se, це захопить батьківський текст текстового вузла, який містить текст, який ви шукаєте.


3

Оскільки в атрибуті даних немає обмежень щодо довжини тексту, використовуйте атрибути даних! І тоді ви можете використовувати звичайні селектори css для вибору елементів (елементів), як хоче ОП.

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>

В ідеалі ви робите частину налаштування атрибутів даних для завантаження документа і трохи звужуєте селектор querySelectorAll для продуктивності.


2

Google має це як головний результат для тих, кому потрібно знайти вузол з певним текстом. За допомогою оновлення, ноделіст тепер доступний для перегляду в сучасних браузерах, не перетворюючи його на масив.

Розв’язання може використовувати forEach так.

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

Це допомогло мені знайти / замінити текст всередині ноделіста, коли звичайний селектор не міг вибрати лише один вузол, тому мені довелося фільтрувати кожен вузол по одному, щоб перевірити його на голку.


2

Використовуйте XPath та document.evaluate (), і обов'язково використовуйте text (), а не. для аргументу містить (), інакше у вас буде узгоджений весь HTML або найвіддаленіший елемент div.

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

або ігнорувати пробіли та пробіли

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

або відповідати всім типам тегів (div, h1, p тощо)

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

Потім повторіть

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

Чи можна використовувати цей метод для додавання класу до елемента? напр.thisheading.setAttribute('class', "esubject")
Матвій

Як тільки у вас є елемент, обов'язково. Однак краще використовувати element.classList.add ("esubject") хоч :)
Стівен Спунгін

1

Ось підхід XPath, але з мінімальним жаргоном XPath.

Регулярний вибір на основі значень атрибутів елемента (для порівняння):

// 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 на основі тексту всередині елемента.

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

І ось з нечутливістю до регістру, оскільки текст є більш мінливим:

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

У мене була подібна проблема.

Функція, що повертає весь елемент, що включає текст із arg.

Це працює для мене:

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

Тут вже є багато чудових рішень. Однак, щоб надати більш обтічне рішення та ще одне, що відповідає ідеї поведінки і синтаксису querySelector, я вибрав рішення, яке розширює Object на пару функцій прототипу. Обидві ці функції використовують регулярні вирази для узгодження тексту, однак, рядок може бути наданий як вільний параметр пошуку.

Просто реалізуйте такі функції:

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

За допомогою цих функцій тепер ви можете телефонувати так:

  • document.queryInnerTextAll('div.link', 'go');
    Це знайде все діви , що містять посилання клас зі словом йти в InnerText (наприклад. Ліворуч чи йти вниз або йти прямо або це Go О.Д. )
  • document.queryInnerText('div.link', 'go');
    Це спрацювало б точно як приклад вище, за винятком того, що він поверне лише перший елемент, що відповідає.
  • document.queryInnerTextAll('a', /^Next$/);
    Знайдіть усі посилання з точним текстом Далі (залежно від регістру). Це виключить посилання, які містять слово Next разом з іншим текстом.
  • document.queryInnerText('a', /next/i);
    Знайдіть перше посилання, що містить слово next , незалежно від регістру (наприклад, Next Page або Next to next )
  • e = document.querySelector('#page');
    e.queryInnerText('button', /Continue/);
    Це виконує пошук у контейнерному елементі за кнопкою, що містить текст « Продовжити» (з урахуванням регістру). (напр., продовжуйте або продовжуйте до наступного, але не продовжуйте )
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.