Adding a class to an element with a specific class - javascript

I'm looking for a way to add a class to a certain element with another class.
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li class="hide">Item 4</li>
<li class="hide">Item 5</li>
<ul>
JS/Jquery
if($('li').hasClass('hide')) {
$('li').removeClass('hide').addClass('slide-down');
}
The problem is that the class slide-down gets added to all li elements.
Is there a way to only target the li elements that have the hide class removed?

Mh maybe it's due to the typo in your HTML: class"hide" (you are missing the equal sign).
Also you got a logical error in your code:
if($('li').hasClass('hide')) the condition will yield true if any <li> element in your document has the hide class.
$('li').removeClass('hide').addClass('slide-down'); the first segment $('li') will actually select ALL <li> elements in your document and remove the class hide from them and add the slide-down to ALL <li> elements in your document.
Here's how I'd do it:
$('li.hide').removeClass('hide').addClass('slide-down');
Note that jQuery is about chaining, i.e selecting subsets and applying functions to these subsets.
What this line does is:
$('li.hide') selects all <li> elements in your document which have the hide class - this becomse your "working subset" now.
.removeClass('hide') removes the hide class from this subset we got in the first step and returns the same subset again.
.addClass('slide-down') adds the slide-down class to all <li> in the selected subset returned from step 2, which is the same as from step 1.
JS fiddle: https://jsfiddle.net/q0nzaa7t/

In vanilla JS:
var liHide = document.querySelectorAll('li.hide');
var i;
var length = liHide.length;
for (i=0;i<length;i++) {
liHide[i].className = 'slide-down';
}
Note that, for some reason, querySelectorAll doesn't get updated automatically like document.getElementsByClassName. The same code wouldn't work if we would have used that method for querying the DOM:
var liHide = document.getElementsByClassName('hide');
var i;
var length = liHide.length;
for (i=0;i<length;i++) {
liHide[i].className = 'slide-down'; //<-- this won't update the 2nd element
}
This would have only changed the first element, since liHide[1] becomes liHide[0], because <li class="hide">Item 4</li> is no longer part of HTML Collection.

Plain javascript for the ones with querySelectorAll and classList support:
var items = document.querySelectorAll('li.hide');
for (var i = 0; i < items.length; i++) {
items[i].classList.remove('hide');
items[i].classList.add('slide-down');
}
Without querySelectorAll:
var items = document.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
if (items[i].classList.contains('hide')) {
items[i].classList.remove('hide');
items[i].classList.add('slide-down');
}
}
Without querySelectorAll and classList:
var items = document.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
if (new RegExp(/(?:^|\s)hide(?!\S)/g).test(items[i].className)) {
items[i].className = items[i].className.replace(/(?:^|\s)hide(?!\S)/g , '');
items[i].className += ' ' + 'slide-down';
}
}

Related

append / appendChild not working on all items

I'm trying to move all the list items from an list to another using only javascript but for some reason only half of them are actually moved.
Heres a working example of what I'm doing:
var results_ul = document.getElementById('results');
var stores_li = document.getElementsByClassName('store-list-item');
for (var x = 0; x < stores_li.length; x++) {
document.getElementById('hide').appendChild(stores_li[x]);
stores_li[x].className += ' teste';
}
<p>results</p>
<ul id="results">
<li class="store-list-item">Teste 1</li>
<li class="store-list-item">Teste 2</li>
<li class="store-list-item">Teste 3</li>
<li class="store-list-item">Teste 4</li>
</ul>
<p>Hide:</p>
<ul id="hide"></ul>
What seems to be the problem?
getElementsByClassName returns a live list.
When you append the element to a different element, you change its position in the list.
So it starts off as:
1 2 3 4
Then you move the first one:
2 3 4 1
Then you access the second one … but the second one is now 3 because everything has shuffled down the list.
You could copy each element into an array (which will not be a live list) and then iterate over that array to move them (so they won't change positions as you go).
Alternatively, you could use querySelectorAll which returns a non-live list.
You should better use querySelectorAll than getElementsByClassName
var results_ul = document.getElementById('results');
var stores_li = document.querySelectorAll('.store-list-item');
stores_li.forEach((item)=>{
document.getElementById('hide').appendChild(item);
item.className += ' teste';
});
<p>results</p>
<ul id="results">
<li class="store-list-item">Teste 1</li>
<li class="store-list-item">Teste 2</li>
<li class="store-list-item">Teste 3</li>
<li class="store-list-item">Teste 4</li>
</ul>
<p>Hide:</p>
<ul id="hide"></ul>
Try use querySelectorAll . It'll returns a non-live list. That's what you need.
var stores_li = document.querySelectorAll('.store-list-item');
To increase more information:
Live : when the changes in the DOM are reflected in the collection. The content suffers the change when a node is modified.
Non-Live : when any change in the DOM does not affect the content of the collection.
document.getElementsByClassName() is an HTMLCollection, and is live.
document.querySelectorAll() is a NodeList and is not live.
In your code you are removing each element from the first list and inserting into the new list. After you remove 2 elements it will have only 2 elements in the first list but now you are searching the 3 rd index in the loop which is not there. So to make it work i have prepended each element from the last.
var results_ul = document.getElementById('results');
var stores_li = document.getElementsByClassName('store-list-item');
var hide_ul = document.getElementById('hide');
for (var x = 0, y = stores_li.length; x < y; x++) {
hide_ul.insertBefore(stores_li[y-x-1],hide_ul.firstChild);
stores_li[x].className += ' teste';
}
<p>results</p>
<ul id="results">
<li class="store-list-item">Teste 1</li>
<li class="store-list-item">Teste 2</li>
<li class="store-list-item">Teste 3</li>
<li class="store-list-item">Teste 4</li>
</ul>
<p>Hide:</p>
<ul id="hide"></ul>
Or you may want to clone the element with Jquery and you can push into the clonned ones then delete the orginals from top. I could not find any equivalent of clone() for js but if you want to check link is here
var results_ul = document.getElementById('results');
var stores_li = document.getElementsByClassName('store-list-item');
while(stores_li.length>0) {
document.getElementById('hide').appendChild(stores_li[0]);
stores_li[x].className += ' teste';
}

using loop to check and remove a className from class name string and add another className to the element which triggers the event

it is fairly simple.
I want to make a current class and put it on a link that is currently active
like this : <li class="li current"><a>Link1</a></li>
but before that, the JS should get all of element with class="li", loop through it and remove the class=" current" if any.
after looping end, then add class=" current" to the element which triggers the event.
for example if user clicks on link3 then JS should remove the class=" current" on link1 and then add class=" current" on link3
it would look like this : <li class="li current"><a>link3</a></li>
it looks like I cannot use getElementsByClassName() for event listener. what should I do to make an event listener then?
the HTML
document.addEventListener("DOMContentLoaded",function(){
var et = document.getElementsByClassName("li");
for(i=0; i <et.length; i++){
et[i].addEventListener("click",function(){
funclink(event);
});
};
});
function funclink(event){
//first get all element with class "li" and remove class " current"
//then add class " current" to the element which triggers the event
var slink = document.getElementsByClassName("li");
for(i =0; i < slink.length; i++){
slink[i].className = slink[i].className.replace(" current","");
}
event.currentTarget.className += " current";
}
.current {
color : orange;
}
<ul id="navmenu">
<li class="li current">Home</li>
<li class="li">Call Service
<li class="li"><a class="a-left" href="#">Rental</a></li>
<li class="li"><a class="a-left" href="#">Sales</a></li>
</li>
</ul>
As commented before, you will have to loop over elements to add eventListener. document.getElementsByClassName will return a NodeList.
Also, if you are only calling your function with event param, you can just pass function ref instead.
Also, instead again fetching all lis, just fetch those lis that has class current. This will reduce number of iterations.
You should use classList instead of className. It has many helpful methods like add, remove and toggle.
Edit 1
As you need to retain the selected li even after refresh, you will have to use client storage or query param to tell your JS about the selected element.
For security reasons, SO does not provide access to localStorage, so you will have to copy code and debug on local files.
document.addEventListener("DOMContentLoaded", function() {
var lis = document.getElementsByClassName("li")
for (var i = 0; i < lis.length; i++) {
lis[i].addEventListener("click", funclink.bind(lis[i], i));
}
initializeUI();
});
function funclink(index, event) {
var lis = document.querySelectorAll('.li.current');
for(var i = 0; i< lis.length; i++){
lis[i].classList.remove('current');
}
this.classList.add('current');
setIndex(index)
}
function initializeUI(){
var lis = document.querySelectorAll('.li');
var index = parseInt(locatStorage.getItem('liIndex'));
lis[index || 0].classList.add("current");
}
function setIndex(index){
localStorage.setItem("liIndex", index);
}
function blockAnchors(){
var as = document.querySelectorAll('a');
for(var i = 0; i< as.length; i++){
as[i].onclick = function(){
return false
}
}
}
blockAnchors();
.current{
background: #ddd;
}
<ul id="navmenu">
<li class="li">Home</li>
<li class="li">Call Service
<li class="li"><a class="a-left" href="?module=rental">Rental</a></li>
<li class="li"><a class="a-left" href="?module=sales">Sales</a></li>
</li>
</ul>
Note: I have added a blockAnchors to stop navigation on a's click. This is just for demonstration.
Array of elements doesn't have to initialized two times.
Using li class is a useless. Modern browsers (IE8+) can use querySelector() (querySelectorAll()) (similiar functionality to the jQuery selector).
When document is ready we get array of links in #navmenu, we add listeners to them.
funclink function removes all classes from links and then sets class "current" to the element, that has triggered that function.
var slink; //prepare global scope var that will be filled with link elems
document.addEventListener("DOMContentLoaded", function() {//wait for document to be loaded
slink = document.querySelectorAll("#navmenu li");//fill global var with array of link elems
for (i = 0; i < slink.length; i++) {//add click listener to all link elems
slink[i].addEventListener("click", function() {
funclink(event);
});
}
});
function funclink(event) {
for (i = 0; i < slink.length; i++)slink[i].className = ""; //remove all current classes
event.currentTarget.className = "current"; //add current class to the element that triggered fnc
}
a {
color: blue;
}
.current,
.current a {
color: red;
}
<ul id="navmenu">
<li class="current">Home</li>
<li>Call Service
<li><a class="a-left" href="#?module=rental">Rental</a></li>
<li><a class="a-left" href="#?module=sales">Sales</a></li>
</li>
</ul>

Randomize or shuffle <ul>items except list items with a specific class

I have this HTML list <ul> which contains list items with two different classes.
<ul id="Items">
<li class="sw">Switchable 1</li>
<li class="sw">Switchable 2</li>
<li class="notsw">This should remain 3</li>
<li class="sw">Switchable 4</li>
<li class="notsw">This should remain 5</li>
<li class="sw">Switchable 6</li>
</ul>
<input type="button" class="btn" value="Shuffle" />
I am trying to randomize or shuffle the order of the list items when an event is triggered (let's say a button was clicked) but only shuffle the list items with the .sw class. So far, I have achieved shuffling the list items(all of them) using a jQuery custom function. Then, I tried storing the initial indexes of the .notsw list items(I think i'm getting the right values) and used jQuery .before() to move it back after the shuffle but still I can't make it go where it should be.
Note: The list items with the .notsw class could be anywhere in the list initially.
jQuery:
$('.btn').click(function(){
var notsws = document.getElementsByClassName("notsw");
var inds = new Array();
for(var i=0; i<notsws.length; i++){
inds[i] =$('.notsw').eq(i).index();
}
$('#Items').randomize();
for(var k=0; k<notsws.length; k++){
var curr = inds[k];
$('.notsw').eq(k).before($('#Items li').eq(curr));
}
});
$.fn.randomize = function(selector){
var $elems = selector ? $(this).find(selector) : $(this).children(),
$parents = $elems.parent();
$parents.each(function(){
$(this).children(selector).sort(function(){
return Math.round(Math.random()) - 0.5;
}).remove().appendTo(this);
});
return this;
};
I HAVE A JSFIDDLE EXAMPLE HERE
I used an alternate approach. I see that you are randomizing your list and then trying to remember whether the original elements were.
Instead of that, why don't you just shuffle elements based on whether they can be shuffled.
In the sense, take an array of the indexes for the switchable elements denoted by the selector .sw and then shuffle only those indexes.
Here's how the shuffle function would look like.
function shuffle(nodes, switchableSelector) {
var length = nodes.length;
//Create the array for the random pick.
var switchable = nodes.filter("." + switchableSelector);
var switchIndex = [];
$.each(switchable, function(index, item) {
switchIndex[index] = $(item).index();
});
//The array should be used for picking up random elements.
var switchLength = switchIndex.length;
var randomPick, randomSwap;
for (var index = length; index > 0; index--) {
//Get a random index that contains a switchable element.
randomPick = switchIndex[Math.floor(Math.random() * switchLength)];
//Get the next element that needs to be swapped.
randomSwap = nodes[index - 1];
//If the element is 'switchable' continue, else ignore
if($(randomSwap).hasClass(switchableSelector)) {
nodes[index - 1] = nodes[randomPick];
nodes[randomPick] = randomSwap;
}
}
return nodes;
}
On your button click, you can simply shuffle the nodes and then re-append them to the container.
$(".btn").click(function() {
var $nodes = $("#Items").find("li");
shuffle($nodes, "sw");
$("#Items").append($nodes);
});
Working fiddle present here.
What you are doing is removing all the "sw" elements and then pushing them at the back of the list. I would create a new randomized list with the "sw" items and then add the "notsw"item at their previous indexes. You should therefore save the "notsw" indexes.

Build list when select option change

I'm trying to display the full list that have the same id that matches with the select option. But I can't figure out how to get the id from the attribute by using the name to be able to filter it.
The html example:
<select id='groopy' onchange='see();'>
<option>default</option>
<option>lista1</option>
<option>list1</option>
</select>
<ul id='grupo'>
<li id='list1' name="lista">Playground (Rangbhoomi)</li>
<li id='default' name="lista">Empire Made Me</li>
<li id='default' name="lista">Transmission</li>
<li id='lista1' name="lista">Hostel Room 131</li>
<li id='default' name="lista">A Disobedient Girl</li>
<li id='default' name="lista">Travels in the Land of Kubilai Khan</li>
<li id='list1' name="lista">The Indian Mutiny</li>
<li id='lista1' name="lista">Beauty and Sadness</li>
<li id='default' name="lista">The Collaborator</li>
<li id='list1' name="lista">I, Lalla</li>
<li id='default' name="lista">No Full Stops in India</li>
<li id='lista1' name="lista">For Lust of Knowing</li>
<li id='default' name="lista">On the Road to Kandahar</li>
</ul>
And the script I'm trying:
<script>
function see(){
var listita = document.getElementById('groopy').options[document.getElementById('groopy').selectedIndex].value;
var items = document.getElementsByName("lista");
var items_id = document.getElementsByName("lista").getAttribute('id');
if(listita==items_id){
for(var i = 0; i < items.length; i++)
{
document.getElementById("description").innerHTML = items[i].outerHTML;
}
}
}
onload= see();
</script>
By the way, the select and the ul are generated dynamically so I don't actually now the id's that could be provided. I'm trying a little different approach here .
When I manage to make the select filter work, the for stop working. WHY? I'm going crazy with this. Please help!!
Firstly you are having multiple elements with same id's which is wrong.. Cause getElementbyId will only fetch the first element encountered by it.
Replace then with class instead
Next you are overwriting the HTML for every iteration, so you will always have the last item appended to it.
Instead store that in a variable and append it after the for loop.
you need to bind your element with a change event, otherwise your call only works once when the page loads for the first time.
Try this
// Cache the selector as you are using it multiple times
var dropdown = document.getElementById('groopy');
function see() {
// set the selected option
var listita = dropdown.options[dropdown.selectedIndex].value
items = document.getElementsByClassName(listita);
html = '';
// For each item with class name, iterate over it and store in a string
for (var i = 0; i < items.length; i++) {
if (items[i].className == listita) {
console.log((items[i].outerHTML));
html += items[i].outerHTML
}
}
// set the html after the for loop
document.getElementById("description").innerHTML = html;
}
onload = see();
// attach the change event handler
dropdown.addEventListener('change', see);
Check Fiddle
try changing the id's to class in the li tags and use this function...
function see(){
var selectedVal = document.getElementById('groopy').options[document.getElementById('groopy').selectedIndex]. value;
var items = document.getElementsByClassName(selectedVal);
var html = '';
for(var i = 0; i < elements.length; i++)
{
html += items[i].outerHTML;
}
document.getElementById("description").innerHTML = html;
}
onload= see();
Step1 - Change id to class
Step2 - Traverse DOM elements with jQuery class selector. In this way replace document.getElementId('id) with $('.className')

Hide parent div if list is empty

I have the following....
<div class="validationbox">
<h1>Errors</h1>
<ul class="validationerrors">
<li>Error 1</li>
<li>Error 2</li>
<li>Error 3</li>
</ul>
</div>
The error list items are generated dynamically so sometimes there are none, I would like to hide the 'validationbox' div if there are no list items.
I imagine it is javascript or jquery that I should be looking at, does anyone have any examples?
In jQuery you can do it as simple as:
$(".validationbox:not(:has(li))").hide();
In pure JavaScript you need to iterate ".validationbox" elements and search for <li> nodes inside:
var div = document.getElementsByClassName("validationbox");
for (var i = 0, len = div.length; i < len; i++) {
if (!div[i].getElementsByTagName("li").length) {
div[i].style.display = "none";
}
}
$('.validationbox').toggle( $('.validationerrors li').length );
toggle() accepts a boolean value as an argument. $('.validationerrors li').length will evaluate to false if there is no <li> elements, else true, which will show the error list.
you can use not .
$('div .validationbox').not(':has(li)').hide();
hope it's help to you

Categories