"for" loop on NodeList to change className [duplicate] - javascript

This question already has an answer here:
JavaScript HtmlCollection loop never returns second element
(1 answer)
Closed 7 years ago.
This should take each element of class="one_level" and change its className to four_level. But this doesn't happen. Only this first list item is getting its appearance changed, the second is still the same.
HTML:
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>DOCUMENT OBJECT MODEL</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="css.css">
</head>
<body>
<ul>
<li class="one_level" style="background-color:black; color:white; margin:5px; ">ONE</li>
<li class="one_level" style="background-color:black; color:white; margin:5px ">TWO</li>
<li class="two_level" style="background-color:orange; color:red; margin:5px">THREE</li>
<li class="two_level" style="background-color:orange; color:red; margin:5px">FOUR</li>
</ul>
<div id="getValues"></div>
<script src="javascript.js"></script>
</body>
</html>
CSS:
.four_level{
border:solid 5px orange;
-webkit-border-radius:5px 6px;
}
.five_level{
border:solid 5px pink;
-webkit-border-radius:5px 6px;
}
JS:
//Get elements for class: one_level
var classOneSet = document.getElementsByClassName("one_level");
var lengthList = classOneSet.length;
//Change appearance
var i;
for (i = 0; i < lengthList; i++){
classOneSet.item(i).className = "four_level";
}

The getElementsByClassName method returns a live list of elements. When you change the class of the first element it will be removed from the list. When you try to change the second element it has moved to the first position in the list and you won't find it at the second position. If you had more than two elements to change, you would see that it changed every other element.
If you loop backwards through the list it works:
window.onload = function(){
//Get elements for class: one_level
var classOneSet = document.getElementsByClassName("one_level");
var lengthList = classOneSet.length;
//Change appearance
var i;
for (i = lengthList - 1; i >= 0; i--){
classOneSet.item(i).className = "four_level";
}
};
.four_level { background: red; }
<ul>
<li class="one_level">ONE</li>
<li class="one_level">TWO</li>
<li class="two_level">THREE</li>
<li class="two_level">FOUR</li>
</ul>

Instead of using item, treat it like a classic array:
var i;
for (i = 0; i < lengthList; i++){
classOneSet[i].className = "four_level";
}

Related

Apply a JavaScript function to all Array elements except the ith element

In one of my projects I made 3 galleries, I would like to put both of them on the same page in the same position, not at the same time, however. For this to be possible, I chose to create 3 buttons. When I click on the first button for example, the first gallery should appear (both galleries are initially on display:none), then when I click on the second button, the second one should appear and the one shown before should disappear, and so for each of the galleries. I made a simplified copy of the page to make the thinking easier.
In general, my problem is that I don't quite know how to apply a function to all the elements in an Array except for one element.
Here is the code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Galleries</title>
<link rel="stylesheet" type="text/css" href="gs.css">
<style type="text/css">
body{
background-color:royalblue;
}
header{
text-align: center;
}
article{
width:95%;
margin:auto 2.5% auto 2.5%;
height:850px;
background-color:tomato;
display:none;
}
</style>
</head>
<body>
<header>
<button>Third Gallery</button>
<button>Second Gallery</button>
<button>Third Gallery</button>
</header>
<section>
<article>
<h1>This is the first gallery</h1>
</article>
<article>
<h1>This is the second gallery</h1>
</article>
<article>
<h1>This is the third gallery</h1>
</article>
</section>
<script type="text/javascript">
var button=document.getElementsByTagName('button');
var gallery=document.getElementsByTagName('article');
for(var i=0; i<button.length; i++){
(function(index){
button[index].onclick=function(){
gallery[index].style.display="block";
}
}(i));
}
</script>
</body>
</html>
You could iterate over all the elements and compare the index of the button with the index of the current gallery item:
[].forEach.call(gallery, function (el, i) {
el.style.display = i === index ? 'block': 'none';
});
or:
for (var i = 0; i < gallery.length; i++) {
gallery[i].style.display = i === index ? 'block': 'none';
}
This will loop over all the elements and set the display of each element to none except for the on with an index that corresponds to the clicked button.
Example Here
var button = document.getElementsByTagName('button');
var gallery = document.getElementsByTagName('article');
for (var i = 0; i < button.length; i++) {
(function(index) {
button[index].onclick = function() {
[].forEach.call(gallery, function (el, i) {
el.style.display = i === index ? 'block': 'none';
});
}
}(i));
}
What you have done is almost right... Loop through the whole thing and when the particular element comes, do not do that, but I don't understand what's the use of closure here:
var button=document.getElementsByTagName('button');
var gallery=document.getElementsByTagName('article');
for(var i=0; i<button.length; i++){
if (i != 2) // Make sure `i` is not equal to 2.
(function(index){
button[index].onclick=function(){
gallery[index].style.display="block";
}
}(i));
}

