Как найти DOM-элементы по тексту
В DOM API нет метода getElementByText(). Если вам нужно найти элемент на основе того, что он содержит, а не того, чем он является, вам придётся реализовать эту возможность самостоятельно. Такая задача возникает чаще, чем можно подумать — скрипты автоматизации, тестирование UI, парсинг динамического контента — и правильный подход зависит от того, насколько гибким должно быть ваше решение.
Ключевые моменты
- В DOM API нет встроенного метода для выбора элементов по текстовому содержимому, но три нативных подхода заполняют этот пробел: фильтрация с помощью
querySelectorAll, обход с помощьюTreeWalkerи запросы с помощью XPath. TreeWalker— наиболее универсальный нативный вариант для полного поиска текста в DOM по всем типам элементов без предварительного формирования большого NodeList.- Предпочитайте
textContentвместоinnerTextдля сопоставления текста — он быстрее, не вызывает пересчёт layout и работает стабильно независимо от видимости элемента. - Остерегайтесь распространённых подводных камней: лишние пробелы, текст вложенных потомков и динамически добавленный контент, который может отсутствовать в момент выполнения вашего скрипта.
Почему querySelector не может выполнять запросы к DOM по текстовому содержимому
querySelector() и querySelectorAll() принимают только CSS-селекторы. Хотя в CSS есть псевдокласс :has() и селекторы атрибутов, стандартного CSS-селектора для сопоставления текстового содержимого элемента не существует. Селекторы вроде div:text("Submit") просто отсутствуют в спецификации.
Это оставляет вам три практических подхода: фильтровать набор кандидатов, обходить DOM с помощью нативного API или использовать XPath.
Метод 1: Фильтрация набора кандидатов по тексту
Простейший подход — запросить элементы по тегу или классу, а затем отфильтровать по тексту. Это хорошо работает, когда вы заранее знаете тип элемента.
function findByText(tag, text) {
return [...document.querySelectorAll(tag)].filter(el =>
el.textContent.trim() === text
)
}
// Использование
const buttons = findByText('button', 'Submit')
Это читаемо и быстро, когда набор кандидатов невелик. Слабая сторона: поиск выполняется только по одному типу элементов за раз. Поиск по всем элементам с помощью '*' работает, но медленнее на больших DOM.
Метод 2: Обход DOM с помощью TreeWalker
TreeWalker — встроенный DOM API, который позволяет эффективно обходить узлы. Он хорошо поддерживается во всех современных браузерах и избегает накладных расходов на предварительное формирование полного NodeList.
function findElementsByText(root, text) {
const walker = document.createTreeWalker(
root,
NodeFilter.SHOW_ELEMENT,
{
acceptNode(node) {
return node.textContent.trim() === text
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP
}
}
)
const results = []
while (walker.nextNode()) results.push(walker.currentNode)
return results
}
// Использование
const matches = findElementsByText(document.body, 'TV')
Это позволяет искать элементы любого типа по всему дереву — универсальное решение, которое не может обеспечить подход с привязкой к конкретному тегу. Также поддерживается досрочное завершение, если вам нужно только первое совпадение.
Примечание о
FILTER_SKIPиFILTER_REJECT: ИспользованиеFILTER_SKIPздесь означает, что walker всё равно спускается к дочерним элементам несовпадающего узла. Если бы вы использовалиFILTER_REJECT, walker пропустил бы узел и всё его поддерево. Для поиска текста почти всегда нуженFILTER_SKIP, посколькуtextContentродителя может не совпадать, даже если совпадает у более глубокого потомка.
Discover how at OpenReplay.com.
Метод 3: Поиск текста в DOM с помощью XPath
Для более выразительного сопоставления document.evaluate() поддерживает XPath-выражения, включая запросы на основе текста. Это самый мощный вариант для сложных паттернов.
function findByXPath(expression, context = document) {
const result = document.evaluate(
expression,
context,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
)
return Array.from({ length: result.snapshotLength }, (_, i) =>
result.snapshotItem(i)
)
}
// Найти любой элемент, содержащий текст "Submit"
const els = findByXPath('//*[contains(text(), "Submit")]')
Поиск текста в DOM с помощью XPath чисто обрабатывает частичные совпадения и сложные условия. Компромисс — читаемость: синтаксис XPath незнаком большинству frontend-разработчиков.
Важный момент: contains(text(), "Submit") сопоставляется только с прямыми текстовыми узлами элемента. Если “Submit” находится внутри дочернего элемента, это выражение не совпадёт с родителем. Чтобы искать по всему тексту потомков, используйте вместо этого contains(., "Submit"), где . обозначает строковое значение всего элемента, включая его потомков.
textContent против innerText: что использовать для сопоставления
Оба свойства возвращают текст, но ведут себя по-разному:
| Свойство | Возвращает | Вызывает layout? |
|---|---|---|
textContent | Сырой DOM-текст, включая скрытые элементы | Нет |
innerText | Только отрендеренный текст | Да (reflow) |
Используйте textContent для сопоставления текста. Он быстрее, не вызывает пересчёт layout и работает стабильно для всех элементов независимо от видимости.
Распространённые подводные камни при поиске DOM-элементов по тексту
Пробелы: textContent включает пробелы из HTML-форматирования. Всегда используйте .trim() перед сравнением.
Вложенный текст: textContent элемента включает весь текст потомков. <div><span>TV</span></div> — textContent элемента div также равен "TV". Будьте конкретны в отношении того, на каком уровне элементов вы работаете.
Динамический контент: Текст, добавленный после загрузки страницы, не будет присутствовать в момент выполнения вашего скрипта. Используйте MutationObserver или выполняйте поиск после подтверждения существования контента.
Выбор правильного подхода
- Известный тип элемента, простое совпадение → фильтрация с помощью
querySelectorAll - Любой тип элемента, полный поиск по DOM →
TreeWalker - Частичное совпадение или сложный паттерн → XPath через
document.evaluate()
Если вы работаете в контексте тестирования, такие инструменты, как Testing Library, предоставляют встроенный getByText() — полезно знать, хотя описанные выше нативные техники остаются необходимыми для скриптов вне тестов.
Заключение
Поиск в DOM на основе текста — это пробел в стандартном API, но эти три подхода покрывают все практические случаи. Используйте querySelectorAll с фильтром для быстрого целевого поиска, когда вы знаете тип элемента. Обращайтесь к TreeWalker, когда нужен полный обход DOM без привязки к конкретному тегу. Используйте XPath, когда логика сопоставления требует частичного текста или сложных условий. Какой бы метод вы ни выбрали, предпочитайте textContent вместо innerText, обрезайте пробелы перед сравнением и учитывайте текст вложенных потомков, чтобы избежать ложных совпадений.
Часто задаваемые вопросы
Нет. querySelector и querySelectorAll принимают только CSS-селекторы, а в CSS нет селектора для сопоставления текстового содержимого. Вам нужно использовать подходы на основе JavaScript, такие как фильтрация результата querySelectorAll, обход DOM с помощью TreeWalker или выполнение XPath-выражения с помощью document.evaluate для поиска элементов по их содержимому.
FILTER_SKIP указывает TreeWalker пропустить текущий узел, но всё равно посетить его дочерние элементы. FILTER_REJECT пропускает узел и всё его поддерево. Для поиска на основе текста обычно правильным выбором является FILTER_SKIP, потому что родитель может не совпадать, в то время как более глубокий потомок совпадает.
Нет. Выражение contains(text(), value) проверяет только прямые текстовые узлы элемента. Если целевая строка находится внутри вложенного дочернего элемента, используйте вместо этого contains(., value). Оператор точки обозначает полное строковое значение элемента, включая весь текст потомков.
textContent быстрее, потому что не вызывает пересчёт layout. Он возвращает весь текст в поддереве DOM независимо от CSS-видимости, что делает его последовательным и предсказуемым. innerText возвращает только отрендеренный текст и заставляет браузер пересчитывать layout, что добавляет ненужные накладные расходы для целей сопоставления.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.