Как правильно получить всех соседей элемента?
Имеется такой код:
function getAllNextSiblings(element) {
let siblings = [];
let sibling = element;
while (sibling.nextSibling) {
sibling = sibling.nextSibling;
sibling.nodeType == 1 && sibling.classList.contains("main-element__block") && siblings.push(sibling);
}
return siblings;
}
const getElementsCollection = () => {
const containerElements = document.querySelectorAll(".main-element");
const storageOfSetsElements = [];
[...containerElements].forEach(containerElement => {
const mainObject = {
heading: "",
subElements: []
};
mainObject.heading = containerElement.textContent;
const getSubElements = () => {
[...getAllNextSiblings(containerElement.nextElementSibling)].forEach(subElementOfContainer => {
let subObj = {};
const $idSelector = subElementOfContainer.querySelector(".main-element__block--id");
const $valueSelector = subElementOfContainer.querySelector(".main-element__block--value");
subObj.id = $idSelector.textContent.trim();
subObj.value = $valueSelector.textContent.trim();
mainObject.subElements.push(subObj);
});
storageOfSetsElements.push(mainObject);
};
const nextElement = containerElement.nextElementSibling;
nextElement.classList.contains("main-element__button") && getSubElements();
});
return storageOfSetsElements;
};
getElementsCollection();
И такая верстка:
<div class="container">
<div class="main-element">Контейнер 1</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-1">
<span class="main-element__block--id">1 —</span>
<span class="main-element__block--value">Клубника</span>
</div>
<div class="main-element__block element-2">
<span class="main-element__block--id">2 —</span>
<span class="main-element__block--value">Банан</span>
</div>
<div class="main-element__block element-3">
<span class="main-element__block--id">3 —</span>
<span class="main-element__block--value">Апельсин</span>
</div>
<div class="main-element__block element-4">
<span class="main-element__block--id">4 —</span>
<span class="main-element__block--value">Вишня</span>
</div>
<div class="main-element">Контейнер 2</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-5">
<span class="main-element__block--id">5 —</span>
<span class="main-element__block--value">Ананас</span>
</div>
<div class="main-element">Контейнер 3</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-6">
<span class="main-element__block--id">6 —</span>
<span class="main-element__block--value">Авокадо</span>
</div>
<div class="main-element__block element-7">
<span class="main-element__block--id">7 —</span>
<span class="main-element__block--value">Груша</span>
</div>
</div>
На первый взгляд всё работает, но стоит заглянуть в полученные данные, и сразу видно ошибку.
function getAllNextSiblings(element) {
let siblings = [];
let sibling = element;
while (sibling.nextSibling) {
sibling = sibling.nextSibling;
sibling.nodeType == 1 && sibling.classList.contains("main-element__block") && siblings.push(sibling);
}
return siblings;
}
const getElementsCollection = () => {
const containerElements = document.querySelectorAll(".main-element");
const storageOfSetsElements = [];
[...containerElements].forEach(containerElement => {
const mainObject = {
heading: "",
subElements: []
};
mainObject.heading = containerElement.textContent;
const getSubElements = () => {
[...getAllNextSiblings(containerElement.nextElementSibling)].forEach(subElementOfContainer => {
let subObj = {};
const $idSelector = subElementOfContainer.querySelector(".main-element__block--id");
const $valueSelector = subElementOfContainer.querySelector(".main-element__block--value");
subObj.id = $idSelector.textContent.trim();
subObj.value = $valueSelector.textContent.trim();
mainObject.subElements.push(subObj);
});
storageOfSetsElements.push(mainObject);
};
const nextElement = containerElement.nextElementSibling;
nextElement.classList.contains("main-element__button") && getSubElements();
});
return storageOfSetsElements;
};
console.log(getElementsCollection());
<div class="container">
<div class="main-element">Контейнер 1</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-1">
<span class="main-element__block--id">1 —</span>
<span class="main-element__block--value">Клубника</span>
</div>
<div class="main-element__block element-2">
<span class="main-element__block--id">2 —</span>
<span class="main-element__block--value">Банан</span>
</div>
<div class="main-element__block element-3">
<span class="main-element__block--id">3 —</span>
<span class="main-element__block--value">Апельсин</span>
</div>
<div class="main-element__block element-4">
<span class="main-element__block--id">4 —</span>
<span class="main-element__block--value">Вишня</span>
</div>
<div class="main-element">Контейнер 2</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-5">
<span class="main-element__block--id">5 —</span>
<span class="main-element__block--value">Ананас</span>
</div>
<div class="main-element">Контейнер 3</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-6">
<span class="main-element__block--id">6 —</span>
<span class="main-element__block--value">Авокадо</span>
</div>
<div class="main-element__block element-7">
<span class="main-element__block--id">7 —</span>
<span class="main-element__block--value">Груша</span>
</div>
</div>
По идее, скрипт должен найти каждый элемент с классом: "main-element", от него находим стоящий рядом элемент с классом: "main-element__button", и уже от этого элемента ищем стоящие рядом с ним элементы с классом: "main-element__block", получив ссылки на коллекцию элементов, перебираем их в цикле, и получаем определенные данные, а уже далее проделываем остальные операции, с которыми проблем нет. Всё.
Основной проблемой является тот факт, что по идее в первый объект должны попасть ссылки на стоящие рядом с первой "кнопкой", элементы с селектором "main-element", но, однако же происходит другое. Каким-то образом, в первый объект попадают элементы которые должны попасть во второй объект, и так далее, то есть во второй падают данные из третьего, и так далее.
Получается что функция игнорирует селекторы "main-element" && "main-element__button" и собирает "main-element__block", но это не правильно...
Ожидаемый результат:
[
{
"heading": "Контейнер 1",
"subElements": [
{
"id": "1 —",
"value": "Клубника"
},
{
"id": "2 —",
"value": "Банан"
},
{
"id": "3 —",
"value": "Апельсин"
},
{
"id": "4 —",
"value": "Вишня"
}
]
},
{
"heading": "Контейнер 2",
"subElements": [
{
"id": "5 —",
"value": "Ананас"
}
]
},
{
"heading": "Контейнер 3",
"subElements": [
{
"id": "6 —",
"value": "Авокадо"
},
{
"id": "7 —",
"value": "Груша"
}
]
}
]
Получаемый результат:
[
{
"heading": "Контейнер 1",
"subElements": [
{
"id": "1 —",
"value": "Клубника"
},
{
"id": "2 —",
"value": "Банан"
},
{
"id": "3 —",
"value": "Апельсин"
},
{
"id": "4 —",
"value": "Вишня"
},
{
"id": "5 —",
"value": "Ананас"
},
{
"id": "6 —",
"value": "Авокадо"
},
{
"id": "7 —",
"value": "Груша"
}
]
},
{
"heading": "Контейнер 2",
"subElements": [
{
"id": "5 —",
"value": "Ананас"
},
{
"id": "6 —",
"value": "Авокадо"
},
{
"id": "7 —",
"value": "Груша"
}
]
},
{
"heading": "Контейнер 3",
"subElements": [
{
"id": "6 —",
"value": "Авокадо"
},
{
"id": "7 —",
"value": "Груша"
}
]
}
]
Это возможно как-то решить? Что нужно исправить в коде чтобы получать "правильный" набор данныx? И вообще, почему это происходит?
Ответы (1 шт):
Если не хочется "тянуть" всю библиотеку jQuery, тогда можно создать упрощённый аналог метода .nextUntil(). И, если правильно его использовать, он подойдёт для любой вложенности.
function fNextUntil(el, sSelector, sFilter) {
let aSiblings = [];
el = el.nextElementSibling;
while (el) {
if (el.matches(sSelector)) break;
if (sFilter && !el.matches(sFilter)) {
el = el.nextElementSibling;
continue;
}
aSiblings.push({
id: el.querySelector('.main-element__block--id').textContent,
value: el.querySelector('.main-element__block--value').textContent
});
el = el.nextElementSibling;
}
return aSiblings;
}
let aResult = [];
[...document.querySelectorAll('.main-element')].forEach((el) => {
aResult.push({
heading: el.textContent,
subElements: fNextUntil(el, '.main-element', '.main-element__block')
});
});
// Выводим результат в консоль
console.log(aResult);
<div class="container">
<div class="main-element">Контейнер 1</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-1">
<span class="main-element__block--id">1 —</span>
<span class="main-element__block--value">Клубника</span>
</div>
<div class="main-element__block element-2">
<span class="main-element__block--id">2 —</span>
<span class="main-element__block--value">Банан</span>
</div>
<div class="main-element__block element-3">
<span class="main-element__block--id">3 —</span>
<span class="main-element__block--value">Апельсин</span>
</div>
<div class="main-element__block element-4">
<span class="main-element__block--id">4 —</span>
<span class="main-element__block--value">Вишня</span>
</div>
<div class="main-element">Контейнер 2</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-5">
<span class="main-element__block--id">5 —</span>
<span class="main-element__block--value">Ананас</span>
</div>
<div class="main-element">Контейнер 3</div>
<div class="main-element__button">Кнопка</div>
<div class="main-element__block element-6">
<span class="main-element__block--id">6 —</span>
<span class="main-element__block--value">Авокадо</span>
</div>
<div class="main-element__block element-7">
<span class="main-element__block--id">7 —</span>
<span class="main-element__block--value">Груша</span>
</div>
</div>