Related
I want to replace a specific div element with a different one, when it has reached 3 clicks on it. That is the only task, I am trying to accomplish with the code.
I have tried looking at some code that does this but all of them replace it with get go, they don't give you a number amount to specify when to replace it with.
Example: <div id="1"></div> has been clicked on 3 times by a user. Once it exceeds that amount replace it with <div id="3"></div>
Changing the id attribute is not a good idea, instead you can use data- attribute like the following way:
var count = 0; // Declare a variable as counter
$('#1').click(function(){
count++; // Increment the couter by 1 in each click
if(count == 3) // Check the counter
$(this).data('id', '3'); // Set the data attribute
console.log($(this).data('id'));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="1" data-id="1">Click</div>
You could write a JavaScript function that keeps track how often you clicked on a specific DOM element (i. e. the div element with id="1"). As soon as the element was clicked three times, it will be replaced by another DOM element which can be created in JavaScript as well.
var clicks = 0;
function trackClick(el) {
clicks++;
if(clicks === 3) {
var newEl = document.createElement('div');
newEl.textContent = 'Div3';
newEl.id = '3';
el.parentNode.replaceChild(newEl, el);
}
}
<div id="1" onclick="trackClick(this)">Div1</div>
In case you should use a library like jQuery or have another HTML structure, please specify your question to improve this code snippet so that it fits for your purpose.
The main idea is to start listening click events on the first div and count them.
The below code shows this concept. Firstly we put first div into variable to be able to create event listeners on it and also create count variable with initial value: 0. Then pre-make the second div, which will replace the first one later.
And the last part is also obvious: put event listener on a div1 which will increment count and check if it is equal 3 each time click happens.
const div1 = document.querySelector('#id-1');
let count = 0;
// pre-made second div for future replacement
const divToReplace = document.createElement('div');
divToReplace.id = 'id-2';
divToReplace.innerText = 'div 2';
div1.addEventListener('click', () => {
count ++;
if (count === 3) {
div1.parentNode.replaceChild(divToReplace, div1);
}
});
<div id="id-1"> div 1 </div>
Note that this approach is easy to understand, but the code itself is not the best, especially if you will need to reuse that logic. The below example is a bit more complicated - we create a function which takes 2 arguments: one for element to track and another - the element to replace with. Such approach will allow us to reuse functionality if needed.
function replaceAfter3Clicks(elem, newElem) {
let count = 0;
div1.addEventListener('click', () => {
count ++;
if (count === 3) {
elem.parentNode.replaceChild(newElem, elem);
}
});
}
const div1 = document.querySelector('#id-1');
// pre-made second div for future replacement
const div2 = document.createElement('div');
div2.id = 'id-2';
div2.innerText = 'div 2';
replaceAfter3Clicks(div1, div2);
<div id="id-1"> div 1 </div>
If you know, how to use JQuery, just put a click event handler on your div 1. On that handler, increment a click counter to 3. If it reaches 3, replace the div with JQuery again.
If there are multiple divs to replace, use an array of counters instead of a single one, or modify a user-specific data attribute via JQuery.
Using native JavaScript, rather than relying upon library (for all the benefits that might offer), the following approach is possible:
// A named function to handle the 'click' event on the relevant elements;
// the EventObject is passed in, automatically, from EventTarget.addEventListener():
const replaceOn = (event) => {
// caching the element that was clicked (because I'm using an Arrow function
// syntax we can't use 'this' to get the clicked element):
let el = event.target,
// creating a new <div> element:
newNode = document.createElement('div'),
// retrieving the current number of clicks set on the element, after this
// number becomes zero we replace the element. Here we use parseInt() to
// convert the string representation of the number into a base-10 number:
current = parseInt(el.dataset.replaceOn, 10);
// here we update the current number with the decremented number (we use the
// '--' operator to reduce the number by one) and then we update the
// data-replace-on attribute value with the new number:
el.dataset.replaceOn = --current;
// here we discover if that number is now zero:
if (current === 0) {
// if so, we write some content to the created <div> element:
newNode.textContent = "Original element has been replaced.";
// and here we use Element.replaceWith() to replace the current
// 'el' element with the new newNode element:
el.replaceWith(newNode);
}
};
// here we use the [data-replace-on] attribute-selector to search
// through the document for all elements with that attribute, and
// use NodeList.forEach() to iterate over that NodeList:
document.querySelectorAll('[data-replace-on]').forEach(
// using an Arrow function we pass a reference to the current
// Node of the NodeList to the function, and here we use
// EventTarget.addEventListener() to bind the replaceOn function
// (note the deliberate lack of parentheses) to handle the
// 'click' event:
(element) => element.addEventListener('click', replaceOn)
);
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
div {
border: 1px solid #000;
display: inline-block;
padding: 0.5em;
border-radius: 1em;
}
div[data-replace-on] {
cursor: pointer;
}
div[data-replace-on]::before {
content: attr(data-replace-on);
}
<div data-replace-on="3"></div>
<div data-replace-on="13"></div>
<div data-replace-on="1"></div>
<div data-replace-on="21"></div>
<div data-replace-on="1"></div>
<div data-replace-on="6"></div>
<div data-replace-on="4"></div>
References:
CSS:
Attribute-selectors ([attribute=attribute-value]).
JavaScript:
Arrow function syntax.
ChildNode.replaceWith().
document.querySelectorAll().
EventTarget.addEventListener().
NodeList.prototype.forEach().
I'm trying to get the ID of an element by class name like this
var prod_id2 = document.getElementsByClassName('select-selected')[1].id
document.getElementById('hidden-input-2').value = prod_id2;
This works fine, but my issue is that if there's only one element with that class it breaks the functionality, so I need some sort of if statement to only define this var if there is a second div with that class.
Any ideas?
Try this:
const elements = document.querySelectorAll('.test');
if (elements[1]) {
elements[1].innerText = 'Hithere';
}
<div class="test">hi</div>
<div class="test">hi</div>
<div class="test">hi</div>
document.querySelectorAll('.test'); selects all elements with the class test and returns a nodelist.
Then we can access the second element via of the nodelist with elements[1].
Here is how to check for the second element.
You can also set another fallback , different to null:
document.addEventListener("DOMContentLoaded", function(event) {
var selectedElements = document.querySelectorAll('.selected-selected'),
prod_id2 = selectedElements[1] || null;
alert(prod_id2)
});
<div id="test" class="selected-selected"></div>
You can also check that value then:
if (prod_id2) { // do some other stuff with the set value }
It breaks the functionality I think because you are grabbing the 2nd element specifically. You can do:
const prod_id2 = document.querySelectorAll('.select-selected');
and loop over the elements and grab the ID
prod_id2.forEach((prod, index) => {
if(index === 2) {
document.getElementById('hidden-input-2').value = prod.id;
}
})
I have an element that already has a class:
<div class="someclass">
<img ... id="image1" name="image1" />
</div>
Now, I want to create a JavaScript function that will add a class to the div (not replace, but add).
How can I do that?
If you're only targeting modern browsers:
Use element.classList.add to add a class:
element.classList.add("my-class");
And element.classList.remove to remove a class:
element.classList.remove("my-class");
If you need to support Internet Explorer 9 or lower:
Add a space plus the name of your new class to the className property of the element. First, put an id on the element so you can easily get a reference.
<div id="div1" class="someclass">
<img ... id="image1" name="image1" />
</div>
Then
var d = document.getElementById("div1");
d.className += " otherclass";
Note the space before otherclass. It's important to include the space otherwise it compromises existing classes that come before it in the class list.
See also element.className on MDN.
The easiest way to do this without any framework is to use element.classList.add method.
var element = document.getElementById("div1");
element.classList.add("otherclass");
Edit:
And if you want to remove class from an element -
element.classList.remove("otherclass");
I prefer not having to add any empty space and duplicate entry handling myself (which is required when using the document.className approach). There are some browser limitations, but you can work around them using polyfills.
find your target element "d" however you wish and then:
d.className += ' additionalClass'; //note the space
you can wrap that in cleverer ways to check pre-existence, and check for space requirements etc..
Add Class
Cross Compatible
In the following example we add a classname to the <body> element. This is IE-8 compatible.
var a = document.body;
a.classList ? a.classList.add('classname') : a.className += ' classname';
This is shorthand for the following..
var a = document.body;
if (a.classList) {
a.classList.add('wait');
} else {
a.className += ' wait';
}
Performance
If your more concerned with performance over cross-compatibility you can shorten it to the following which is 4% faster.
var z = document.body;
document.body.classList.add('wait');
Convenience
Alternatively you could use jQuery but the resulting performance is significantly slower. 94% slower according to jsPerf
$('body').addClass('wait');
Removing the class
Performance
Using jQuery selectively is the best method for removing a class if your concerned with performance
var a = document.body, c = ' classname';
$(a).removeClass(c);
Without jQuery it's 32% slower
var a = document.body, c = ' classname';
a.className = a.className.replace( c, '' );
a.className = a.className + c;
References
jsPerf Test Case: Adding a Class
jsPerf Test Case: Removing a Class
Using Prototype
Element("document.body").ClassNames.add("classname")
Element("document.body").ClassNames.remove("classname")
Element("document.body").ClassNames.set("classname")
Using YUI
YAHOO.util.Dom.hasClass(document.body,"classname")
YAHOO.util.Dom.addClass(document.body,"classname")
YAHOO.util.Dom.removeClass(document.body,"classname")
Another approach to add the class to element using pure JavaScript
For adding class:
document.getElementById("div1").classList.add("classToBeAdded");
For removing class:
document.getElementById("div1").classList.remove("classToBeRemoved");
2 different ways to add class using JavaScript
JavaScript provides 2 different ways by which you can add classes to HTML elements:
Using element.classList.add() Method
Using className property
Using both methods you can add single or multiple classes at once.
1. Using element.classList.add() Method
var element = document.querySelector('.box');
// using add method
// adding single class
element.classList.add('color');
// adding multiple class
element.classList.add('border', 'shadow');
.box {
width: 200px;
height: 100px;
}
.color {
background: skyblue;
}
.border {
border: 2px solid black;
}
.shadow {
box-shadow: 5px 5px 5px gray;
}
<div class="box">My Box</div>
2. Using element.className Property
Note: Always use += operator and add a space before class name to add class with classList method.
var element = document.querySelector('.box');
// using className Property
// adding single class
element.className += ' color';
// adding multiple class
element.className += ' border shadow';
.box {
width: 200px;
height: 100px;
}
.color {
background: skyblue;
}
.border {
border: 2px solid black;
}
.shadow {
box-shadow: 5px 5px 5px gray;
}
<div class="box">My Box</div>
document.getElementById('some_id').className+=' someclassname'
OR:
document.getElementById('some_id').classList.add('someclassname')
First approach helped in adding the class when second approach didn't work.
Don't forget to keep a space in front of the ' someclassname' in the first approach.
For removal you can use:
document.getElementById('some_id').classList.remove('someclassname')
When the work I'm doing doesn't warrant using a library, I use these two functions:
function addClass( classname, element ) {
var cn = element.className;
//test for existance
if( cn.indexOf( classname ) != -1 ) {
return;
}
//add a space if the element already has class
if( cn != '' ) {
classname = ' '+classname;
}
element.className = cn+classname;
}
function removeClass( classname, element ) {
var cn = element.className;
var rxp = new RegExp( "\\s?\\b"+classname+"\\b", "g" );
cn = cn.replace( rxp, '' );
element.className = cn;
}
Assuming you're doing more than just adding this one class (eg, you've got asynchronous requests and so on going on as well), I'd recommend a library like Prototype or jQuery.
This will make just about everything you'll need to do (including this) very simple.
So let's say you've got jQuery on your page now, you could use code like this to add a class name to an element (on load, in this case):
$(document).ready( function() {
$('#div1').addClass( 'some_other_class' );
} );
Check out the jQuery API browser for other stuff.
You can use the classList.add OR classList.remove method to add/remove a class from a element.
var nameElem = document.getElementById("name")
nameElem.classList.add("anyclss")
The above code will add(and NOT replace) a class "anyclass" to nameElem.
Similarly you can use classList.remove() method to remove a class.
nameElem.classList.remove("anyclss")
To add an additional class to an element:
To add a class to an element, without removing/affecting existing values, append a space and the new classname, like so:
document.getElementById("MyElement").className += " MyClass";
To change all classes for an element:
To replace all existing classes with one or more new classes, set the className attribute:
document.getElementById("MyElement").className = "MyClass";
(You can use a space-delimited list to apply multiple classes.)
If you don't want to use jQuery and want to support older browsers:
function addClass(elem, clazz) {
if (!elemHasClass(elem, clazz)) {
elem.className += " " + clazz;
}
}
function elemHasClass(elem, clazz) {
return new RegExp("( |^)" + clazz + "( |$)").test(elem.className);
}
I too think that the fastest way is to use Element.prototype.classList as in es5: document.querySelector(".my.super-class").classList.add('new-class')
but in ie8 there is no such thing as Element.prototype.classList, anyway you can polyfill it with this snippet (fell free to edit and improve it):
if(Element.prototype.classList === void 0){
function DOMTokenList(classes, self){
typeof classes == "string" && (classes = classes.split(' '))
while(this.length){
Array.prototype.pop.apply(this);
}
Array.prototype.push.apply(this, classes);
this.__self__ = this.__self__ || self
}
DOMTokenList.prototype.item = function (index){
return this[index];
}
DOMTokenList.prototype.contains = function (myClass){
for(var i = this.length - 1; i >= 0 ; i--){
if(this[i] === myClass){
return true;
}
}
return false
}
DOMTokenList.prototype.add = function (newClass){
if(this.contains(newClass)){
return;
}
this.__self__.className += (this.__self__.className?" ":"")+newClass;
DOMTokenList.call(this, this.__self__.className)
}
DOMTokenList.prototype.remove = function (oldClass){
if(!this.contains(newClass)){
return;
}
this[this.indexOf(oldClass)] = undefined
this.__self__.className = this.join(' ').replace(/ +/, ' ')
DOMTokenList.call(this, this.__self__.className)
}
DOMTokenList.prototype.toggle = function (aClass){
this[this.contains(aClass)? 'remove' : 'add'](aClass)
return this.contains(aClass);
}
DOMTokenList.prototype.replace = function (oldClass, newClass){
this.contains(oldClass) && this.remove(oldClass) && this.add(newClass)
}
Object.defineProperty(Element.prototype, 'classList', {
get: function() {
return new DOMTokenList( this.className, this );
},
enumerable: false
})
}
To add, remove or check element classes in a simple way:
var uclass = {
exists: function(elem,className){var p = new RegExp('(^| )'+className+'( |$)');return (elem.className && elem.className.match(p));},
add: function(elem,className){if(uclass.exists(elem,className)){return true;}elem.className += ' '+className;},
remove: function(elem,className){var c = elem.className;var p = new RegExp('(^| )'+className+'( |$)');c = c.replace(p,' ').replace(/ /g,' ');elem.className = c;}
};
var elem = document.getElementById('someElem');
//Add a class, only if not exists yet.
uclass.add(elem,'someClass');
//Remove class
uclass.remove(elem,'someClass');
I know IE9 is shutdown officially and we can achieve it with element.classList as many told above but I just tried to learn how it works without classList with help of many answers above I could learn it.
Below code extends many answers above and improves them by avoiding adding duplicate classes.
function addClass(element,className){
var classArray = className.split(' ');
classArray.forEach(function (className) {
if(!hasClass(element,className)){
element.className += " "+className;
}
});
}
//this will add 5 only once
addClass(document.querySelector('#getbyid'),'3 4 5 5 5');
You can use modern approach similar to jQuery
If you need to change only one element, first one that JS will find in DOM, you can use this:
document.querySelector('.someclass').className += " red";
.red {
color: red;
}
<div class="someclass">
<p>This method will add class "red" only to first element in DOM</p>
</div>
<div class="someclass">
<p>lorem ipsum</p>
</div>
<div class="someclass">
<p>lorem ipsum</p>
</div>
<div class="someclass">
<p>lorem ipsum</p>
</div>
Keep in mind to leave one space before class name.
If you have multiple classes where you want to add new class, you can use it like this
document.querySelectorAll('.someclass').forEach(function(element) {
element.className += " red";
});
.red {
color: red;
}
<div class="someclass">
<p>This method will add class "red" to all elements in DOM that have "someclass" class.</p>
</div>
<div class="someclass">
<p>lorem ipsum</p>
</div>
<div class="someclass">
<p>lorem ipsum</p>
</div>
<div class="someclass">
<p>lorem ipsum</p>
</div>
This might be helpful for WordPress developers etc.
document.querySelector('[data-section="section-hb-button-1"] .ast-custom-button').classList.add('TryMyClass');
Just to elaborate on what others have said, multiple CSS classes are combined in a single string, delimited by spaces. Thus, if you wanted to hard-code it, it would simply look like this:
<div class="someClass otherClass yetAnotherClass">
<img ... id="image1" name="image1" />
</div>
From there you can easily derive the javascript necessary to add a new class... just append a space followed by the new class to the element's className property. Knowing this, you can also write a function to remove a class later should the need arise.
I think it's better to use pure JavaScript, which we can run on the DOM of the Browser.
Here is the functional way to use it. I have used ES6 but feel free to use ES5 and function expression or function definition, whichever suits your JavaScript StyleGuide.
'use strict'
const oldAdd = (element, className) => {
let classes = element.className.split(' ')
if (classes.indexOf(className) < 0) {
classes.push(className)
}
element.className = classes.join(' ')
}
const oldRemove = (element, className) => {
let classes = element.className.split(' ')
const idx = classes.indexOf(className)
if (idx > -1) {
classes.splice(idx, 1)
}
element.className = classes.join(' ')
}
const addClass = (element, className) => {
if (element.classList) {
element.classList.add(className)
} else {
oldAdd(element, className)
}
}
const removeClass = (element, className) => {
if (element.classList) {
element.classList.remove(className)
} else {
oldRemove(element, className)
}
}
Sample with pure JS. In first example we get our element's id and add e.g. 2 classes.
document.addEventListener('DOMContentLoaded', function() {
document.getElementsById('tabGroup').className = "anyClass1 anyClass2";
})
In second example we get element's class name and add 1 more.
document.addEventListener('DOMContentLoaded', function() {
document.getElementsByClassName('tabGroup')[0].className = "tabGroup ready";
})
For those using Lodash and wanting to update className string:
// get element reference
var elem = document.getElementById('myElement');
// add some classes. Eg. 'nav' and 'nav header'
elem.className = _.chain(elem.className).split(/[\s]+/).union(['nav','navHeader']).join(' ').value()
// remove the added classes
elem.className = _.chain(elem.className).split(/[\s]+/).difference(['nav','navHeader']).join(' ').value()
Shortest
image1.parentNode.className+=' box';
image1.parentNode.className+=' box';
.box { width: 100px; height:100px; background: red; }
<div class="someclass">
<img ... id="image1" name="image1" />
</div>
You can use the API querySelector to select your element and then create a function with the element and the new classname as parameters. Using classlist for modern browsers, else for IE8. Then you can call the function after an event.
//select the dom element
var addClassVar = document.querySelector('.someclass');
//define the addclass function
var addClass = function(el,className){
if (el.classList){
el.classList.add(className);
}
else {
el.className += ' ' + className;
}
};
//call the function
addClass(addClassVar, 'newClass');
In my case, I had more than one class called main-wrapper in the DOM, but I only wanted to affect the parent main-wrapper. Using :first Selector (https://api.jquery.com/first-selector/), I could select the first matched DOM element. This was the solution for me:
$(document).ready( function() {
$('.main-wrapper:first').addClass('homepage-redesign');
$('#deals-index > div:eq(0) > div:eq(1)').addClass('doubleheaderredesign');
} );
I also did the same thing for the second children of a specific div in my DOM as you can see in the code where I used $('#deals-index > div:eq(0) > div:eq(1)').addClass('doubleheaderredesign');.
NOTE: I used jQuery as you can see.
The majority of people use a .classList.add on a getElementById, but I i wanted to use it on a getElementByClassName. To do that, i was using a forEach like this :
document.getElementsByClassName("class-name").forEach(element => element.classList.add("new-class"));
But it didn't work because i discovered that getElementsByClassName returns a HTML collection and not an array. To handle that I converted it to an array with this code :
[...document.getElementsByClassName("class-name")].forEach(element => element.classList.add("new-class"));
first, give the div an id. Then, call function appendClass:
<script language="javascript">
function appendClass(elementId, classToAppend){
var oldClass = document.getElementById(elementId).getAttribute("class");
if (oldClass.indexOf(classToAdd) == -1)
{
document.getElementById(elementId).setAttribute("class", classToAppend);
}
}
</script>
This js code works for me
provides classname replacement
var DDCdiv = hEle.getElementBy.....
var cssCNs = DDCdiv.getAttribute('class');
var Ta = cssCNs.split(' '); //split into an array
for (var i=0; i< Ta.length;i++)
{
if (Ta[i] == 'visible'){
Ta[i] = 'hidden';
break;// quit for loop
}
else if (Ta[i] == 'hidden'){
Ta[i] = 'visible';
break;// quit for loop
}
}
DDCdiv.setAttribute('class',Ta.join(' ') ); // Join array with space and set class name
To add just use
var cssCNs = DDCdiv.getAttribute('class');
var Ta = cssCNs.split(' '); //split into an array
Ta.push('New class name');
// Ta.push('Another class name');//etc...
DDCdiv.setAttribute('class',Ta.join(' ') ); // Join array with space and set class name
To remove use
var cssCNs = DDCdiv.getAttribute('class');
var Ta = cssCNs.split(' '); //split into an array
for (var i=0; i< Ta.length;i++)
{
if (Ta[i] == 'visible'){
Ta.splice( i, 1 );
break;// quit for loop
}
}
DDCdiv.setAttribute('class',Ta.join(' ') ); // Join array with space and set class name
Hope this is helpful to sombody
In YUI, if you include yuidom, you can use
YAHOO.util.Dom.addClass('div1','className');
HTH
I have a HTML as:
<div id="xyz">
<svg>......</svg>
<img>....</img>
<div id = "a"> hello </div>
<div id = "b"> hello
<div id="b1">I m a grand child</div>
</div>
<div id = "c"> hello </div>
</div>
I want to get all the children with tags as "div" of the parent element with id = xyz in a javascript variable.
Such that my output should be:
"<div id = "a"> hello </div>
<div id = "b"> hello
<div id="b1">I m a grand child</div>
</div>
<div id = "c"> hello </div>"
You can simply get the #xyz div first, then find all div children:
var childDivs = document.getElementById('xyz').getElementsByTagName('div')
// ^ Get #xyz element; ^ find it's `div` children.
The advantage of this method over Document.querySelectorAll is that these selectors work in pretty much every browser, as opposed to IE 8/9+ for the queryselector.
You can use querySelectorAll:
var childDivs = document.querySelectorAll('#xyz div')
A method to transform the divs to a string (to store or to alert) could be:
var divsHtml = function () {
var divhtml = [],
i = -1,
divs = document.querySelectorAll('#xyz div');
while (i++ < divs.length) {
divs[i] && divhtml.push(divs[i].outerHTML);
}
return divhtml.join('');
}();
If you need compatibility for older browsers (i.c. IE<8) use #Cerbrus' method to retrieve the divs, or use a shim.
To avoid double listing of (nested) divs, you may want to use
var divsHtml = function () {
var divhtml = [],
i = -1,
divs = document.querySelector('#xyz').childNodes;
while (i++ < divs.length) {
divs[i] &&
/div/i.test(divs[i].tagName) &&
divhtml.push(divs[i].outerHTML);
/* ^ this can also be written as:
if(divs[i] && /div/i.test(divs[i].tagName) {
divhtml.push(divs[i].outerHTML)
}
*/
}
return divhtml.join('');
}();
[edit 2021] Seven years old, this answer. See this snippet for another approach.
If you want only the immediate children of xyz, you can call
var childrendivs = document.querySelectorAll('#xyz > div');
or calculate them yourself, if you use an older browser without document.querySelectorAll-Support
var childrendivs = [],
children = document.getElementById('xyz').children;
for(var i = 0; i < children.length; i++){
if (children[i].tagName == "DIV") {
childrendivs.push(children[i]);
}
}
Unless I misunderstood, this is exactly what getElementsByTagName does.
To get only the direct children of a specific element tag:
// All `div` children of document (body) (including nested)
document.querySelectorAll('div')
.forEach(elm => elm.classList.add('querySelectorAll'))
// only direct children of document (body) which matches a `div` selector
const directDivs = [...document.body.children]
.filter(elm => elm.matches('div'))
// style only the `div`
.forEach(elm => elm.classList.add('direct-div-children'))
*:not(body):not(html) {
margin: 5px;
padding: 5px;
border: 3px solid black;
}
.querySelectorAll {
background: lightyellow;
}
.direct-div-children {
border: 3px solid red;
}
<div>
A
<div>A1</div>
<div>A2</div>
</div>
<p>not a div</p>
<div>B</div>
<div>C</div>
For some performance reasons, I am trying to find a way to select only sibling nodes of the selected node.
For example,
<div id="outer">
<div id="inner1"></div>
<div id="inner2"></div>
<div id="inner3"></div>
<div id="inner4"></div>
</div>
If I selected inner1 node, is there a way for me to access its siblings, inner2-4 nodes?
Well... sure... just access the parent and then the children.
node.parentNode.childNodes[]
or... using jQuery:
$('#innerId').siblings()
Edit: Cletus as always is inspiring. I dug further. This is how jQuery gets siblings essentially:
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;
This will return the sibling immediately after it, or null no more siblings are available. Likewise, you can use previousSibling.
[Edit] On second thought, this will not give the next div tag, but the whitespace after the node. Better seems to be
var sibling = node.nextElementSibling;
There also exists a previousElementSibling.
Quick:
var siblings = n => [...n.parentElement.children].filter(c=>c!=n)
https://codepen.io/anon/pen/LLoyrP?editors=1011
Get the parent's children as an array, filter out this element.
Edit:
And to filter out text nodes (Thanks pmrotule):
var siblings = n => [...n.parentElement.children].filter(c=>c.nodeType == 1 && c!=n)
From 2017:
straightforward answer: element.nextElementSibling for get the right element sibling. also you have element.previousElementSibling for previous one
from here is pretty simple to got all next sibiling
var n = element, ret = [];
while (n = n.nextElementSibling){
ret.push(n)
}
return ret;
have you checked the "Sibling" method in jQuery?
sibling: function( n, elem ) {
var r = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
r.push( n );
}
}
return r;
}
the n.nodeType == 1 check if the element is a html node and n!== exclude the current element.
I think you can use the same function, all that code seems to be vanilla javascript.
There are a few ways to do it.
Either one of the following should do the trick.
// 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;
}
FYI: The jQuery code-base is a great resource for observing Grade A Javascript.
Here is an excellent tool that reveals the jQuery code-base in a very streamlined way.
http://james.padolsey.com/jquery/
The following function will return an array containing all the siblings of the given element.
const getSiblings = node => [...node.parentNode.children].filter(c => c !== node)
// get "c" element siblings (excluding itself)
const siblingsToC = getSiblings(document.querySelector('.c'))
console.log( siblingsToC )
<ul>
<li class='a'>a</li>
<li class='b'>b</li>
<li class='c'>c</li>
<li class='d'>d</li>
<li class='e'>e</li>
</ul>
Just pass the selected element into the getSiblings() function as it's only parameter.
Here's how you could get previous, next and all siblings (both sides):
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);
}
Use document.querySelectorAll() and Loops and iteration
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>
jQuery
$el.siblings();
Native - latest, Edge13+
[...el.parentNode.children].filter((child) =>
child !== el
);
Native (alternative) - latest, Edge13+
Array.from(el.parentNode.children).filter((child) =>
child !== el
);
Native - IE10+
Array.prototype.filter.call(el.parentNode.children, (child) =>
child !== el
);
var childNodeArray = document.getElementById('somethingOtherThanid').childNodes;
1) Add selected class to target element 2) Find all children of parent element excluding target element 3) Remove class from target element
<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
}
You can access the following sibling nodes, with the currentNode.nextSibiling property.
This is how you can do in the event delegation way, which is a dynamic way to add event listeners
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 */
}
})
My use case was different. I had to select a few spans which didn't have any id/classes (nor their parents), just an entry point (#target). Once you have that, run a querySelectorAll on its parent with the appropriate selector, using :scope as you can't simply use > div or > span or > .foo.
Note that this approach ALSO selects the target element, if it matches the selector. In the below example, I'd have to use :scope > span:not(#target) to avoid selecting the entry point.
const spanSiblings = document.getElementById("target").parentNode.querySelectorAll(":scope > span");
console.log([...spanSiblings].map(e => e.innerText));
<div>
<span>One</span>
<span id="target">Target</span>
<div>A</div>
<span>Two</span>
<div>B</div>
<div>Hey</div>
</div>
BEST SOLUTION:
This is the best solution according my opinion:
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`)
At the first line the event.target will be the inner1 node, if we click on that. The parentNode will be the "outer" node, and on the partent node we start a search ( .querySelector(#inner2) ) to select the inner2 node.
OTHER SOLUTIONS:
I list other possible options, but they are not that flexible, since at them the sequence of the nodes are matter, which makes the code fragile, if we later add another node to the parent the whole code will break, what we want to avoid:
2)
This selects the first child (this index starts from 1, and NOT from 0)
node.parentNode.childNodes[1]
3) Assume that you have already selected inner1Node to a variable, the next sibling you can get:
let inner2Node = inner1Node.nextElementSibling;
4) The previous sibling you can get:
let inner1NodeAGAIN = inner2Node.previousElementSibling;
x1 = document.getElementById('outer')[0]
.getElementsByTagName('ul')[1]
.getElementsByTagName('li')[2];
x1.setAttribute("id", "buyOnlineLocationFix");