jQuery how to removeClass of only certain elements - javascript

I have a jQuery project where I'm looping through a list of elements that all have the same class. What I need is for the 1st two elements class to be removed. Therefore I'm using the .removeClass() method. However, I don't know how to use that method and only remove the 1st two. It's moving all of them. Can someone help me without changing the direction of my code.
function putSiblingsInTableForEachH2() {
// line above sets function that will ultimately store siblings of each h2.toggler into a HTML table
var togglerHeaders = $("h2.toggler");
// line above sets variable togglerHeaders to all h2 elements with a class of ".toggler"
for (i = 0; i < togglerHeaders.length; i++) {
// line above: for loop that loops through array "togglerHeaders"
var currentH2Element = togglerHeaders[i];
// line above sets variable currentH2Element to togglerHeaders at position i
if (currentH2Element == togglerHeaders[0] || togglerHeaders[1]) {
$("h2").removeClass("toggler");
}
var siblingsofH2 = $(currentH2Element).nextUntil("h2.toggler");
// line above access siblings that are in h2.toggler element
// line says: set variable "siblingsofH2" to the current h2.toggler element you're on actual sibling elements but only siblings that are in between current element and next element that is "h2.toggler"
$(siblingsofH2).wrapAll("<table></table>");
// line above says: after correct sibling elements are stored to variable siblingsofH2 wrap elements in HTML table
} // line ends for loop
} // line ends function
putSiblingsInTableForEachH2();
// line above actually runs function

Of course $("h2").removeClass("toggler"); will remove class from all, because it is referring to all.
I slightly edited your lines:
if (i < 2) {
$(currentH2Element).removeClass("toggler");
}

Plain JS .querySelectorAll with :nth-child formula
var firstTwo = document.querySelectorAll(".my-class:nth-child(-n+2)");

This is what I came up with.
var putThingsInTable = query => {
var matches = document.querySelectorAll(query);
if(!matches) return;
var table = document.createElement('table')
table.appendChild(document.createElement('tr'))
var i = 0;
var toAdd = [];
matches.forEach(match => {
if(i > 1){
toAdd.push(match);
}
i++
});
toAdd.forEach(element => {
var td = document.createElement('td');
td.appendChild(element);
table.children[0].appendChild(td)
});
return table;
}
document.body.appendChild(putThingsInTable('h2.toggler'))
table, th, td {
border: 1px solid black;
}
<h2 class="toggler">a</h2>
<h2 class="toggler">a</h2>
<h2 class="toggler">a</h2>
<h2 class="toggler">c</h2>
<h2 class="toggler">c</h2>
<h2 class="toggler">w</h2>
<h2 class="toggler">w</h2>
<h2 class="toggler">w</h2>
<h2 class="toggler">w</h2>

