Есть ли способ выбрать узлы-братья?

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

 <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 */
   }
 })

Мой вариант использования был другим. пришлось выбрать несколько spans, у которых не было ни 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");
Другие вопросы по тегам