I have an array of image objects:
var pics = ["pic1","pic2","pic3","pic4","pic5","pic6"]
I want to loop through and set the style.left value to 1 less than the current value. This loop iterates 100 times.
I got a great suggestion here to use css for this. I created a style called move:
margin-left: -1px;
This works great the first time through the loop:
for (i = 0; i < 100; i++) {
for (j = 0; j < 6; j++) {
var image = document.getElementById(pics[j]);
image.className = image.className + " move"
}
}
The problem is the images do not move after the first iteration though the loop. Any ideas what I can do?
The images do not move after the first iteration because you are applying the move class which sets the margin left to -1. Note it does not subtract 1 from their current left margin it sets it to -1.
So lets say it starts out with a left margin of 10 . When it goes through this loop I think you are expecting it to have a left margin of 9 to move it one pixel closer to the left side of the screen. Instead when it goes through this loop it will skip all the way -1 as the left margin because you are explicitly telling it to do that with your move class.
What you really want to do is use javascript to grab its current left margin , subtract 1 from that and then reset the left margin as the new, now less one, number.
At quick glance I think this tutorial could help you.
You can save off the margin variable and apply css with this:
var margin = -1;
for (i = 0; i < 100; i++) {
for (j = 0; j < 6; j++) {
var image = document.getElementById(pics[j]);
image.style.marginLeft = margin + "px";
margin--;
}
}
Related
Getting really stuck on this one... I'm trying to build a donation progress bar... In ReactJS - but I'm a beginner, so I want to get the code right first in Vanilla js...
What I'm trying to do, is loop through an array of numbers, (aka, donations already submitted via a form). EG:
[2, 5, 25] etc.
Everytime, a donation is submitted, it get's added to this array.
Want I want, is for my donation bar to increase/fill in colour, based on the donations already made in the array.
The bar would be full at 100%. Or £100.
Here's the snippet of JS I already have:
// FUNCTION TO CALCULATE TOTAL DONATIONS
const numbers = donated.map(Number);
function add(a, b) {
return a + b;
}
// SUM VALUE OF NUMBERS IN THE ARRAY
const sum = numbers.reduce(add, 0);
console.log('numbers', numbers);
//THE VALUE OF NUMBERS IN ARRAY, TURNED INTO A PERCENTAGE
const total = 100;
const percentage = (sum / total) * 100;
console.log('percentage', percentage);
// LOOP THROUGH EVERY NUMBER IN THE ARRAY, AND ADD A DIV WITH A MATCHING WIDTH
for (var i = 0; i < numbers; i++) {
if (i < 100) {
const div = document.createElement('div');
div.style.background = 'red';
div.style.width = numbers + 'px';
div.style.height = '50px';
div.style.float = 'left';
document.querySelector('.bar').appendChild(div);
}
}
The loop works, slightly. The first div in the array gets added. But as I add more donations, no more divs are added to the progress bar.
Eventually, I want to stop at 100...
Got it working! I needed to set numbers[i] in my style width:
for (var i = 0; i < numbers.length; i++) {
if (i < 100) {
div +1;
const div = document.createElement('div');
div.style.background = 'red';
**div.style.width = numbers[i] + 'px';**
div.style.height = '50px';
div.style.float = 'left';
document.querySelector('.bar').appendChild(div);
}
}
As i see, you styled your divs float left. That mean each div will appears over the previous on left. In chrome, right click on the div and left click on inspect. You will see that you have many divs generated in your Dom.
So I know this code is randomly removing to of my objects to create a hole so another object can go through but line by line I would like to go through and understand each part. Would be great if someone not so arrogant could help me because I'm new. I would appreciate any help. The area I don't understand is the last part which I have highlighted in bold. Thank you.
// Add a pipe on the screen
add_one_pipe: function(x, y) {
// Get the first dead pipe of our group
var pipe = this.pipes.getFirstDead();
// Set the new position of the pokeballs
pipe.reset(x, y);
// Add velocity to the pokeballs to make it move left
pipe.body.velocity.x = -200;
// Kill the pokeballs when it's no longer visible
pipe.outOfBoundsKill = true;
},
**add_row_of_pipes: function() {
var hole = Math.floor(Math.random()*5)+1;**
**for (var i = 0; i < 8; i++)
if (i != hole && i != hole +1)
this.add_one_pipe(400, i*60+10);**
add_row_of_pipes will add 6 pipes at fixed intervals heights, but with a randomly placed gap of 2 missing pipes.
var hole = Math.floor(Math.random()*5)+1;
Take a random number (between 0 and 0.999),
Multiply by 5 (possible range is now 0-4.9999...),
Round down (0-4),
Add one (1-5)
This value is where the hole is
for (var i = 0; i < 8; i++)
For the whole numbers 0 to 7 inclusive, representing height, i...
if (i != hole && i != hole +1)
If this height is not where the hole starts, nor the next value,
this.add_one_pipe(400, i*60+10);
Add a pipe at width 400, and height i*60+10.
I already have a function to swap the images, but they swap without any animation. Now I want a function witch swaps the images with a fading. Here is my written code for the swap function:
var header = new Array();
header[0] = "images/lan/IMG_2799.jpg";
header[1] = "images/lan/IMG_2816.jpg";
var x = 0;
function swapHeader() {
document.getElementById("header").style.backgroundImage = "url(" + header[x] + ")";
if (x < header.length - 1) x++; else x = 0;
setTimeout("swapHeader()", 5000);
}
window.onload=swapHeader;
You may want to use two elements, one per image. Then, slowly increase and decrease the opacity (a CSS property) of the one on top. The rest of your content should go above those two elements, and have a transparent background.
Because we can't set table-cell's max-height so I am using this loop to limit row height (equal to cell), by increasing the table width until all rows height < height I set in loop.
var i = 0,
row, table = document.getElementsByTagName('table')[0],
j = table.offsetWidth;
while (row = table.rows[i++]) {
while (row.offsetHeight > 200 && j < 3000) {
j += 250;
table.style.width = j + 'px';
}
}
But there is a problem in this script, if for some reason a specific row height can't be less than 400px, which is greater than max-height I set for rows in script, the table becomes unnecessary 3000px wide because of the condition in loop.
Is there any way to send loop to next row if a row height is same before and after table width increment by loop. I tried web searching but nothing found to handle this, if anyone here knows about this please help me
Example (no problem when no <br/> tag): http://jsfiddle.net/jm5cpqr4/5/
Example (problem when <br/> usage limiting least possible height of cell): http://jsfiddle.net/jm5cpqr4/6/
script47 correctly indicated a break statement is used in the resolution.
Readers may need to reduce the offset value tested in the "failing" case on jsfiddle to reproduce the problem in their browser - it did't immediately fail for me
A solution example:
function loaded()
{ var i = 0, row, table = document.getElementsByTagName('table')[0],
j = table.offsetWidth;
var testHeight = 100;
while (row = table.rows[i++])
{ var h0 = row.offsetHeight;
while (row.offsetHeight > testHeight && j < 3000)
{ j += testHeight;
table.style.width = j + 'px';
if(row.offsetHeight==h0)
{ table.style.width = j-testHeight + 'px'
break;
}
}
}
}
loaded();
Say I have a total width of 585px. And I wanted to divide the space into equal sections and assign each an index value within position. I could do something like this if I had lets say 6 sections: (assigned by total width / number of sections)
//Set up elements with variables
this.sliderContent = config.sliderContent;
this.sectionsWrap = config.sectionsWrap;
//Selects <a>
this.sectionsLinks = this.sectionsWrap.children().children();
//Create drag handle
this.sectionsWrap.parent().append($(document.createElement("div")).addClass("handle-containment")
.append($(document.createElement("a")).addClass("handle ui-corner-all").text("DRAG")));
//Select handle
this.sliderHandle = $(".handle");
var left = ui.position.left,
position = [];
var position = ((left >= 0 && left <= 80) ? [0, 1] :
((left >= 81 && left <= 198) ? [117, 2] :
((left >= 199 && left <= 315) ? [234, 3] :
((left >= 316 && left <= 430) ? [351, 4] :
((left >= 431 && left <= 548) ? [468, 5] :
((left >= 549) ? [585, 6] : [] ) ) ) ) ) );
if (position.length) {
$(".handle").animate({
left : position[0]
}, 400);
Slider.contentTransitions(position);
}
But what if I had an x number of sections. These sections are just elements like
<li><a></a></li>
<li><a></a></li>
<li><a></a></li>
Or
<div><a></a></div>
<div><a></a></div>
<div><a></a></div>
<div><a></a></div>
How would I divide the total of 585px and classify the index in position according to the current left value of the .handle element? I can know where the drag handle is by using ui.position.left, what I want is to be able to set an index for each element and be able to animate handle depending on where the handle is within the indexed elements. Since each element is indexed I later call a transition method and pass in the current index # to be displayed. The code I show above works, but isn't really efficient. I also need to account for the width of the handle to fit the section width. http://jsfiddle.net/yfqhV/1/
Ok, there is a slight inconsistency in the difference between the range figures in the question, which makes it hard to algorithmise [ my made-up-word de jour =) ] this exactly:
81 to 199 = 118
199 to 316 = 117
316 to 431 = 115
431 to 518 = 118
If you can adjust for that, I have a solution - it's not especially clever JavaScript, so there may well be better ways to do this (SO JS people, feel free to educate me!) but it works.
First we need a function to find the index of an array range, a given value falls within (this replaces your nested if-else shorthands), then we have a function to set up the positional arrays, and finally we can do a range search and return the corresponding array of values.
This solution should dynamically deal with a varying number of sections, as long as this line:
var len = $("#sectionContainer").children().length;
is adjusted accordingly. The only other values that may need adjusting are:
var totalWidth = 585;
var xPos = 81;
although you could set them if you have elements you can draw the values from, making it even more of a dynamic solution.
/**
* function to find the index of an array element where a given value falls
* between the range of values defined by array[index] and array[index+1]
*/
function findInRangeArray(arr, val){
for (var n = 0; n < arr.length-1; n++){
if ((val >= arr[n]) && (val < (arr[n+1]))) {
break;
}
}
return n;
}
/**
* function to set up arrays containing positional values
*/
function initPositionArrays() {
posArray = [];
leftPosArray = [];
var totalWidth = 585;
var xPos = 81;
var len = $("#sectionContainer").children().length;
var unit = totalWidth/(len - 1);
for (var i=1; i<=len; i++) {
pos = unit*(i-1);
posArray.push([Math.round(pos), i]);
xMin = (i >= 2 ? (i==2 ? xPos : leftPosArray[i-2] + posArray[1][0]) : 0);
leftPosArray.push(Math.round(xMin));
}
}
var left = ui.position.left;
initPositionArrays();
// find which index of "leftPosArray" range that "left" falls within
foundPos = findInRangeArray(leftPosArray, left);
var position = posArray[foundPos];
if (position.length) {
$(".handle").animate({
left : position[0]
}, 400);
Slider.contentTransitions(position);
}
I've set up a jsFiddle to illustrate.
Enjoy!
Edit
I've looked at #JonnySooter s own answer, and whilst it calculates the positioning correctly, it won't deal with a variable number of sections.
To get it to work with any number of sections, the handleContainment div (that is created on-the-fly) needs to have it's width set dynamically (via inline styling).
This is calculated by multiplying the number of sections by the width of each section (which is actually the same as the width of the slider).
This is all done after creating the handle so that the width can be extracted from the "handle" css class, meaning a change to the width of the handle will cascade into the routine when applied at the css level.
See this jsFiddle where the number of sections can be altered and the slider behaves properly.
var numSections = // ...;
var totalWidth = // ...;
var sectionWidth = totalWidth / numSections;
var index = Math.floor($(".handle").position().left / sectionWidth);
var leftPosition = index * sectionWidth;
var rightPosition = leftPosition + sectionWidth - 1;
UPDATE:
I worked on trying to find a solution myself and this is what I came up with:
function( event, ui ) {
var left = ui.position.left, //Get the current position of the handle
self = Slider, //Set to the Slider object cus func is a callback
position = 1;
sections_count = self.sectionsLinks.length, //Count the sections
section_position = Math.floor(self.sectionsWrap.width() / sections_count); //Set width of each section according to total width and section count
left = Math.round(left / section_position); //Set the index
position = (left * section_position); //Set the left ammount
if(position < section_position){ //If handle is dropped in the first section
position = 0.1; //Set the distance to animate
left = 0; //Set index to first section
}
if (position.length) {
$(this).animate({
left : position //Animate according to distance
}, 200);
left = left += 1; //Add one to the index so that I can use the nth() child selector later.
self.contentTransitions(left);
}
}