Answer
This is the line of jQuery you are looking for ...
$("h2.toggler:gt(1)")
Explanation
:gt(1) Will select all elements at an index greater than 1 within the matched set
See ... https://api.jquery.com/gt-selector/
Example
For full example see ...
https://codepen.io/stephanieschellin/pen/MPdWVw
or ...
$('h2.toggler:gt(1)').each(function(e) {
$(this).nextUntil('h2').wrapAll('<table></table>')
})
table {
color: blue;
border: solid 3px black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2 class="toggler">one</h2>
<span>one</span>
<span>one</span>
<h2 class="toggler">two</h2>
<span>two</span>
<span>two</span>
<h2 class="toggler">three</h2>
<span>three</span>
<span>three</span>
<span>three</span>
<h2 class="toggler">four</h2><span>four</span>
<span>four</span>
<span>four</span>
<h2 class="toggler">five</h2>
<span>five</span>
<span>five</span>
<span>five</span>
<span>five</span>
<h2 class="toggler">six</h2>
<span>six</span>
<span>six</span>

Related

How to clone an element?

I have made a script to clone elements in js but this only selects the first element. Here is the code :
function clone(sel) {
var rVxyz = document.querySelector(sel);
var rVabc = rVxyz.cloneNode(true);
document.body.appendChild(rVabc) ||
document.documentElement.appendChild(rVabc);
};
Yes I know I have used querySelector but I was not able to use it with querySelectorAll. I wrote something like this :
function clone(sel, num) {
var rVxyz = document.querySelectorAll(sel)[num];
var rVabc = rVxyz.cloneNode(true);
document.body.appendChild(rVabc) ||
document.documentElement.appendChild(rVabc);
};
This only works when num = 0, otherwise it doesn't.
Thanks in advance.
This will depend on what exactly you pass to your function as sel. If you are passing the id of an element, like this: #someID, there can only be 0 elements or 1 element in rVxyz as ids must be unique. So you can't clone that element, unless you include some code to change the ID, in which case it's not really a clone anyway.
Of course, if you are selecting by class, by passing something like .someClass to sel, then there can be any number of elements, from 0 to n.
I think it honestly makes more sense to clone all matching elements, rather than trying to pass an index to your function. So the clone() would use forEach instead.
function clone(sel)
{
var rVxyz = document.querySelectorAll(sel);
rVxyz.forEach(function(element) {
let rVabc = element.cloneNode(true);
document.body.appendChild(rVabc) ||
document.documentElement.appendChild(rVabc);
});
};
clone("#biz");
clone(".boi");
clone(".boi:nth-child(2)");
The HTML for test:
<div id="biz"><p>1 Unique thing</p></div>
<div class="boi"><p>2 hi</p></div>
<div class="boi"><p>hello there</p></div>
The output is:
1 Unique thing
2 hi
3 hello there
2 hi
3 hello there
2 hi
The first element is not cloned because IDs must be unique. Also, notice in the second call to clone(), I used the nth-child selector, so only the second matching element within each parent element is cloned. The nice thing about this is that you can even select every second, third, fourth, etc. element if you want.
As long as the elements are within the same parent container element, this will work.
Here's some more info about nth-child.
But your original approach would work as well if you actually want to clone the nth match. E.g. the 2nd match. You just need to check that you aren't going beyond the end of the NodeList rVxyz.
function clone(sel, num)
{
var rVxyz = document.querySelectorAll(sel);
var limit = rVxyz.length;
if (num < limit)
{
var rVabc = rVxyz[num].cloneNode(true);
document.body.appendChild(rVabc) ||
document.documentElement.appendChild(rVabc);
}
else
{
console.log(limit);//Do nothing or output error message here.
}
};
clone(".boi", 1);
This clones the second matching element only, as long as there are 2 or more elements found.
But your code works... You just could add a few lines, to get less errors.
function clone(sel, num) {
num = parseInt(num);
var id = !!sel.match('#');
var elem = (id) ? document.querySelector(sel) : document.querySelectorAll(sel)[num];
if (elem) {
var clone = elem.cloneNode(true);
clone.classList.add('demo'); // just for demo (remove);
if (id || elem.id) {
clone.id = elem.id + '-bubuClone';
console.log( `New clone id="${clone.id}"` );
}
document.body.appendChild(clone) || document.documentElement.appendChild(clone);
} else {
console.log(
'Didn\'t found element to clone: ("' + sel + '")' + ( !id ? '[' + num + ']' : '' )
);
}
}
clone('.test', 2);
clone('.test', 5);
clone('.test', 9);
clone('.test', 15);
clone('#bubu', 15);
clone('#check');
.test, .test-id {
display: inline-block;
border: 2px solid orange;
padding: 5px;
margin: 5px;
}
.demo {
border-color: red;
}
<div class="test-id" id="bubu">bubu</div>
<div class="test">0</div>
<div class="test">1</div>
<div class="test" id="test007">2</div>
<div class="test">3</div>
<div class="test">4</div>
<div class="test">5</div>
<div class="test">6</div>
<div class="test">7</div>
<div class="test">8</div>
<div class="test">9</div>
<br>
Or, add the third parameter to function, which will define the search type ( selector / selectorAll )

JavaScript - select the last item from the 'for' loop

Let's say I have a simple HTML markup with three elements and a javascript loop looking through all of them. What I need to do is to select the last one of those items.
This pile of code will run a loop, select all elements with some_div class, and paste some text inside them...
What if I only wanted the last item on the list to be selected and changed?
Is there a way for me to only select the last item from the loop and then do some operations like adding a specific class to this exact element?
var elements = document.getElementsByClassName('some_div');
for (var i=0; i<elements.length; i++) {
elements[i].innerHTML = 'hello';
}
.some_div {
height: 30px;
width: 100px;
border-bottom: solid 2px black;
}
<body>
<div class="some_div"></div>
<div class="some_div"></div>
<div class="some_div"></div>
</body>
Selecting the last match for a class (e.g., with a CSS selector) is awkward¹, but you can easily access the last match:
var elements = document.getElementsByClassName('some_div');
var last = elements[elements.length - 1];
if (last) {
last.innerHTML = 'hello';
}
Live Example:
var elements = document.getElementsByClassName('some_div');
var last = elements[elements.length - 1];
if (last) {
last.innerHTML = 'hello';
}
.some_div {
height: 30px;
width: 100px;
border-bottom: solid 2px black;
}
<body>
<div class="some_div"></div>
<div class="some_div"></div>
<div class="some_div"></div>
</body>
¹ (or impossible? :nth-last-of-type applies to element type, not class...)
since elements is an array-like object, you can easily index the last item
const elements = document.getElementsByClassName('some_div');
if(elements.length > 0){
const lastElement = elements[elements.length-1]
}

How can I attach a class to each element separately?

I have a list of 4 boxes, each have two sections .content-primary-wrapper and .content-secondary-wrapper, inside both these sections is a div called .content-inner. I am running a conditional based on the heights of the content-inner classes to decide which one get's a border-class addd to it.
As I have it now, the function runs, then applies the results to all of the classes, how can I get it to apply it on an element basis? I am trying to get it so it works like this:
Run function, determine heights of first element
determine heights of first element
Apply border class to this element only
Rinse and repeat for the other 3 objects
Here is my script I am using, any ideas how I could loop through these?
var primary_height = $('.content-primary-wrapper .content-inner').height();
var secondary_height = $('.content-secondary-wrapper .content-inner').height();
if ( primary_height >= secondary_height ){
$(this).find('.content-primary-wrapper .content-inner').addClass('add-border-right');
} else {
$(this).find('.content-secondary-wrapper .content-inner').addClass('add-border-left');
}
Is this what you're going for? Loop through all of your boxes, find the .content-inners in each one, and apply your class.
$(".box").each(function() {
var $pri = $(this).find(".content-primary-wrapper .content-inner"),
$sec = $(this).find(".content-secondary-wrapper .content-inner");
if ($pri.height() >= $sec.height()) {
$pri.addClass("add-border-right");
} else {
$sec.addClass("add-border-left");
}
});
.box {
float: left;
clear: both;
}
.add-border-left {
border-left: 1px solid red;
}
.add-border-right {
border-right: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box">
<div class="content-primary-wrapper">
<div class="content-inner">two<br>lines</div>
</div>
<div class="content-secondary-wrapper">
<div class="content-inner">one</div>
</div>
</div>
<div class="box">
<div class="content-primary-wrapper">
<div class="content-inner">one</div>
</div>
<div class="content-secondary-wrapper">
<div class="content-inner">two<br>lines</div>
</div>
</div>
There are multiple ways you could do this. I would probably run a selector to get the elements that contain the four boxes, then run localized queries for the primary and secondary wrappers on them, like so:
$('.box-container').each(function(index, container) {
var primary_height = $('.content-primary-wrapper .content-inner', container).height();
var secondary_height = $('.content-secondary-wrapper .content-inner', container).height();
// ...
}
Alternatively, if you're sure that they're always going to be paired like this, you could simply do something like:
var primary_elements = $('.content-primary-wrapper .content-inner');
var secondary_elements = $('.content-secondary-wrapper .content-inner');
var i;
for (i = 0; i < primary_elements.length; ++i) {
var primary_height = $(primary_elements[i]).height();
var secondary_height = $(secondary_elements[i]).height();
// ...
}
This version seems a little less robust to me, just in case you start removing one or the other element at some point and they're no longer always paired in the way that this assumes.

Wrapping a reoccuring collection of elements in a div

This is kind of a two part question - I have an HTML page where I need to take reoccurring groups of elements such as:
<h2>1</h2>
<p>foo</p>
<p>bar</p>
<h2>2</h2>
<p>fizz</p>
and wrap them individually with divs on each heading:
<div>
<h2>1</h2>
<p>foo</p>
<p>bar</p>
</div>
<div>
<h2>2</h2>
<p>fizz</p>
</div>
I know about getting elements by tag name, but I can't work out how to take the group of elements and then replacing them.
Thank you for any help.
You can use Array.from(), .forEach() loop to iterate h2 elements collection, check if .nextElementSibling .tagName is "P" in while loop, if true, push elements to an array.
Use .forEach() on array of arrays of p elements, create <div> element, append h2 at index of array to div element, loop each element of inner array, call .appendChild() with p element as parameter.
var headers = document.querySelectorAll("h2");
var arr = [];
Array.from(headers).forEach(function(h2, index) {
arr[index] = [];
var curr = h2.nextElementSibling;
while (curr.tagName === "P") {
arr[index].push(curr);
var next = curr.nextElementSibling;
curr = next;
}
});
arr.forEach(function(html, index) {
var div = document.createElement("div");
document.body.appendChild(div);
div.appendChild(headers[index]);
html.forEach(function(el) {
div.appendChild(el)
})
});
div {
padding: 4px;
margin: 4px;
border: 2px solid green;
}
<h2>1</h2>
<p>foo</p>
<p>bar</p>
<h2>2</h2>
<p>fizz</p>

Get children with tagname as <div> only in javascript

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>

Categories