Как получить соседний элемент js
Перейти к содержимому

Как получить соседний элемент js

  • автор:

Свойство previousElementSibling

Свойство previousElementSibling содержит предыдущий элемент, находящийся в этом же родителе. Если такого элемента нет — возвращается null .

Синтаксис

элемент.previousElementSibling;

Пример

Дан элемент #elem . Давайте выведем текст его соседа сверху:

sibling

elem

let elem = document.querySelector(‘#elem’); let text = elem.previousElementSibling.textContent; console.log(text);

Результат выполнения кода:

Пример

Если соседа сверху нет или он расположен не в родителе нашего элемента, также возвращается null :

sibling

elem

let elem = document.querySelector(‘#elem’); console.log(elem.previousElementSibling);

Результат выполнения кода:

Смотрите также

  • свойство nextElementSibling ,
    которое содержит соседа снизу (следующий элемент)

Навигация по DOM-элементам

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

Все операции с DOM начинаются с объекта document . Это главная «точка входа» в DOM. Из него мы можем получить доступ к любому узлу.

Так выглядят основные ссылки, по которым можно переходить между узлами DOM:

Поговорим об этом подробнее.

Сверху: documentElement и body

Самые верхние элементы дерева доступны как свойства объекта document :

= document.documentElement Самый верхний узел документа: document.documentElement . В DOM он соответствует тегу . = document.body Другой часто используемый DOM-узел – узел тега : document.body . = document.head Тег доступен как document.head .

Есть одна тонкость: document.body может быть равен null

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

В частности, если скрипт находится в , document.body в нём недоступен, потому что браузер его ещё не прочитал.

Поэтому, в примере ниже первый alert выведет null :

        

В мире DOM null означает «не существует»

В DOM значение null значит «не существует» или «нет такого узла».

Дети: childNodes, firstChild, lastChild

Здесь и далее мы будем использовать два принципиально разных термина:

  • Дочерние узлы (или дети) – элементы, которые являются непосредственными детьми узла. Другими словами, элементы, которые лежат непосредственно внутри данного. Например, и являются детьми элемента .
  • Потомки – все элементы, которые лежат внутри данного, включая детей, их детей и т.д.

    (и несколько пустых текстовых узлов):

    и вложенные в них:
    (ребёнок

      ) и (ребёнок
      ) – в общем, все элементы поддерева.

    Коллекция childNodes содержит список всех детей, включая текстовые узлы.

    Пример ниже последовательно выведет детей document.body :

    Обратим внимание на маленькую деталь. Если запустить пример выше, то последним будет выведен элемент . На самом деле, в документе есть ещё «какой-то HTML-код», но на момент выполнения скрипта браузер ещё до него не дошёл, поэтому скрипт не видит его.

    Свойства firstChild и lastChild обеспечивают быстрый доступ к первому и последнему дочернему элементу.

    Они, по сути, являются всего лишь сокращениями. Если у тега есть дочерние узлы, условие ниже всегда верно:

    elem.childNodes[0] === elem.firstChild elem.childNodes[elem.childNodes.length - 1] === elem.lastChild

    Для проверки наличия дочерних узлов существует также специальная функция elem.hasChildNodes() .

    DOM-коллекции

    Как мы уже видели, childNodes похож на массив. На самом деле это не массив, а коллекция – особый перебираемый объект-псевдомассив.

    И есть два важных следствия из этого:

    1. Для перебора коллекции мы можем использовать for..of :
    for (let node of document.body.childNodes) < alert(node); // покажет все узлы из коллекции >

    Это работает, потому что коллекция является перебираемым объектом (есть требуемый для этого метод Symbol.iterator ).

    1. Методы массивов не будут работать, потому что коллекция – это не массив:
    alert(document.body.childNodes.filter); // undefined (у коллекции нет метода filter!)

    Первый пункт – это хорошо для нас. Второй – бывает неудобен, но можно пережить. Если нам хочется использовать именно методы массива, то мы можем создать настоящий массив из коллекции, используя Array.from :

    alert( Array.from(document.body.childNodes).filter ); // сделали массив

    DOM-коллекции – только для чтения

    DOM-коллекции, и даже более – все навигационные свойства, перечисленные в этой главе, доступны только для чтения.

    Мы не можем заменить один дочерний узел на другой, просто написав childNodes[i] = . .

    Для изменения DOM требуются другие методы. Мы увидим их в следующей главе.

    DOM-коллекции живые

    Почти все DOM-коллекции, за небольшим исключением, живые. Другими словами, они отражают текущее состояние DOM.

    Если мы сохраним ссылку на elem.childNodes и добавим/удалим узлы в DOM, то они появятся в сохранённой коллекции автоматически.

    Не используйте цикл for..in для перебора коллекций

    Коллекции перебираются циклом for..of . Некоторые начинающие разработчики пытаются использовать для этого цикл for..in .

    Не делайте так. Цикл for..in перебирает все перечисляемые свойства. А у коллекций есть некоторые «лишние», редко используемые свойства, которые обычно нам не нужны:

       

    Соседи и родитель

    Соседи – это узлы, у которых один и тот же родитель.

    Например, здесь и соседи:

    • говорят, что – «следующий» или «правый» сосед
    • также можно сказать, что «предыдущий» или «левый» сосед .

    Следующий узел того же родителя (следующий сосед) – в свойстве nextSibling , а предыдущий – в previousSibling .

    Родитель доступен через parentNode .

    // родителем является alert( document.body.parentNode === document.documentElement ); // выведет true // после идёт alert( document.head.nextSibling ); // HTMLBodyElement // перед находится alert( document.body.previousSibling ); // HTMLHeadElement

    Навигация только по элементам

    Навигационные свойства, описанные выше, относятся ко всем узлам в документе. В частности, в childNodes находятся и текстовые узлы и узлы-элементы и узлы-комментарии, если они есть.

    Но для большинства задач текстовые узлы и узлы-комментарии нам не нужны. Мы хотим манипулировать узлами-элементами, которые представляют собой теги и формируют структуру страницы.

    Поэтому давайте рассмотрим дополнительный набор ссылок, которые учитывают только узлы-элементы:

    Эти ссылки похожи на те, что раньше, только в ряде мест стоит слово Element :

    • children – коллекция детей, которые являются элементами.
    • firstElementChild , lastElementChild – первый и последний дочерний элемент.
    • previousElementSibling , nextElementSibling – соседи-элементы.
    • parentElement – родитель-элемент.

    Зачем нужен parentElement ? Разве может родитель быть не элементом?

    Свойство parentElement возвращает родитель-элемент, а parentNode возвращает «любого родителя». Обычно эти свойства одинаковы: они оба получают родителя.

    За исключением document.documentElement :

    alert( document.documentElement.parentNode ); // выведет document alert( document.documentElement.parentElement ); // выведет null

    Причина в том, что родителем корневого узла document.documentElement ( ) является document . Но document – это не узел-элемент, так что parentNode вернёт его, а parentElement нет.

    Эта деталь может быть полезна, если мы хотим пройти вверх по цепочке родителей от произвольного элемента elem к , но не до document :

    while(elem = elem.parentElement) < // идти наверх до alert( elem ); >

    Изменим один из примеров выше: заменим childNodes на children . Теперь цикл выводит только элементы:

    Выбор соседних узлов в JavaScript: увеличиваем производительность

    Для быстрого выбора соседних узлов вы можете использовать CSS-комбинаторы: + позволит выбрать непосредственного соседа, а ~ – всех последующих элементов на том же уровне вложенности. Вот пример использования комбинатора + :

    Скопировать код

    h1 + p < color: blue; >/* Соседний абзац станет синим */

    А вот как можно использовать ~ :

    Скопировать код

    h1 ~ p < color: green; >/* Закрасим в зелёный каждый следующий абзац */

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

    Работа с соседями в JavaScript

    Когда CSS не достаточен для динамического управления элементами, на помощь приходит JavaScript.

    Поиск соседних элементов через DOM

    Прямой доступ к соседним элементам можно получить с помощью методов DOM. node.nextElementSibling позволяет выбрать следующий элемент:

    Скопировать код

    const nextSibling = element.nextElementSibling; // Обращаемся к следующему соседу

    А node.previousElementSibling предназначен для доступа к предыдущему элементу:

    Скопировать код

    const previousSibling = element.previousElementSibling; // Возвращаемся к предыдущему соседу

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

    Специальные функции для работы с соседями

    Для более сложного доступа к соседним элементам можно использовать функцию getSiblings(element) , которая возвращает массив всех соседей данного элемента:

    Скопировать код

    function getSiblings(element) < let siblings = []; Array.from(element.parentNode.children).forEach(sibling => < if(sibling !== element) < siblings.push(sibling); >>); return siblings; > // Получаем массив соседей элемента

    Использование jQuery

    Если вы работаете с jQuery, метод .siblings() может значительно упростить задачи:

    Скопировать код

    $('#innerId').siblings().hide(); // Прячем всех соседей

    Хотя, стандартный DOM API может выполнять аналогичные действия без использования jQuery.

    Текстовые и комментарные узлы? Нам они не нужны!

    Работая с previousSibling , будьте осторожны с текстовыми и комментарными узлами. Лучше воспользоваться previousElementSibling и nextElementSibling , чтобы воздействовать только на нужные элементы, вместо текстовых узлов.

    Визуализация

    HTML-элементы можно представить как созвездия, взаимосвязанные в пространстве DOM-дерева:

    Скопировать код

    Галактика DOM: �� 
    ��

    .

    �� Выбери меня! �� . // Сфокусируемся на элементе 'a', он сосед 'span'

    Используйте комбинаторы + (для непосредственного соседа) и ~ (для всех последующих элементов), чтобы перемещаться между этими созвездиями:

    Скопировать код

    /* Меняем цвет первого элемента 'a', который идёт сразу за 'span' */ span + a < color: #FFD700; /* Внесём немного космической стилистики */ >/* ИЛИ */ /* Меняем цвет всех элементов 'a', которые идут после 'span' */ span ~ a < color: #ADD8E6; /* Перебираемся в свет далёких звёзд */ >

    Этот подход облегчает навигацию в сложно устроенном пространстве DOM и позволяет посмотреть на веб-разработку под иным углом.

    Обработка динамически генерируемого содержимого

    Когда HTML-содержимое генерируется динамически, особенно важно научиться эффективно и безопасно управлять соседними узлами.

    Безопасное взаимодействие с невидимыми узлами

    В динамическом HTML важна безопасность ваших действий. Гарантируйте, что ваши манипуляции с соседями не приведут к ошибкам с узлами, которые в данный момент не отображаются. Кешируйте узлы перед выполнением циклов, чтобы снизить нагрузку на перерисовку и пересчёт макета.

    Продвинутые техники работы со соседями

    Сложные задачи, например выбор соседей по определенным критериям или массовое взаимодействие с ними, требуют использования JavaScript-методов для работы с массивами и различных приемов работы с DOM. Убедитесь, что вы готовы к этим вызовам.

    Полезные материалы

    1. Детские и соседние селекторы | CSS-Tricks — подробное руководство по использованию CSS селекторов для соседних элементов.
    2. Комбинатор последующего соседа — CSS | MDN — обзор комбинаторов последующих соседей на MDN.
    3. Комбинатор следующего соседа — CSS | MDN — детальное руководство по комбинаторам следующих соседей на MDN.
    4. Комбинаторы CSS — учебник по применению различных комбинаторов в CSS.
    5. Справочник селекторов CSS — всё, что нужно знать о CSS селекторах.
    6. Запоминалка по селекторам CSS — SitePoint — быстрый обзор для срочного изучения.
    7. Укрощение сложных CSS-селекторов — Smashing Magazine — углубленное освоение продвинутых CSS-селекторов.

    Навигация по DOM-элементам

    Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

    Более новая информация по этой теме находится на странице https://learn.javascript.ru/dom-navigation.

    DOM позволяет делать что угодно с HTML-элементом и его содержимым, но для этого нужно сначала нужный элемент получить.

    Доступ к DOM начинается с объекта document . Из него можно добраться до любых узлов.

    Так выглядят основные ссылки, по которым можно переходить между узлами DOM:

    Посмотрим на них повнимательнее.

    Сверху documentElement и body

    Самые верхние элементы дерева доступны напрямую из document .

    = document.documentElement Первая точка входа – document.documentElement . Это свойство ссылается на DOM-объект для тега . = document.body Вторая точка входа – document.body , который соответствует тегу .

    В современных браузерах (кроме IE8-) также есть document.head – прямая ссылка на

    Есть одна тонкость: document.body может быть равен null

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

    В частности, если скрипт находится в , то в нём недоступен document.body .

    Поэтому в следующем примере первый alert выведет null :

             

    В DOM активно используется null

    В мире DOM в качестве значения, обозначающего «нет такого элемента» или «узел не найден», используется не undefined , а null .

    Дети: childNodes, firstChild, lastChild

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

    • Дочерние элементы (или дети) – элементы, которые лежат непосредственно внутри данного. Например, внутри обычно лежат и .
    • Потомки – все элементы, которые лежат внутри данного, вместе с их детьми, детьми их детей и так далее. То есть, всё поддерево DOM.

    Псевдо-массив childNodes хранит все дочерние элементы, включая текстовые.

    Пример ниже последовательно выведет дочерние элементы document.body :

    Обратим внимание на маленькую деталь. Если запустить пример выше, то последним будет выведен элемент . На самом-то деле в документе есть ещё текст (обозначенный троеточием), но на момент выполнения скрипта браузер ещё до него не дошёл.

    Пробельный узел будет в итоговом документе, но его ещё нет на момент выполнения скрипта.

    Список детей – только для чтения!

    Скажем больше – все навигационные свойства, которые перечислены в этой главе – только для чтения. Нельзя просто заменить элемент присвоением childNodes[i] = . .

    Изменение DOM осуществляется другими методами, которые мы рассмотрим далее, все навигационные ссылки при этом обновляются автоматически.

    Свойства firstChild и lastChild обеспечивают быстрый доступ к первому и последнему элементу.

    При наличии дочерних узлов всегда верно:

    elem.childNodes[0] === elem.firstChild elem.childNodes[elem.childNodes.length - 1] === elem.lastChild

    Коллекции – не массивы

    DOM-коллекции, такие как childNodes и другие, которые мы увидим далее, не являются JavaScript-массивами.

    В них нет методов массивов, таких как forEach , map , push , pop и других.

    var elems = document.documentElement.childNodes; elems.forEach(function(elem) < // нет такого метода! /* . */ >);

    Именно поэтому childNodes и называют «коллекция» или «псевдомассив».

    Это возможно, основных варианта два:

      Применить метод массива через call/apply :

    var elems = document.documentElement.childNodes; [].forEach.call(elems, function(elem) < alert( elem ); // HEAD, текст, BODY >);
    var elems = document.documentElement.childNodes; elems = Array.prototype.slice.call(elems); // теперь elems - массив elems.forEach(function(elem) < alert( elem.tagName ); // HEAD, текст, BODY >);

    Нельзя перебирать коллекцию через for..in

    Ранее мы говорили, что не рекомендуется использовать для перебора массива цикл for..in .

    Коллекции – наглядный пример, почему нельзя. Они похожи на массивы, но у них есть свои свойства и методы, которых в массивах нет.

    К примеру, код ниже должен перебрать все дочерние элементы . Их, естественно, два: и . Максимум, три, если взять ещё и текст между ними.

    Но в примере ниже alert сработает не три, а целых 5 раз!

    var elems = document.documentElement.childNodes; for (var key in elems) < alert( key ); // 0, 1, 2, length, item >

    Цикл for..in выведет не только ожидаемые индексы 0 , 1 , 2 , по которым лежат узлы в коллекции, но и свойство length (в коллекции оно enumerable), а также функцию item(n) – она никогда не используется, возвращает n-й элемент коллекции, проще обратиться по индексу [n] .

    В реальном коде нам нужны только элементы, мы же будем работать с ними, а служебные свойства – не нужны. Поэтому желательно использовать for(var i=0; i

    Соседи и родитель

    Доступ к элементам слева и справа данного можно получить по ссылкам previousSibling / nextSibling .

    Родитель доступен через parentNode . Если долго идти от одного элемента к другому, то рано или поздно дойдёшь до корня DOM, то есть до document.documentElement , а затем и document .

    Навигация только по элементам

    Навигационные ссылки, описанные выше, равно касаются всех узлов в документе. В частности, в childNodes сосуществуют и текстовые узлы и узлы-элементы и узлы-комментарии, если есть.

    Но для большинства задач текстовые узлы нам не интересны.

    Поэтому посмотрим на дополнительный набор ссылок, которые их не учитывают:

    Эти ссылки похожи на те, что раньше, только в ряде мест стоит слово Element :

    • children – только дочерние узлы-элементы, то есть соответствующие тегам.
    • firstElementChild , lastElementChild – соответственно, первый и последний дети-элементы.
    • previousElementSibling , nextElementSibling – соседи-элементы.
    • parentElement – родитель-элемент.

    Зачем parentElement ? Неужели бывают родители не-элементы?

    Свойство elem.parentNode возвращает родитель элемента.

    Оно всегда равно parentElement , кроме одного исключения:

    alert( document.documentElement.parentNode ); // document alert( document.documentElement.parentElement ); // null

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

    Модифицируем предыдущий пример, применив children вместо childNodes .

    Теперь он будет выводить не все узлы, а только узлы-элементы:

    Всегда верны равенства:

    elem.firstElementChild === elem.children[0] elem.lastElementChild === elem.children[elem.children.length - 1]

    В IE8- поддерживается только children

    Других навигационных свойств в этих браузерах нет. Впрочем, как мы увидим далее, можно легко сделать полифил, и они, всё же, будут.

    В IE8- в children присутствуют узлы-комментарии

    С точки зрения стандарта это ошибка, но IE8- также включает в children узлы, соответствующие HTML-комментариям.

    Это может привести к сюрпризам при использовании свойства children , поэтому HTML-комментарии либо убирают либо используют фреймворк, к примеру, jQuery, который даёт свои методы перебора и отфильтрует их.

    Особые ссылки для таблиц

    У конкретных элементов DOM могут быть свои дополнительные ссылки для большего удобства навигации.

    Здесь мы рассмотрим таблицу, так как это важный частный случай и просто для примера.

    В списке ниже выделены наиболее полезные:

    • table.rows – коллекция строк TR таблицы.
    • table.caption/tHead/tFoot – ссылки на элементы таблицы CAPTION , THEAD , TFOOT .
    • table.tBodies – коллекция элементов таблицы TBODY , по спецификации их может быть несколько.
    • tbody.rows – коллекция строк TR секции.
    • tr.cells – коллекция ячеек TD/TH
    • tr.sectionRowIndex – номер строки в текущей секции THEAD/TBODY
    • tr.rowIndex – номер строки в таблице
    • td.cellIndex – номер ячейки в строке
     
    один два
    три четыре

    Даже если эти свойства не нужны вам прямо сейчас, имейте их в виду на будущее, когда понадобится пройтись по таблице.

    Конечно же, таблицы – не исключение.

    Аналогичные полезные свойства есть у HTML-форм, они позволяют из формы получить все её элементы, а из них – в свою очередь, форму. Мы рассмотрим их позже.

    Интерактивное путешествие

    Для того, чтобы убедиться, что вы разобрались с навигацией по DOM-ссылкам – вашему вниманию предлагается интерактивное путешествие по DOM.

    Ниже вы найдёте документ (в ифрейме), и кнопки для перехода по нему.

    Изначальный элемент – . Попробуйте по ссылкам найти «информацию». Или ещё чего-нибудь.

    Вы также можете открыть документ в отдельном окне и походить по нему в браузерной консоли разработчика, чтобы лучше понять разницу между показанным там DOM и реальным.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *