Discovering the siblings of the DOM element that is currently selected

How can I accurately find all the next siblings and previous siblings in JavaScript without using jQuery? I have tried a few methods, but none of them seem to give me the right solution. If an element is selected, I need to retrieve the length of all its next siblings while excluding any white space, spaces, or line breaks.

I am specifically looking for a JavaScript solution and would prefer not to rely on jQuery for this task.

Answer №1

This solution provides a more detailed approach to creating a filter for selecting siblings.

There are three functions available to select only previous, only next, or all siblings. While there is room for improvement, this serves as a solid starting point if you require more specific control over the types of siblings you want to retrieve. It's a useful addition worth considering.

Check out the working example here

Get all next siblings

// This function will start from the current element and collect all the following siblings

function getNextSiblings(elem, filter) {
    var sibs = [];
    while (elem = elem.nextSibling) {
        if (elem.nodeType === 3) continue; // text node
        if (!filter || filter(elem)) sibs.push(elem);
    }
    return sibs;
}

Get all previous siblings

// This function will start from the current element and gather all the previous siblings

function getPreviousSiblings(elem, filter) {
    var sibs = [];
    while (elem = elem.previousSibling) {
        if (elem.nodeType === 3) continue; // text node
        if (!filter || filter(elem)) sibs.push(elem);
    }
    return sibs;
}

Get all siblings

// This function will start from the first child of the current element's parent and fetch all the siblings

function getAllSiblings(elem, filter) {
    var sibs = [];
    elem = elem.parentNode.firstChild;
    do {
        if (elem.nodeType === 3) continue; // text node
        if (!filter || filter(elem)) sibs.push(elem);
    } while (elem = elem.nextSibling)
    return sibs;
}

Example filter to apply to the above functions

// This example filter only considers divs and spans but can be extended
function exampleFilter(elem) {
    switch (elem.nodeName.toUpperCase()) {
        case 'DIV':
            return true;
        case 'SPAN':
            return true;
        default:
            return false;
    }
}

HTML and testing output

HTML

<div id='test'>
    <div id='test2'>asdf</div>
    <br /> sdf
    <div>asdfasdf<span>asdf</span></div>
    <div>a</div>
    <span>a</span>
    <br />
    <div>d</div>
    <hr/>
</div>

JavaScript

var elem;
elem = document.getElementById('test2');

// With the filter applied, alerts 4
alert( getNextSiblings( elem, exampleFilter ).length );

// Without a filter, alerts 7
elem = document.getElementById('test2');
alert( getNextSiblings( elem ).length );

// Alerts 0
elem = document.getElementById('test2');
alert( getPreviousSiblings( elem, exampleFilter ).length );

// Alerts 5
elem = document.getElementById('test2');
alert( getAllSiblings( elem, exampleFilter ).length );

Answer №2

In the scenario where this code is executed within an event handler, it assumes that 'this' refers to the specific element targeted and you aim to manipulate its siblings.

If not applicable to your context, modifications will be necessary.

const siblingElements = [];
let currentNode = this.parentNode.firstChild;

while (currentNode) {
    if (currentNode !== this && currentNode.nodeType === Node.ELEMENT_NODE) {
        siblingElements.push(currentNode);
    }
    currentNode = currentNode.nextElementSibling || currentNode.nextSibling;
}

// The array 'siblingElements' now holds all adjacent siblings of the target element

Answer №3

If you're looking for a quick and efficient solution using ES6, here's a concise method:

const fetchAllRelatedElements = (targetElement, parentContainer) => {
        const childNodes = [...parentContainer.children];
        return childNodes.filter(childNode => childNode !== targetElement);
    }

This function will retrieve all direct children of a specified parent element that are not the target element itself.

Answer №4

An elegant approach utilizing ES2015 (spread & indexOf)

const fetchAdjacentNodes = (element, includeTextNodes) => {
  if (!element || !element.parentNode) return
  
  let siblings = [...element.parentNode[includeTextNodes ? 'childNodes' : 'children']],
      index = siblings.indexOf(element);
  
  siblings.before = siblings.slice(0, index)
  siblings.after = siblings.slice(index + 1)
  
  return siblings
}

// Example of how to use the function:

const $element = document.querySelector('em') // target element 
const adjacentNodes = fetchAdjacentNodes($element)

console.log("Nodes Before <em/>:", display(adjacentNodes.before))
console.log("Nodes After <em/>:", display(adjacentNodes.after))
console.log("All Nodes around <em/>:", display(adjacentNodes))

function display(elements){
  return JSON.stringify(elements.map(node => node.tagName || node.textContent))
}
<div></div>
text node 1
<a></a>
<p></p>
<em></em>
<main></main>
text node 2
<hr/>
<b></b>

Answer №5

This update is in response to @subhaze's previous answer.

The code showcased here utilizes the matches DOM method, which is compatible with modern web browsers:

Check out the Demo here

function matches(elem, filter) {
  if (elem && elem.nodeType === 1) {
    if (filter) {
      return elem.matches(filter);
    }
    return true;
  }
  return false;
}

// Retrieve all next siblings starting from the current element
function getNextSiblings(elem, filter) {
  var sibs = [];
  while (elem = elem.nextSibling) {
    if (matches(elem, filter)) {
      sibs.push(elem);
    }
  }
  return sibs;
}

// Grab all previous siblings starting from the current element
function getPreviousSiblings(elem, filter) {
  var sibs = [];
  while (elem = elem.previousSibling) {
    if (matches(elem, filter)) {
      sibs.push(elem);
    }
  }
  return sibs;
}

// Get all siblings starting from the first child of the current element's parent
function getAllSiblings(elem, filter) {
  var sibs = [];
  elem = elem.parentNode.firstChild;
  while (elem = elem.nextSibling) {
    if (matches(elem, filter)) {
      sibs.push(elem);
    }
  } 
  return sibs;
}

To use these functions, follow the examples below:

var elem = document.querySelector('#test');

// Find all "div" and "span" siblings
var after = getNextSiblings(elem, 'div, span');

// Locate previous siblings with ".list-item" class
var index = getPreviousSiblings(elem, '.list-item');

// Gather all siblings with a title attribute
var allSibs = getAllSiblings(elem, '[title]');

Answer №6

Going back to the year 2017:
Perhaps there exists a superior solution, however this one is good and slightly cleaner

function findSiblingElement(domElement, query) {
   var siblingElements = domElement.parentElement.querySelectorAll(query);
   return [].slice.call(siblingElements).filter( element => element != domElement);
}

Answer №7

Here is a more concise alternative:

const siblings = [...elem.parentNode.children].filter(node => node !== elem)];

Answer №8

To obtain every child of the parent element, simply exclude the element from the selection.

Answer №9

This solution was previously shared here in response to a similar query.

There are multiple approaches to achieve this.

Any of the following methods should work effectively.

// METHOD X (FILTERING WITH INDEXOF)
var findSiblings = function(currentNode, allChildren) {
    let siblingList = allChildren.filter(function(child) {
        return [currentNode].indexOf(child) != -1;
    });
    return siblingList;
}

// METHOD Y (FOR LOOP AND ARRAY PUSH)
var findSiblings = function(currentNode, allChildren) {
    var siblingList = [];
    for (let i = allChildren.length - 1; i >= 0; i--) {
        if (allChildren[i] != currentNode) {
            siblingList.push(allChildren[i]);
        }  
    }
    return siblingList;
}

// METHOD Z (INDEXOF AND SPLICE)
var findSiblings = function(currentNode, allChildren) {
   let siblingList = allChildren;
   let index = siblingList.indexOf(currentNode);
   if(index != -1) {
       siblingList.splice(index, 1);
   }
   return siblingList;
}

For those interested in diving deep into top-notch JavaScript, exploring the jQuery codebase is highly recommended.

Check out this amazing tool that presents the jQuery codebase in a streamlined manner.

Answer №10

Find all the elements before this one.

// Using jQuery (optional filter selector)
$el.prevAll($filter);

// Utilizing Vanilla JavaScript (optional filter function)
function getPreviousSiblings(elem, filter) {
  var siblings = [];
  while (elem = elem.previousSibling) {
      if (elem.nodeType === 3) continue; // Skip text nodes
      if (!filter || filter(elem)) siblings.push(elem);
  }
  return siblings;
}

Locate all the elements after this one.

// jQuery method (optional selector filter)
$el.nextAll($filter);

// Native JavaScript implementation (optional filter function)
function getNextSiblings(elem, filter) {
        var siblings = [];
        var nextElem = elem.parentNode.firstChild;
        do {
            if (nextElem.nodeType === 3) continue; // Ignore text nodes
            if (nextElem === elem) continue; // Ignore target element
            if (nextElem === elem.nextElementSibling) {
                if (!filter || filter(elem)) {
                    siblings.push(nextElem);
                    elem = nextElem;
                }
            }
        } while(nextElem = nextElem.nextSibling)
        return siblings;
    }

Example of a filtering function:

function exampleFilter(elem) {
  switch (elem.nodeName.toUpperCase()) {
    case 'DIV':
      return true;
    case 'SPAN':
      return true;
    default:
      return false;
  }
}

Answer №11

This method is specifically designed to assist in selecting all siblings of a chosen element

The following technique proved to be effective in selecting ALL SIBLINGS (excluding the selected item itself) within the same PARENT container (it's similar to how your parent knows all your siblings, but you may only be aware of your immediate elder sibling as previousElementSibling and immediate younger sibling as nextElementSibling). Quite an interesting analogy!

The Implementation

const allSiblings = Array.from(YOUR_SELECTION.parentElement.children)
                         .filter(sibling => sibling.UNIQUE_PropertyName !== (YOUR_SELECTION.COMPARABLE/UNIQUE_PropertyName));

// You can skip storing it in a variable if necessary

Illustrative Example

HTML

<div id="mummy">
    <div id="baby_1">Samuel</div>
    <div id="baby_2">Dave</div>
    <div id="baby_3">Shaun</div>
    <div id="baby_4">Michael</div>
    <div id="baby_5" class="selected">Fazlullah</div>
    <div id="baby_6">Samson</div>
    <div id="baby_7">Bais</div>
<div>

Javascript

// In this scenario, I have opted to utilize the children's names as the UNIQUE_Property, thus using the .textContent property 

const selected = document.querySelector('.selected'); // Selecting "Fazlullah" as the target

const allSiblings = Array.from(selected.parentElement.children) // Identifying his parent (.parentElement), followed by the parent's children(.children)
                         .filter(sibling => sibling.textContent !== selected.textContent); // This provides a list (specifically an array) of his lesser-known siblings.

allSiblings.forEach(sibling => {
    console.log(sibling.textContent);
});

If the 'id' attribute of the children was employed, the chained filter method would appear as:

.filter(sibling => sibling.id !== selected.id);

Check out the Demo

Answer №12

Here is an Angular solution for those in need, but it can also be easily converted to plain JavaScript

findNextElements(targetElement: any) {
let element = targetElement as HTMLElement;
let nextElements: any[] = [];

let addElement = false;
element.parentElement?.childNodes.forEach(node => {
  let childNode = node as HTMLElement;

  if (addElement) {
    nextElements.push(childNode);
  }

  if (childNode.id == element.id) {
    addElement = true;
  }
});

return nextElements;

}

Answer №13

In my opinion, I have devised a couple of functions to retrieve both the previous and the next siblings of any given element.

const obtainPreviousAll = el => {
  const allPrevious = [];
  const getPrev = el => {
    if (el !== null) {
      allPrevious.push(el);
      const prevSibling = el.previousElementSibling;
      if (prevSibling !== null) {
        getPrev(prevSibling);
      }
    }
  };
  getPrev(el.previousElementSibling);
  return allPrevious;
};

const obtainNextAll = el => {
  const targetEl = el;
  const allNext = [];
  const getAll = el => {
    if (el != null) {
      allNext.push(el);
      const nextSibling = el.nextElementSibling;
      if (nextSibling !== null) {
        getAll(nextSibling);
      }
    }
  };
  getAll(targetEl.nextElementSibling);
  return allNext;
};

All you need to do is execute these functions with a node obtained using getElementById.

Answer №14

An alternative approach is to utilize a generator function in conjunction with

Element.parentNode.firstElementChild
and Element.nextElementSibling:

function* findSiblings(element) {

  let next = element?.parentNode?.firstElementChild;
  
  while (next !== null && next !== undefined) {
    if (element !== next) {
      yield next;
    }
    next = next.nextElementSibling;
  }

}
const mainEl = document.querySelector('main');

for (const el of findSiblings(mainEl)) {
  console.log(el);
}

Answer №15

Discover a simple technique to locate all sibling elements of the currently selected DOM element https://i.sstatic.net/51O87mpH.png

TS

import {Component, ElementRef } from '@angular/core';

@Component({
  selector: 'app-json-block',
  templateUrl: './json-block.component.html',
  styleUrls: ['./json-block.component.scss'],
})

export class JsonBlockComponent {
  constructor(private jsonBlockContainer: ElementRef) { }

  ngOnChanges(changes: SimpleChanges): void {
  console.log(this.jsonBlockContainer.nativeElement.parentElement.children);
      }
   }

HTML

<div class="jsonBlock" #jsonBlockContainer>
  <h2>
    {{ data.title }}
  </h2>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can AngularJS store {{expressions}} by assigning them to a $scope variable?

I am currently working on a Rails application, incorporating AngularJS for certain aspects of the frontend. Can I save {{expressions}} as a value in $scope.variable? Below is the code snippet: Displayed is the Angular controller // Data is retrieved fr ...

The PHP script receives an empty string value passed from JavaScript

I am struggling to pass a string from my JavaScript code to my PHP code. Here is the code snippet that triggers when I hit Enter in a text input: $('#user').keypress(function(e) { if(e.which == 13) { var val = $(this).val(); ...

Tips for determining the final cost post discount entry

Calculate the final cost with discount Issue with OnChange event function CalculateDiscount() { var quantity = document.getElementById("ticket-count").innerText; var price = document.getElementById("item-price").innerText; var discount = document.getEle ...

Dynamic SVG circles with timer and progress animation

Is there a way to modify the following: var el = document.getElementById('graph'); // get canvas var options = { percent: el.getAttribute('data-percent') || 25, size: el.getAttribute('data-size') || 220, lineW ...

Obtaining data from a callback function within a NodeJS application

There is a function in my code that performs a backend call to retrieve an array of names. The function looks something like this: module.exports.getTxnList = function(index, callback) { ....some operations ..... .... callback(null, respon ...

Discovering the perfect CSS unit for this specific number

I have an input field of type text <input type="text"> Currently, I am utilizing JavaScript's ClientRect to retrieve caret details. The ClientRect object structure is as follows: [object ClientRect] { [functions]: , __proto__: { } ...

Pictures that can be chosen in a fashion similar to checking off a box

I am just starting out in the world of web development and I have a specific idea in mind. I want to create images that act like checkboxes, where only one can be selected at a time. Here is an example of what I'm looking for: https://i.sstatic.net/Nu ...

The div escapes the container and falls down to the one below it

I am encountering an issue with the layout of my outer container, which contains a column of numbers and animated text. The problem arises when the animated text, supposed to be beside the number column, drops under the numbers before bouncing back up as i ...

Creating custom folding icons for the Vue Treeselect Component: A step-by-step guide

I am currently working on a Vue TreeSelect component within my Nuxt application. However, I am facing an issue with customizing the folding icons in the Treeselect component: https://i.sstatic.net/46XvO.png Is there a way to achieve this? I attempted to ...

Tips for assigning unique non-changing keys to siblings in React components

I've been searching for a solution for more than an hour without success. The structure of the data is as follows: const arr = [ { id: 1, title: 'something', tags: ['first', 'second', 'third'] }, { id: 2, t ...

Exploring the usage of multidimensional arrays in React components

How can I extract data such as teamId from the "teams" array using datas.map() method in a multidimensional array? Any tips or help would be appreciated. Is there another way to map out this data? { "gameId": 3226262256, "platformId": "NA1", " ...

What is the best way to reset a dropdown list value in angular?

Is there a way to erase the selected value from an Angular dropdown list using either an x button or a clear button? Thank you. Code <div fxFlex fxLayout="row" formGroupName="people"> <mat-form-field appearance=&quo ...

Is it possible to utilize personalized functionalities in the main.js file of the Firefox Addon SDK?

Why am I encountering difficulties accessing custom functions with the Firefox Addon SDK? In the code snippet provided below, whenever I click on context menu Sub Item 1, it does not work as intended; It is trying to access the custom function verifyTest( ...

retrieve the value from the angularfire database list subscription

I need help with calculating the total sum of 'amount' values in my 'expenses' list. Take a look at my database: https://i.sstatic.net/lN3OQ.gif Although the log inside the subscribe function correctly shows a total of 1700, I'm ...

How to incorporate click and drag feature for rotating a 3D object using vue.js

Whenever I click a button, the rotation function is activated and allows me to rotate an object by hovering over it with my mouse. Now, I want to modify this behavior so that the object rotates only when I click on it and move the mouse (while holding dow ...

What is the process of parsing a Java property file in AngularJS?

Is it possible to access a properties file from an AngularJS application that is located outside of the web server? For example, in Java we can access a property file deployed separately from the project. Is there a way to achieve this in AngularJS? I at ...

What could be causing my jQuery event handler to not work properly when connected to numerous elements?

I have implemented jquery to dynamically add multiple "addTask" form elements to a "ul" on the webpage every time a link is clicked. $('span a').click(function(e){ e.preventDefault(); $('<li>\ <ul>\ ...

Using TypeScript to Verify the Existence of Words in a String

Is there a way in typescript to find specific words within a given string? For example: If we have a list: ['Mr', 'Mrs', 'FM.', 'Sir'] and a string named 'Sir FM. Sam Manekshaw'. The words 'Sir' ...

Submitting Forms Using PHP with Ajax

My form code is as follows: <form id="editpageform" method="post"> <div class="form-group"> <label for="title">Page Title</label> <input type="text" id="title" name="title" placeholder ...

Using NodeJS to perform asynchronous tasks with setImmediate while also incorporating private class

Today marks my first time experimenting with setImmediate. I've come to realize that it may not be able to run private class methods. Can someone shed some light on this? Why is that the case? Not Functioning Properly When trying to use a private cl ...