How to dynamically set the colour of a 'div' element to match that of the colour picked on a colour widget using for loop?

I have this html code that includes a colour widget picker, the background of the entire page will change according to which pre-defined choice the user clicks on. I have got that working but I would like to highlight the box around the colour choice once it has been clicked to differentiate from the other unselected choices.
<html>
<head>
<meta charset="utf8" />
<title></title>
<script>
function changeColor(e) {
document.getElementById("page").className = e;
var i;
var x = document.getElementById("page");
for (i = 0; i < 5; i++)
if (document.getElementById("page").className = e ){
x.getElementsByTagName("li")[i].style.borderColor = "red";
}
}
</script>
</head>
<body>
<div id="page" class=""><!-- start page wrapper -->
<hr />
<div id="theme-picker">
<h2>Theme Picker</h2>
<p>Select a theme from the options below:</p>
<div id="palette">
<ul>
<li class="midnight" onClick="changeColor('midnight')">Midnight</li>
<li class="matrix" onclick="changeColor('matrix')">Matrix</li>
<li class="peardrop" onclick="changeColor('peardrop')">Peardrop</li>
<li class="skylight" onclick="changeColor('skylight')">Skylight</li>
<li class="sunset" onclick="changeColor('sunset')">Sunset</li>
</ul>
<div class="clearfix"></div>
<hr />
</div><!-- /page -->
</body>
</html>
Here's how I would do this (of course change the classes as needed):
$('.changeColor').click(function(){
var $this=$(this);
var color=$this.data('color');
$('#page').removeClass('midnight matrix peardrop skylight sunset').addClass(color);
$('.changeColor').removeClass('highlight'); // jquery actually implements a loop here it's just internal...
$this.addClass('highlight');
});
/*
// Methods with a explicit loop per your comment
$('.changeColor').click(function(){
var $this=$(this);
var $page=$('#page');
var color=$this.data('color');
$page.removeClass('midnight matrix peardrop skylight sunset').addClass(color);
$page.find('li').each(function(){ // jQuery loop....not really neccisary though...
$(this).removeClass('highlight');
});
$this.addClass('highlight');
});
$('.changeColor').click(function(){
var $this=$(this);
var $page=$('#page');
var color=$this.data('color');
$page.removeClass('midnight matrix peardrop skylight sunset').addClass(color);
var lis = $page.find('li');
for(var i =0; i< lis.length; i++){ // js loop....again not really neccisary though...
$(lis[i]).removeClass('highlight');
}
$this.addClass('highlight');
});
*/
body {
margin: 10px;
}
.midnight{
background-color:black;
color:white;
}
.matrix{
background-color:purple;
}
.peardrop{
background-color:grey;
}
.skylight{
background-color:blue;
}
.sunset{
background-color:pink;
}
.highlight{
background-color:yellow;
outline: 1px solid #red;
color:black;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="page" class=""><!-- start page wrapper -->
<hr />
<div id="theme-picker">
<h2>Theme Picker</h2>
<p>Select a theme from the options below:</p>
<div id="palette">
<ul>
<li class="changeColor" data-color="midnight">Midnight</li>
<li class="changeColor" data-color="matrix">Matrix</li>
<li class="changeColor" data-color="peardrop">Peardrop</li>
<li class="changeColor" data-color="skylight">Skylight</li>
<li class="changeColor" data-color="sunset">Sunset</li>
</ul>
<div class="clearfix"></div>
<hr />
</div>
You could pass the object everytime you call the function, and then change that object color. Something like this:
...
<script>
function changeColor(className,object) {
document.getElementById("page").className = className;
var siblings = object.parentElement.getElementsByTagName('li');
for(var sibling in siblings){
if(siblings[sibling].className){
siblings[sibling].className = siblings[sibling].className.split(' ')[0];
}
}
object.className = object.className+' selected';
}
</script>
...
<li class="midnight" onClick="changeColor('midnight',this)">Midnight</li>
<li class="matrix" onclick="changeColor('matrix',this)">Matrix</li>
<li class="peardrop" onclick="changeColor('peardrop',this)">Peardrop</li>
<li class="skylight" onclick="changeColor('skylight',this)">Skylight</li>
<li class="sunset" onclick="changeColor('sunset',this)">Sunset</li>
...
Edited, since I was missing the point of resetting the selection. You could just create a selected class with whatever you need and your done

How to invoke JavaScript on hover over an element class or id?

I have 1,000 links on one page. Each link has a title/note/tool-tip. All the titles say the same thing so rather than typing it up on each line is there a way that I can have javscript do this for me?
Example Before:
<div class="links">
Tooltips
Tooltips
Tooltips
Tooltips
</div>
Example of what I would like:
<div class="links">
<a href="#" >Tooltips</a>
<a href="#" >Tooltips</a>
<a href="#" >Tooltips</a>
<a href="#" >Tooltips</a>
</div>
and have java script display a note when mouseover of "div .links a"
Thanks in advance.
jsFiddle Demo
var set = document.querySelectorAll(".links a");
var tip = document.createElement("div");
tip.className = "hover";
var msg = document.createElement("div");
tip.appendChild(msg);
msg.innerHTML = "Generic Hover Message";
for( var i = 0, n = set.length; i < n; i++ ){
set[i].onmouseover = function(){
this.parentNode.insertBefore(tip,this);
};
set[i].onmouseout = function(){
tip.parentNode.removeChild(tip);
};
}
Set up events for the target elements
Use a combination of document.querySelectorAllMDN and iterate through the set assigning the onmouseover eventMDN and onmousout eventMDN to each element.
var set = document.querySelectorAll(".links a");
for( var i = 0, n = set.length; i < n; i++ ){
set[i].onmouseover = function(){
Create an element using document.createElementMDN for the tooltip
var tip = document.createElement("div");
tip.className = "hover";
var msg = document.createElement("div");
tip.appendChild(msg);
msg.innerHTML = "Generic Hover Message";
Create styling to position the tooltip
.hover{
position: absolute;
display:inline-block;
width:100%;
}
.hover > div{
top: 1.2em;
position:absolute;
z-index: 1;
}
Use insertBeforeMDN to place the element
this.parentNode.insertBefore(tip,this);
Like this?
<div class="links" title="This is a title">
<a href="#" >Tooltips</a>
...
Here is an example using Travis' proposed solution with a querySelector and onmouseover.
<html>
<head>
<title>Link over</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<style>
.links{
}
</style>
<script>
// I show a tooltip
function showTip() {
console.log("I'm a tooltip");
}
// I set up the listeners
window.onload = function() {
var links = document.querySelector("div.links");
for (var i = 0; i < links.children.length; i++) {
links.children[i].onmouseover = showTip;
}
};
</script>
</head>
<body>
<div class="links">
Tooltips
Tooltips
Tooltips
Tooltips
</div>
</body>
</html>

JavaScript for loop not changing link text

I have a nested for loop inside a for loop that is supposed to change the link text to a random number between 1 and 5. The ID of the links are "aX_Y", X and Y being numbers. The links are arranged in a 4x3 square. The problem is that the random numbers for the link text is only displayed for the last row:
<!DOCTYPE html>
<html>
<head>
<title>RISK</title>
<style type="text/css" media="screen">
a:link, a:visited {color: #eee;border:3px solid #ccc;text-decoration:none;padding:20px;}
.one {background: #7B3B3B;}
.two {background: #547980;}
#status {color: #eee;padding:1px;text-align:center}
.current {border:3px solid #000;}
</style>
<script type="text/javascript" charset="utf-8">
var xTurn = true;
var gameOver = false;
var numMoves = 0;
function newgame()
{
var status = document.getElementById('status');
numMoves = 0;
gameOver = false;
xTurn = true;
status.innerHTML = 'Player One\'s turn';
for(var x = 0; x < 4; x++)
{
for(var y = 0; y < 3; y++)
{
document.getElementById('a' + x + '_' + y).innerHTML = Math.floor(Math.random()*5 + 1);
console.log('a' + x + '_' + y);
}
}
}
function current(selected)
{
var status = document.getElementById('status');
var value = selected.value;
}
//document.getElementById("status").setAttribute("class", "two");
</script>
<meta name="viewport" content="user-scalable=no, width=device-width" />
</head>
<body onload='newgame();'>
<p id="status" class="one">Player One's turn</p>
<br />
<br />
<br />
<br /><br />
<p><input type="button" id="newgame" value="New Game" onclick="newgame();" /></p>
</body>
</html>
Here is a direct link to it:
https://dl.dropbox.com/u/750932/iPhone/risk.html
This change to your CSS fixes the issue:
a:link, a:visited
{
color: #eee;
border:3px solid #ccc;
text-decoration:none;
display:inline-block;
padding:20px;
}
(Tested in Firefox)
Your Javascript code is fine; all of the grid squares are getting populated with random numbers. What I am seeing instead is that each row of links is overlapping the previous row, so the numbers in the previous row are being hidden.
Is the overlapping intentional?
All the random numbers are being generated correctly. The top 2 rows are just hidden due to your CSS rules. You can prove this by making the following CSS change:
Change the line that looks like this:
a:link, a:visited {color: #eee;border:3px solid #ccc;text-decoration:none;padding:20px;}
to this:
a:link, a:visited {color: #eee;border:3px solid #ccc;text-decoration:none;}
And voila, it's all working beautifully.
Heh, I'm pretty sure it is working...the other boxes are just overlapped by the ones in front and you can't see them. Firebug shows values inside all the boxes.
a {
display:block;
float:left;
}
br {
clear:both;
}
...though actually those top-level elements shouldn't be restyled like that necessarily, I'd put it all in a <div id="game"></div> and make them .game a and .game br.

Dynamically arranging divs using jQuery

I have the following structure:
<div id="container">
<div id="someid1" style="float:right"></div>
<div id="someid2" style="float:right"></div>
<div id="someid3" style="float:right"></div>
<div id="someid4" style="float:right"></div>
</div>
Now someid is acually a unique id for that div. Now i receive an array which has a different order say someid 3,2,1,4, then how do i move these divs around to match the new order using jQuery?
Thankyou very much for your time.
My plugin version - Working Demo
Takes an array and optional id prefix and reorders elements whose ids correspond to the order of (id prefix) + values inside the array. Any values in the array that don't have an element with the corresponding id will be ignored, and any child elements that do not have an id within the array will be removed.
(function($) {
$.fn.reOrder = function(array, prefix) {
return this.each(function() {
prefix = prefix || "";
if (array) {
for(var i=0; i < array.length; i++)
array[i] = $('#' + prefix + array[i]);
$(this).empty();
for(var i=0; i < array.length; i++)
$(this).append(array[i]);
}
});
}
})(jQuery);
Code from the demo
jQuery
$(function() {
$('#go').click(function() {
var order = $('#order').val() == ""? null: $('#order').val().split(",");
$('#container').reOrder(order, 'someid');
});
});
(function($) {
$.fn.reOrder = function(array, prefix) {
return this.each(function() {
prefix = prefix || "";
if (array) {
for(var i=0; i < array.length; i++)
array[i] = $('#' + prefix + array[i]);
$(this).empty();
for(var i=0; i < array.length; i++)
$(this).append(array[i]);
}
});
}
})(jQuery);
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<title>reOrder Demo</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen">
body { background-color: #fff; font: 16px Helvetica, Arial; color: #000; }
div.style { width: 200px; height: 100px; border: 1px solid #000000; margin: 5px; }
</style>
</head>
<body>
<div id="container">
<div id="someid1" class="style" style="background-color:green;">div1</div>
<div id="someid2" class="style" style="background-color:blue;">div2</div>
<div id="someid3" class="style" style="background-color:red;">div3</div>
<div id="someid4" class="style" style="background-color:purple;">div4</div>
</div>
<p>Pass in a comma separated list of numbers 1-4 to reorder divs</p>
<input id="order" type="text" />
<input id="go" type="button" value="Re-Order" />
</body>
</html>
[Edit], This is tested and works:
var order = [3,2,1,4];
var container = $("#container");
var children = container.children();
container.empty();
for (var i = 0; i < order.length; i++){
container.append(children[order[i]-1])
}
The i-1 is necessary since your ordering starts at 1 but arrays are indexed from 0.
Thanks to J-P and Russ Cam for making me look at it again.
Here's a jQuery-less solution:
function appendNodesById(parent, ids) {
for(var i = 0, len = ids.length; i < len; ++i)
parent.appendChild(document.getElementById(ids[i]));
}
appendNodesById(document.getElementById('container'),
['someid4', 'someid2', 'someid3', 'someid1']);
If you have all the content in the array then remove all the content from the wrapper div container in your code. then start adding the received divs one by one:
var v = $(ar[0]).append(ar[1]).append(ar[2]);
$("#container").html(v);
If this does not works then look into this thread that discusses about positioning elements relative to other elements.

Categories

Resources