Есть ли способ выбрать узлы-братья?
По некоторым причинам производительности я пытаюсь найти способ выбрать только узлы-братья выбранного узла. Например,
<div id="outer">
<div id="inner1"> </div>
<div id="inner2"> </div>
<div id="inner3"> </div>
<div id="inner4"> </div>
</div>
Если я выбрал inner1 узел, есть ли способ для меня, чтобы получить доступ к его братьям и сестрам, inner2-4
узлы?
17 ответов
Ну... конечно... просто получить доступ к родителю, а затем к детям.
node.parentNode.childNodes[]
или... используя jQuery:
$('#innerId').siblings()
Редактировать: Cletus как всегда вдохновляет. Я копал дальше. Вот как JQuery получает братьев и сестер по существу:
function getChildren(n, skipMe){
var r = [];
for ( ; n; n = n.nextSibling )
if ( n.nodeType == 1 && n != skipMe)
r.push( n );
return r;
};
function getSiblings(n) {
return getChildren(n.parentNode.firstChild, n);
}
var sibling = node.nextSibling;
Это вернет брата сразу после него, или null больше не доступен. Точно так же вы можете использовать previousSibling
,
[Редактировать] Если подумать, это не даст следующий div
тег, но пробел после узла. Лучше кажется
var sibling = node.nextElementSibling;
Там также существует previousElementSibling
,
Быстрый:
var siblings = n => [...n.parentElement.children].filter(c=>c!=n)
https://codepen.io/anon/pen/LLoyrP?editors=1011
Получить дочерние родительские элементы в виде массива, отфильтровать этот элемент.
Редактировать:
И отфильтровать текстовые узлы (Спасибо pmrotule):
var siblings = n => [...n.parentElement.children].filter(c=>c.nodeType == 1 && c!=n)
С 2017 года:
прямой ответ: element.nextElementSibling
для получения правильного элемента родного брата. также у вас есть element.previousElementSibling
за предыдущий
отсюда довольно просто получить все последующие
var n = element, ret = [];
while (n = n.nextElementSibling){
ret.push(n)
}
return ret;
Вы проверили метод "Sibling" в jQuery?
sibling: function( n, elem ) {
var r = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
r.push( n );
}
}
return r;
}
n.nodeType == 1 проверяет, является ли элемент узлом html, а n!== исключает текущий элемент.
Я думаю, что вы можете использовать ту же функцию, весь этот код, кажется, ванильный JavaScript.
Есть несколько способов сделать это.
Любой из следующих должен сделать трюк.
// METHOD A (ARRAY.FILTER, STRING.INDEXOF)
var siblings = function(node, children) {
siblingList = children.filter(function(val) {
return [node].indexOf(val) != -1;
});
return siblingList;
}
// METHOD B (FOR LOOP, IF STATEMENT, ARRAY.PUSH)
var siblings = function(node, children) {
var siblingList = [];
for (var n = children.length - 1; n >= 0; n--) {
if (children[n] != node) {
siblingList.push(children[n]);
}
}
return siblingList;
}
// METHOD C (STRING.INDEXOF, ARRAY.SPLICE)
var siblings = function(node, children) {
siblingList = children;
index = siblingList.indexOf(node);
if(index != -1) {
siblingList.splice(index, 1);
}
return siblingList;
}
К вашему сведению: кодовая база jQuery - отличный ресурс для изучения Javascript класса A.
Вот превосходный инструмент, который показывает базу кода jQuery в очень обтекаемой форме. http://james.padolsey.com/jquery/
Да. Вам нужно использовать служебную функцию для достижения этой цели, как описано здесь https://gomakethings.com/how-to-get-all-of-an-elements-siblings-with-vanilla-js/ article. Следующая функция вернет массив, содержащий все элементы данного элемента.
var getSiblings = function (elem) { // Setup siblings array and get the first sibling var siblings = []; var sibling = elem.parentNode.firstChild; // Loop through each sibling and push to the array while (sibling) { if (sibling.nodeType === 1 && sibling !== elem) { siblings.push(sibling); } sibling = sibling.nextSibling } return siblings; };
Чтобы использовать вышеуказанный метод, вы находите элемент в DOM, а затем передаете его в метод getSiblings().
var element = document.querySelector('#elementId'); var siblings = getSiblings(element);
Вот как вы можете получить предыдущих, следующих и всех братьев и сестер (обе стороны):
function prevSiblings(target) {
var siblings = [], n = target;
while(n = n.previousElementSibling) siblings.push(n);
return siblings;
}
function nextSiblings(target) {
var siblings = [], n = target;
while(n = n.nextElementSibling) siblings.push(n);
return siblings;
}
function siblings(target) {
var prev = prevSiblings(target) || [],
next = nexSiblings(target) || [];
return prev.concat(next);
}
Используйте document.querySelectorAll() и Loops и итерации
function sibblingOf(children,targetChild){
var children = document.querySelectorAll(children);
for(var i=0; i< children.length; i++){
children[i].addEventListener("click", function(){
for(var y=0; y<children.length;y++){children[y].classList.remove("target")}
this.classList.add("target")
}, false)
}
}
sibblingOf("#outer >div","#inner2");
#outer >div:not(.target){color:red}
<div id="outer">
<div id="inner1">Div 1 </div>
<div id="inner2">Div 2 </div>
<div id="inner3">Div 3 </div>
<div id="inner4">Div 4 </div>
</div>
J Query
$el.siblings();
Родной - последний, Edge13+
[...el.parentNode.children].filter((child) =>
child !== el
);
Родная (альтернатива) - последняя, Edge13+
Array.from(el.parentNode.children).filter((child) =>
child !== el
);
Родной - IE10+
Array.prototype.filter.call(el.parentNode.children, (child) =>
child !== el
);
2023 решения
Эти решения намного короче, чем в принятом ответе.
Решение ES5
Мы можем использоватьсвойство, затем преобразуйте его в и удалите цель из этого массива.
свойство получает только узлы типаElement
во всех современных браузерах (включая IE9). Поэтому фильтрация по типу узла является избыточной.
Для лучшей совместимости с браузером я не использую здесь функциюArray.from
потому что он не поддерживается в IE11.
[].slice.call(HTMLCollection)
- конвертируетHTMLCollection
кArray
.
Документация
var childNodeArray = document.getElementById('somethingOtherThanid').childNodes;
1) Добавить выбранный класс в целевой элемент
2) Найти все дочерние элементы родительского элемента, исключая целевой элемент
3) Удалить класс из целевого элемента
<div id = "outer">
<div class="item" id="inner1">Div 1 </div>
<div class="item" id="inner2">Div 2 </div>
<div class="item" id="inner3">Div 3 </div>
<div class="item" id="inner4">Div 4 </div>
</div>
function getSiblings(target) {
target.classList.add('selected');
let siblings = document.querySelecttorAll('#outer .item:not(.currentlySelected)')
target.classList.remove('selected');
return siblings
}
ЛУЧШЕЕ РЕШЕНИЕ:
let inner2 = event.target.parentNode.querySelector(`#inner2`)
/*Or if you have already stored the inner1 node to a variable called: inner1*/
let inner2 = inner1.parentNode.querySelector(`#inner2`)
В первой строке event.targetбудет узел inner1, если мы нажмем на него. Это parentNodeбудет «внешний» узел, и на узле-партенте мы начинаем поиск ( ), чтобы выбрать внутренний2 узел..querySelector(
ДРУГИЕ РЕШЕНИЯ:
Я перечисляю другие возможные варианты, но они не такие гибкие, так как в них имеет значение последовательность узлов, что делает код хрупким, если мы потом добавим еще один узел к родителю, то весь код сломается, чего мы хотим избежать :
2) Это выбирает первого ребенка (этот индекс начинается с 1, а НЕ с 0)
node.parentNode.childNodes[1]
3) Предположим, что вы уже выбрали inner1Node для переменной, следующего брата вы можете получить:
let inner2Node = inner1Node.nextElementSibling;
4) Предыдущего брата вы можете получить:
let inner1NodeAGAIN = inner2Node.previousElementSibling;
Вы можете получить доступ к следующим родственным узлам с помощью свойства currentNode.nextSibiling .
Вот как вы можете сделать делегирование событий, которое является динамическим способом добавления прослушивателей событий.
document.addEventListener('click', (event) => {
if (event.target.matches("#inner1")) {
console.log(event.targert.nextSibling); //inner2 div
console.log(event.targert.nextSibling.nextSibling); //inner3 div
/* The more of the property you keep appending the further it goes to
the next sibling */
}
})
Мой вариант использования был другим. пришлось выбрать несколько
span
s, у которых не было ни id/классов (ни их родителей), только точка входа (
#target
). Получив это, запустите
querySelectorAll
на его
parent
с соответствующим селектором, используя
:scope
так как вы не можете просто использовать
> div
или же
> span
или же
> .foo
.
Обратите внимание, что этот подход ТАКЖЕ выбирает целевой элемент, если он соответствует селектору. В приведенном ниже примере мне пришлось бы использовать
:scope > span:not(#target)
чтобы избежать выбора точки входа.
x1 = document.getElementById('outer')[0]
.getElementsByTagName('ul')[1]
.getElementsByTagName('li')[2];
x1.setAttribute("id", "buyOnlineLocationFix");