Как найти всех потомков js

У нас есть DOM-элемент, который имеет разную структуру вложенности одного в другой. Нам нужно получить всех его потомков — детей, детей у детей, детей у тех детей и так далее до конца. Как это сделать?

Для этого нужно сначала получить сам элемент element и уже у него вызвать метод getElementsByTagName(). В качестве параметра нужно передать строку, которая будет состоять из одного символа звёздочки *

element.getElementsByTagName("*")

Скриншот из стандарта:

Получение всех потомков DOM-элемента - JavaScript

Получение всех потомков DOM-элемента — JavaScript

Информационные ссылки

Перевод стандарта Объектной Модели Документа — DOM — https://efim360.ru/dom/

Стандарт DOM — Раздел «4.5. Interface Document» — Блок «Для разработчиков» — https://dom.spec.whatwg.org/#interface-document

ECMAScript | Глобальная Запись Среды | Метод CreateGlobalVarBinding ( N, D )

CreateGlobalVarBinding ( N, D ) Конкретный метод CreateGlobalVarBinding(Создать привязку глобальной переменной), глобальной записи среды envRec принимает аргументы N (строка) и D (логическое […]

JavaScript | Как посчитать количество перестановок (слов) из набора символов, учитывая длину последовательности?

Есть массив, который содержит уникальные символы: let myarr = [‘1′,’2′,’3′,’4′,’5’]; Нам нужно узнать сколько слов определённой длины можно получить при любых перестановках […]

Работа префикса квантификатора диапазона с последовательностью символов

  В этой публикации мы рассмотрим такой вид префикса квантификатора (QuantifierPrefix) для шаблона (Pattern) регулярного выражения (RegExp), который обозначается фигурными скобками. В стандарте […]

Ключи text и textcontent - JavaScript

Одной командой document.write((((Array.from(document.getElementsByTagName(«script»))).map(i => {return i.src}))).join(«<br>»)) Куда вводить эту команду? Открываете HTML-страницу, с которой хотите получить все ссылки. Включаете «Инструменты разработчика» в […]

I want to scan a div for all childNodes including the ones that are nestled within other elements. Right now I have this:

var t = document.getElementById('DivId').childNodes;
for(i=0; i<t.length; i++) alert(t[i].id);

But it only gets the children of the Div and not the grandchildren. Thanks!

Edit: This question was too vague. Sorry about that. Here’s a fiddle:

http://jsfiddle.net/F6L2B/

The body.onload script doesn’t run at JSFiddle, but it works, except that the ‘Me Second’ and ‘Me Third’ input fields are not being assigned a tabIndex and are therefore being skipped over.

asked Nov 30, 2011 at 6:15

Michael Swarts's user avatar

Michael SwartsMichael Swarts

3,7738 gold badges29 silver badges41 bronze badges

This is the fastest and simplest way, and it works on all browsers:

myDiv.getElementsByTagName("*")

answered Apr 27, 2015 at 16:33

rvighne's user avatar

rvighnervighne

20.5k11 gold badges51 silver badges73 bronze badges

4

If you’re looking for all HTMLElement on modern browsers you can use:

myDiv.querySelectorAll("*")

answered Oct 9, 2013 at 13:20

Mad Echet's user avatar

Mad EchetMad Echet

3,6937 gold badges28 silver badges44 bronze badges

5

What about great-grandchildren?

To go arbitrarily deep, you could use a recursive function.

var alldescendants = [];

var t = document.getElementById('DivId').childNodes;
    for(let i = 0; i < t.length; i++)
        if (t[i].nodeType == 1)
            recurseAndAdd(t[i], alldescendants);

function recurseAndAdd(el, descendants) {
  descendants.push(el.id);
  var children = el.childNodes;
  for(let i=0; i < children.length; i++) {
     if (children[i].nodeType == 1) {
         recurseAndAdd(children[i]);
     }
  }
}

If you really only want grandchildren, then you could take out the recursion (and probably rename the function)

function recurseAndAdd(el, descendants) {
  descendants.push(el.id);
  var children = el.childNodes;
  for(i=0; i < children.length; i++) {
     if (children[i].nodeType == 1) {
         descendants.push(children[i].id);
     }
  }
}

lexipenia's user avatar

answered Nov 30, 2011 at 6:17

Adam Rackis's user avatar

Adam RackisAdam Rackis

82.2k55 gold badges268 silver badges393 bronze badges

9

If anyone else wants all nodes within that tree, and not just the elements, here’s a 2022-era JS snippet:

function getDescendantNodes(node, all = []) {
  all.push(...node.childNodes);
  for (const child of node.childNodes) 
    getDescendantNodes(child, all);
  return all;
}

Protip: afterwards, you can filter to your preferred nodeType with the Node global: nodes = getDescendantNodes($0); nodes.filter(n => n.nodeType === Node.TEXT_NODE)

answered Sep 13, 2022 at 16:23

Paul Irish's user avatar

Paul IrishPaul Irish

46.9k22 gold badges96 silver badges132 bronze badges

I was wondering, JavaScript offers a variety of methods to get the first child element from any element, but which is the best? By best, I mean: most cross-browser compatible, fastest, most comprehensive and predictable when it comes to behaviour. A list of methods/properties I use as aliases:

var elem = document.getElementById('container');
var child = elem.children[0];
var child = elem.firstElementChild; // == children[0]

This works for both cases:

var child = elem.childNodes[0]; // or childNodes[1], see below

That’s in case of forms, or <div> iteration. If I might encounter text elements:

var child = elem.childNodes; // treat as NodeList
var child = elem.firstChild;

As far as I can work out, firstChild uses the NodeList from childNodes, and firstElementChild uses children. I’m basing this assumption on the MDN reference:

childNode is a reference to the first child element of the element node, or null if there isn’t one.

I’m guessing that, in terms of speed, the difference, if any, will be next to nothing, since firstElementChild is effectively a reference to children[0], and the children object is already in memory anyway.

What does throw me, is the childNodes object. I’ve used it to take a look at a form, in a table element. While children lists all form elements, childNodes also seems to include whitespace from the HTML code:

console.log(elem.childNodes[0]);
console.log(elem.firstChild);

Both log <TextNode textContent="n ">

console.log(elem.childNodes[1]);
console.log(elem.children[0]);
console.log(elem.firstElementChild);

All log <input type="text">. How come? I’d understand that one object would allow me to work with the “raw” HTML code, while the other sticks to the DOM, but the childNodes element seems to work on both levels.

To get back to my initial question, my guess would be: if I want the most comprehensive object, childNodes is the way to go, but because of its comprehensiveness, it might not be the most predictable in terms of it returning the element I want/expect at any given moment. Cross-browser support might also prove to be a challenge in that case, though I could be wrong.

Could anyone clarify the distinction between the objects at hand? If there is a speed difference, however negligible, I’d like to know, too. If I’m seeing this all wrong, feel free to educate me.


PS: Please, please, I like JavaScript, so yes, I want to deal with this sort of thing. Answers like “jQuery deals with this for you” are not what I’m looking for, hence no jquery tag.

Поиск: getElement* и querySelector* и не только

Прямая навигация от родителя к потомку удобна, если элементы рядом. А если нет?

Как достать произвольный элемент откуда-то из глубины документа?

Для этого в DOM есть дополнительные методы поиска.

document.getElementById или просто id

Если элементу назначен специальный атрибут id, то можно получить его прямо по переменной с именем из значения id.

Например:

<div id="*!*content-holder*/!*">
  <div id="*!*content*/!*">Элемент</div>
</div>

<script>
*!*
  alert( content ); // DOM-элемент
  alert( window['content-holder'] ); // в имени дефис, поэтому через [...]
*/!*
</script>

Это поведение соответствует стандарту. Оно существует, в первую очередь, для совместимости, как осколок далёкого прошлого и не очень приветствуется, поскольку использует глобальные переменные. Браузер пытается помочь нам, смешивая пространства имён JS и DOM, но при этом возможны конфликты.

Более правильной и общепринятой практикой является доступ к элементу вызовом document.getElementById("идентификатор").

Например:

<div id="*!*content*/!*">Выделим этот элемент</div>

<script>
*!*
  var elem = document.getElementById('content');

  elem.style.background = 'red';

  alert( elem == content ); // true

  content.style.background = ""; // один и тот же элемент
*/!*
</script>
По стандарту значение `id` должно быть уникально, то есть в документе может быть только один элемент с данным `id`. И именно он будет возвращён.

Если в документе есть несколько элементов с уникальным `id`, то поведение неопределено. То есть, нет гарантии, что браузер вернёт именно первый или последний -- вернёт случайным образом.

Поэтому стараются следовать правилу уникальности `id`.

Далее в примерах я часто буду использовать прямое обращение через переменную, чтобы было меньше букв и проще было понять происходящее. Но предпочтительным методом является document.getElementById.

getElementsByTagName

Метод elem.getElementsByTagName(tag) ищет все элементы с заданным тегом tag внутри элемента elem и возвращает их в виде списка.

Регистр тега не имеет значения.

Например:

// получить все div-элементы
var elements = document.getElementsByTagName('div');

Обратим внимание: в отличие от getElementById, который существует только в контексте document, метод getElementsByTagName может искать внутри любого элемента.

Например, найдём все элементы input внутри таблицы:

<table id="age-table">
  <tr>
    <td>Ваш возраст:</td>

    <td>
      <label>
        <input type="radio" name="age" value="young" checked> младше 18
      </label>
      <label>
        <input type="radio" name="age" value="mature"> от 18 до 50
      </label>
      <label>
        <input type="radio" name="age" value="senior"> старше 60
      </label>
    </td>
  </tr>

</table>

<script>
*!*
  var tableElem = document.getElementById('age-table');
  var elements = tableElem.getElementsByTagName('input');
*/!*

  for (var i = 0; i < elements.length; i++) {
    var input = elements[i];
    alert( input.value + ': ' + input.checked );
  }
</script>

Можно получить всех потомков, передав звездочку '*' вместо тега:

// получить все элементы документа
document.getElementsByTagName('*');

// получить всех потомков элемента elem:
elem.getElementsByTagName('*');

“`warn header=”Не забываем про букву "s"!”
Одна из самых частых ошибок начинающих (впрочем, иногда и не только) — это забыть букву `”s”`, то есть пробовать вызывать метод `getElementByTagName` вместо getElementsByTagName.

Буква "s" не нужна там, где элемент только один, то есть в getElementById, в остальных методах она обязательна.


````warn header="Возвращается коллекция, а не элемент"
Другая частая ошибка -- это код вида:

```js
// не работает
document.getElementsByTagName('input').value = 5;

То есть, вместо элемента присваивают значение коллекции. Работать такое не будет.

Коллекцию нужно или перебрать в цикле или получить элемент по номеру и уже ему присваивать value, например так:

// работает
document.getElementsByTagName('input')[0].value = 5;

## document.getElementsByName

Вызов `document.getElementsByName(name)` позволяет получить все элементы с данным атрибутом `name`.

Например, все элементы с именем `age`:

```js
var elems = document.getElementsByName('age');
```

До появления стандарта HTML5 этот метод возвращал только те элементы, в которых предусмотрена поддержка атрибута `name`, в частности: `iframe`, `a`, `input` и другими. В современных браузерах (IE10+) тег не имеет значения.

Используется этот метод весьма редко.

## getElementsByClassName

Вызов `elem.getElementsByClassName(className)` возвращает коллекцию элементов с классом `className`. Находит элемент и в том случае, если у него несколько классов, а искомый - один из них.

Поддерживается всеми современными браузерами, кроме IE8-.

Например:

```html run height=50
<div class="article">Статья</div>
<div class="long article">Длинная статья</div>

<script>
  var articles = document.getElementsByClassName('article');
  alert( articles.length ); // 2, найдёт оба элемента
</script>
```

Как и `getElementsByTagName`, этот метод может быть вызван и в контексте DOM-элемента, и в контексте документа.

## querySelectorAll [#querySelectorAll]

Вызов `elem.querySelectorAll(css)` возвращает все элементы внутри `elem`, удовлетворяющие CSS-селектору `css`.

Это один из самых часто используемых и полезных методов при работе с DOM.

Он есть во всех современных браузерах, включая IE8+ (в режиме соответствия стандарту).

Следующий запрос получает все элементы `LI`, которые являются последними потомками в `UL`:

```html run
<ul>
  <li>Этот</li>
  <li>тест</li>
</ul>
<ul>
  <li>полностью</li>
  <li>пройден</li>
</ul>
<script>
*!*
  var elements = document.querySelectorAll('ul > li:last-child');
*/!*

  for (var i = 0; i < elements.length; i++) {
    alert( elements[i].innerHTML ); // "тест", "пройден"
  }
</script>
```

```smart header="Псевдо-класс тоже работает"
Псевдо-классы в CSS-селекторе, в частности `:hover` и `:active`, также поддерживаются. Например, `document.querySelectorAll(':hover')` вернёт список, в порядке вложенности, из текущих элементов под курсором мыши.
```

## querySelector [#querySelector]

Вызов `elem.querySelector(css)` возвращает не все, а только первый элемент, соответствующий CSS-селектору `css`.

Иначе говоря, результат -- такой же, как и при `elem.querySelectorAll(css)[0]`, но в последнем вызове сначала ищутся все элементы, а потом берётся первый, а в `elem.querySelector(css)` ищется только первый, то есть он эффективнее.

Этот метод часто используется, когда мы заведомо знаем, что подходящий элемент только один, и хотим получить в переменную сразу его.

## matches

Предыдущие методы искали по DOM.

Метод [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) ничего не ищет, а проверяет, удовлетворяет ли `elem` селектору `css`. Он возвращает `true` либо `false`.

Не поддерживается в IE8-.

Этот метод бывает полезным, когда мы перебираем элементы (в массиве или по обычным навигационным ссылкам) и пытаемся отфильтровать те из них, которые нам интересны.

Ранее в спецификации он назывался `matchesSelector`, и большинство браузеров поддерживают его под этим старым именем, либо с префиксами `ms/moz/webkit`.

Например:

```html run
<a href="http://example.com/file.zip">...</a>
<a href="http://ya.ru">...</a>

<script>
  var elems = document.body.children;

  for (var i = 0; i < elems.length; i++) {
*!*
    if (elems[i].matches('a[href$="zip"]')) {
*/!*
      alert( "Ссылка на архив: " + elems[i].href );
    }
  }
</script>
```

## closest

Метод `elem.closest(css)` ищет ближайший элемент выше по иерархии DOM, подходящий под CSS-селектор `css`. Сам элемент тоже включается в поиск.

Иначе говоря, метод `closest` бежит от текущего элемента вверх по цепочке родителей и проверяет, подходит ли элемент под указанный CSS-селектор. Если подходит -- останавливается и возвращает его.

Он самый новый из методов, рассмотренных в этой главе, поэтому старые браузеры его слабо поддерживают. Это, конечно, легко поправимо, как мы увидим позже в главе <info:dom-polyfill>.

Пример использования (браузер должен поддерживать `closest`):

```html run
<ul>
  <li class="chapter">Глава I
    <ul>
      <li class="subchapter">Глава <span class="num">1.1</span></li>
      <li class="subchapter">Глава <span class="num">1.2</span></li>
    </ul>
  </li>
</ul>

<script>
  var numberSpan = document.querySelector('.num');

  // ближайший элемент сверху подходящий под селектор li
  alert(numberSpan.closest('li').className) // subchapter

  // ближайший элемент сверху подходящий под селектор .chapter
  alert(numberSpan.closest('.chapter').tagName) // LI

  // ближайший элемент сверху, подходящий под селектор span
  // это сам numberSpan, так как поиск включает в себя сам элемент
  alert(numberSpan.closest('span') === numberSpan) // true
</script>
```

## XPath в современных браузерах

Для полноты картины рассмотрим ещё один способ поиска, который обычно используется в XML. Это <a href="http://www.w3.org/TR/xpath/">язык запросов XPath</a>.

Он очень мощный, во многом мощнее CSS, но сложнее. Например, запрос для поиска элементов `H2`, содержащих текст `"XPath"`, будет выглядеть так: `//h2[contains(., "XPath")]`.

Все современные браузеры, кроме IE, поддерживают XPath с синтаксисом, близким к [описанному в MDN](https://developer.mozilla.org/en/XPath).

Найдем заголовки с текстом `XPath` в текущем документе:

```js run no-beautify
var result = document.evaluate("//h2[contains(., 'XPath')]", document.documentElement, null,
  XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

for (var i = 0; i < result.snapshotLength; i++) {
  alert( result.snapshotItem(i).outerHTML );
}
```

IE тоже поддерживает XPath, но эта поддержка не соответствует стандарту и работает только для XML-документов, например, полученных с помощью `XMLHTTPRequest` (AJAX).  Для обычных же HTML-документов XPath в IE не поддерживается.

Так как XPath сложнее и длиннее CSS, то используют его очень редко.

## Итого

Есть 6 основных методов поиска элементов DOM:
<table>
<thead>
<tr>
<td>Метод</td>
<td>Ищет по...</td>
<td>Ищет внутри элемента?</td>
<td>Поддержка</td>
</tr>
</thead>
<tbody>
<tr>
<td><code>getElementById</code></td>
<td><code>id</code></td>
<td>-</td>
<td>везде</td>
</tr>
<tr>
<td><code>getElementsByName</code></td>
<td><code>name</code></td>
<td>-</td>
<td>везде</td>
</tr>
<tr>
<td><code>getElementsByTagName</code></td>
<td>тег или <code>'*'</code></td>
<td>✔</td>
<td>везде</td>
</tr>
<tr>
<td><code>getElementsByClassName</code></td>
<td>классу</td>
<td>✔</td>
<td>кроме IE8-</td>
</tr>
<tr>
<td><code>querySelector</code></td>
<td>CSS-селектор</td>
<td>✔</td>
<td>везде</td>
</tr>
<tr>
<td><code>querySelectorAll</code></td>
<td>CSS-селектор</td>
<td>✔</td>
<td>везде</td>
</tr>
</tbody>
</table>

Практика показывает, что в 95% ситуаций достаточно `querySelector/querySelectorAll`. Хотя более специализированные методы `getElement*` работают чуть быстрее, но разница в миллисекунду-другую редко играет роль.

Кроме того:

- Есть метод `elem.matches(css)`, который проверяет, удовлетворяет ли элемент CSS-селектору. Он поддерживается большинством браузеров в префиксной форме (`ms`, `moz`, `webkit`).
- Метод `elem.closest(css)` ищет ближайший элемент выше по иерархии DOM, подходящий под CSS-селектор css. Сам элемент тоже включается в поиск.
- Язык запросов XPath поддерживается большинством браузеров, кроме IE, даже 9-й версии, но `querySelector` удобнее. Поэтому XPath используется редко.

Добавить комментарий