function is setting all instead of each - javascript

I have a simple function that sets the width of a bar based on an argument.
And I call the function on .each with jQuery.
The console logs the statement correctly, showing me it seems to work. However, the style seems to be overridden by the last value found.
Here is the function:
function barGraph(innerWidth, barWidth) {
innerWidth = parseInt(innerWidth) * .01 || .50;
barWidth = parseInt(barWidth) || 267;
// find percentage of total width
var innerWidth = Math.floor(innerWidth * barWidth);
var $innerBar = $('.slider-box div');
$innerBar.css('width', innerWidth + 'px');
console.log("Width should be: " + innerWidth + 'px');
}
then i call the function on each with jQuery:
$(document).ready(function() {
var $innerBar = $('.slider-box div');
$innerBar.each(function(index) {
var newWidth = $(this).attr("data-bar-width");
barGraph(newWidth, 267);
});
});
the console log shows 10 times, with all appropriate widths. However, the style for all is the same as the last width.
Can someone help explain how I get the function to set the width of the currently selected div?
Thanks so much in advance,
Adam.

Let's break it down
$(document).ready(function() {
var $innerBar = $('.slider-box div');
// going to call the barGraph function on each matching element
// so far, so good
$innerBar.each(function(index) {
var newWidth = $(this).attr("data-bar-width");
barGraph(newWidth, 267);
});
});
Then in barGraph
function barGraph(innerWidth, barWidth) {
innerWidth = parseInt(innerWidth) * .01 || .50;
barWidth = parseInt(barWidth) || 267;
// find percentage of total width
var innerWidth = Math.floor(innerWidth * barWidth);
// getting all the matching elements (again)
var $innerBar = $('.slider-box div');
// setting the width of each matched element to
// the innerwidth calculated in this barGraph call.
$innerBar.css('width', innerWidth + 'px');
console.log("Width should be: " + innerWidth + 'px');
}
So, the barGraph function is run as many times as there are matched elements in $('.slider-box div'), but each run sets the width of all matched elements. In effect, the last run will set the width of all matched elements to whatever the innerWidth is calculated to be on the last run. Is that what you want to happen?
What is more likely is perhaps something like this
$(function() {
var $innerBar = $('.slider-box div');
// going to call the barGraph function on each matching element
// so far, so good
$innerBar.each(function(index) {
var bar = $(this),
newWidth = bar.attr("data-bar-width");
barGraph(bar, newWidth, 267);
});
function barGraph(bar, innerWidth, barWidth) {
innerWidth = parseInt(innerWidth, 10) * .01 || .50;
barWidth = parseInt(barWidth, 10) || 267;
innerWidth = Math.floor(innerWidth * barWidth);
bar.css('width', innerWidth + 'px');
console.log("Width should be: " + innerWidth + 'px');
}
});
If the barGraph function is not used outside of the each call, then I might be inclined to move the function body inside of the anonymous function passed to each or modify barGraph function to be the function passed to each i.e.
$(function() {
$('.slider-box div').each(barGraph);
function barGraph(index, element) {
var bar = $(this),
newWidth = bar.attr("data-bar-width");
newWidth = parseInt(newWidth , 10) * .01 || .50;
newWidth = Math.floor(innerWidth * 267);
bar.css('width', newWidth + 'px');
console.log("Width should be: " + newWidth + 'px');
}
});

the problem is in here, have a look at your barGraph function:
var $innerBar = $('.slider-box div'); //here you choose all divs inside .slider-box
$innerBar.css('width', innerWidth + 'px'); //and set the width for all of them
change the barGraph function:
function barGraph(innerWidth, barWidth) {
innerWidth = parseInt(innerWidth) * .01 || .50;
barWidth = parseInt(barWidth) || 267;
// find percentage of total width
var innerWidth = Math.floor(innerWidth * barWidth);
var $innerBar = $('.slider-box div');
$innerBar.each(function(index){
$(this).css('width', innerWidth + 'px');
});
console.log("Width should be: " + innerWidth + 'px');
}

this may happen bacause on last barGraph() call you set all $('.slider-box div');
with last value you read into the each()
what if you try something like this
function barGraph(el, innerWidth, barWidth) {
...
el.css('width', innerWidth + 'px');
console.log("Width should be: " + innerWidth + 'px');
}
$(document).ready(function() {
var $innerBar = $('.slider-box div');
$innerBar.each(function(index, el) {
var newWidth = $(el).attr("data-bar-width");
barGraph($(el), newWidth, 267);
});
});
in this approach I passed a jQuery reference to each element, to the barGraph() function. It's also less expensive than before, since you always create a jQuery reference to a div collection.

In your barGraph function you select all instances of .slider-box div and set the width. You only want to set the one you are currently working with.
function barGraph($bar, innerWidth, barWidth) {
innerWidth = parseInt(innerWidth) * .01 || .50;
barWidth = parseInt(barWidth) || 267;
// find percentage of total width
var innerWidth = Math.floor(innerWidth * barWidth);
$bar.css('width', innerWidth + 'px');
console.log("Width should be: " + innerWidth + 'px');
}
by passing the bar into barGraph during the loop.
$(document).ready(function() {
var $innerBar = $('.slider-box div');
$innerBar.each(function(index) {
var $bar = $(this)
, newWidth = $bar.attr("data-bar-width");
barGraph($bar, newWidth, 267);
});
});
You may also want to move the selection of the width into the barGraph function to keep things clean.

Like most of jQuery's functions, css() works on collections as well as on single elements. Since $('.slider-box div') returns a collection, the CSS rule will be applied to all of the divs on every iteration of the each loop. So for 10 divs, barGraph will be called 10 × 10 = 100 times. And because the divs stay in the same order, the newWidth of the last div will be applied to all of the divs.
To apply the newWidth only to the current element in the each loop, you could keep all your logic inside that function:
var $innerBar = $('.slider-box div');
$('.slider-box div').each(function(index, element) {
var $this = $(this);
var innerWidth = ($this.attr("data-bar-width") / 10) || 0.5;
var barWidth = 267;
innerWidth = Math.floor(innerWidth * barWidth);
$this.css('width', innerWidth); // Note: no need to append 'px'; jQuery
});
This works pretty well as long as the logic is simple (a couple of lines at most) and you don't need to use it elsewhere. But when your code gets a bit more complex, you might want to brake it out into a separate function. Since you had already done this, let's look at some other solutions.
Two common solutions:
Eiter: pass the current element as a parameter of the function;
or: make it into a jQuery plugin.
The first is pretty simple:
function barGraph(element, innerWidth, barWidth) {
var $this = $(element);
// et cetera
}
// Call like this:
$('.slider-box div').each(function(index) {
var newWidth = $(this).attr("data-bar-width");
barGraph(this, newWidth, 267);
});
Like I said: simple. But it's not very neat, is it? The second solution is a bit more elegant:
$.fn.barGraph = function(barWidth) {
var $this = this; // `this` is already a jQuery object
var innerWidth = ($this.attr("data-bar-width") / 10) || 0.5;
barWidth = barWidth || 267;
innerWidth = Math.floor(innerWidth * barWidth);
$this.css('width', innerWidth);
};
// Use like this:
$('.slider-box div').each(function(index) {
$(this).barGraph(267);
});
And that's how easy it is to write your own jQuery plugin! Of course, it can use a little work, like a more descriptive name and support for chainability.

Related

setting a css variable via javascript

I am trying to get the height and width of the browser window and display it on the body as well as changing the height to match.
Here's my current code:
window.onresize = window.onload = function() {
width = this.innerWidth;
height = this.innerHeight;
document.body.innerHTML = width + 'x' + height; // For demo purposes
}
The above code displays the width and height on the body ok, now time to add it to a css variable:
var header = document.querySelector('.header')
window.onresize = window.onload = function() {
width = this.innerWidth;
height = this.innerHeight;
header.style.setProperty('--height', height);
header.style.setProperty('--width', width);
document.body.innerHTML = width + 'x' + height; // For demo purposes
}
I know the code is not correct but I can't find any sample to compare with, here's a fiddle just in case the code is not enough.
https://jsfiddle.net/rbtwsxd8/6/
You have a number of different issues here:
(at least in the fiddle) you were trying to document.queryselect the header element before it existed
your debug code overwrote the header element by setting document.body
You omitted the units when setting the height and width (This used to work in "quirks mode" but will not work in modern doctypes.)
You added extra double hyphens when trying to set the height and width
Here's a working version which corrects these problems:
window.onresize = window.onload = function() {
var header = document.querySelector('.header');
// your original code used 'this.innerWidth' etc, which does work
// (because the function is being run on the window object) but can
// be confusing; may be better to refer to the window object
// explicitly:
var width = window.innerWidth;
var height = window.innerHeight;
header.style.width = width + "px"; // need 'px' units
header.style.height = height + "px";
// the above is equivalent shorthand for
// header.style.setProperty('height', window.innerHeight + 'px');
// header.style.setProperty('width', window.innerWidth + 'px');
// setting this inside the header, so we don't remove it in the process:
header.innerHTML = width + "x" + height;
}
https://jsfiddle.net/pm7rgx4q/1/
window.onresize = window.onload = function() {
var header = document.querySelector('.header')
width = this.innerWidth;
height = this.innerHeight;
header.innerHTML = width + 'x' + height; // For demo purposes
header.style.setProperty('height', height + 'px')
header.style.setProperty('width', width + 'px');
//header.style.height = height + 'px';
//header.style.width =width + 'px';
}
fiddle

jquery infinite loop (to move a div position)

I have the following jquery code to move a rectangle from one place to another. It works.
jQuery(function ($) {
$(document).ready(function () {
loop_pos = function () {
var docHeight = $(document).height(),
docWidth = $(document).width(),
$div = $('#test'),
divWidth = $div.width(),
divHeight = $div.height(),
heightMax = docHeight - divHeight,
widthMax = docWidth - divWidth;
$div.css({
left: Math.floor(Math.random() * widthMax),
top: Math.floor(Math.random() * heightMax)
// here
});
}
// or here
});
});
Now, I want to make this rectangle change its position automatically in a infinite loop.
I tried to call the function loop_pos, in //here also // or here, but it doesn't work and I don't know how to do it.
Another solution maybe setTimeout() but I can't make it work.
Please a little help. Thanks.
First, refactor so that you don't compute widths and heights at every function call.
Then use setInterval to call a function every n milliseconds :
jQuery(function($) {
// No need for document.ready cause jQuery is made to execute this function when the doc is ready.
// initialize variables
var $div = $('#test'),
delay = 1000, // ms
docHeight,
docWidth,
divWidth,
divHeight,
heightMax,
widthMax;
function onResize(){
//update them
docHeight = $(document).height();
docWidth = $(document).width();
divWidth = $div.width();
divHeight = $div.height();
heightMax = docHeight - divHeight,
widthMax = docWidth - divWidth;
}
function loop_pos(){
$div.css({
left: Math.floor(Math.random()*widthMax),
top: Math.floor(Math.random()*heightMax)
// here
});
}
// Calculate for the first time
onResize();
// Add event listener
$(window).resize(onResize);
// run loop
setInterval(loop_pos, delay); // 1000 ms = 1 second
});
See fiddle :
http://jsfiddle.net/5f4quu8u/
"or here" is your answer, just refactor your code to prevent running your DOM for sizes when it's not needed:
jsFiddle demo
$(document).ready(function() {
var $div = $('#test'), x, y;
function getSizes(){
x = $(document).width() - $div.width();
y = $(document).height() - $div.height();
}
function loop_pos(){
$div.css({ // or use `.animate` instead of `.css`
left: ~~(Math.random() * x),
top: ~~(Math.random() * y)
});
}
getSizes(); // Get them immediately
$("window").on("resize", getSizes); // Update values on Win. resize
setInterval(loop_pos, 1000);
});
Well, I would first move your code outside the $(document).ready()-part and declare it as a separate function.
function moveDiv(){
var docHeight = $(document).height();
docWidth = $(document).width();
$div = $('#test');
divWidth = $div.width();
divHeight = $div.height();
heightMax = docHeight - divHeight;
widthMax = docWidth - divWidth;
$div.css({
left: Math.floor(Math.random()*widthMax),
top: Math.floor(Math.random()*heightMax)
});
}
Then you can use the SetInterval-function in javascript.
$(document).ready(function() {
var delay = 1*1000; //would move the div every 1 second
setInterval( moveDiv , delay );
});
That should do it I think. (Given that your code works which it seems to me it should!)

Making a function work in all divs with the same class using jquery

I have a responsive div (.container) that contains a row of nested divs (.imgWrapper) that each contain an image (.imgWrapper img). The purpose of the following code is to make all images the same height while still fitting in one row in the container despite all images being a different proportion to one another (i.e. landscape and portrait)
var totalHeight = 100;
var totalWidth = 1;
$('.imgWrapper').each(function(){
totalWidth += $(this).outerWidth();
});
var containerWidth = $('.container').width();
var ratio = totalWidth / totalHeight;
var containerHeight = containerWidth / ratio;
$('.container').css('height',containerHeight + "px");
$('.imgWrapper img').css('height',containerHeight + "px");
var newTotalWidth = 0;
$('.imgWrapper').each(function(){
newTotalWidth += $(this).outerWidth();
});
$('.container').css('width',newTotalWidth + "px");
});
});
This all works great if there is only one div with the class '.container', however as soon as I add div with the same class, the function is applied to all images instead of images in each '.container' div. How to I apply this function to each '.container' div one at a time?
Here is a jsfidde example: http://jsfiddle.net/tbbeqcqb/
Not 100% sure what you are trying to achieve, but I think it's this;
$(window).bind("load", function() {
var totalHeight = 100;
$('.container').each(function(){
var totalWidth = 1;
$(this).children().filter('.imgWrapper').each(function(){
totalWidth += $(this).outerWidth();
});
var containerWidth = $(this).width();
var ratio = totalWidth / totalHeight;
var containerHeight = containerWidth / ratio;
$(this).css('height',containerHeight + 'px');
var newTotalWidth = 0;
$(this).children().filter('.imgWrapper').each(function(){
$(this).children('img').css('height',containerHeight + 'px');
newTotalWidth += $(this).outerWidth();
});
$(this).css('width',newTotalWidth + 'px');
});
});

making a function works on specific widths

I am trying to make this function works only when the screen size is above 1024px.
//Parallax background image
var velocity = 0.5;
function update(){
var pos = $(window).scrollTop();
$('.parallax').each(function() {
var $element = $(this);
var height = $element.height();
$(this).css('background-position', '40%' + Math.round((height - pos) * velocity) + 'px');
});
};$(window).bind('scroll', update); update();
Here is what I have tried to do:
//Parallax background image
var velocity = 0.5;
$(window).on("ready resize", function() {
if ($(window).width() < 770) {
function update(){
var pos = $(window).scrollTop();
$('.parallax').each(function() {
var $element = $(this);
var height = $element.height();
$(this).css('background-position', '40%' + Math.round((height - pos) * velocity) + 'px');
});
};});$(window).bind('scroll', update); update();
I really don't know what I am doing wrong...
You haven't stated what the problem you're coming across is. If it's "my code doesn't work", then perhaps you should check your syntax first. Your braces are messed up.
//Initialize velocity and empty update function
var velocity = 0.5;
var update = function () {};
//When window is ready (content loaded) OR resized, execute the following function
$(window).on("ready resize", function () {
if ($(window).width() >= 1024) { //Check if window width is 1024px wide or larger
update = function () { //Set update to run this function when executed.
var pos = $(window).scrollTop(); //Get scrollbar position https://api.jquery.com/scrollTop/
//For each element with 'parallax' class, execute the following function
$('.parallax').each(function () {
var $element = $(this); //Get the current parallax-classed element
var height = $element.height(); //Save the current height of this element
//Set the CSS of this parallax-classed element set the background position
$(this).css('background-position', '40% + ' + Math.round((height - pos) * velocity) + 'px');
});
};
} else { //Execute if screen width is < 1024px
update = function () {}; //Set update to do nothing
}
});
//When window is scrolled through, run the update function
$(window).bind('scroll', update);
//update();
Last line is unnecessary, as resize will handle function value, and scroll will handle the execution.
You were missing a + or - within the background-position setting.
So for example, if the result of your Math.round() was "30", then Javascript would interpret that line as $(this).css('background-position', '40%30px'); which obviously would cause issues. I'm sure you wanted it to say something like $(this).css('background-position', '40% + 30px');.

jQuery - Fixing the animation

I am creating a new "whack-a-mole" style game where the children have to hit the correct numbers in accordance to the question.
I have the numbers animating from a set top position to another with a random width so that they look like they are floating up like bubbles.
The only problem I am having with it is that sometimes the numbers glitch and the width on them changes suddenly making it appear to jump from one side of the container to the other.
The only explanation I can think of is the width must be resetting somewhere which I have tried to look for.
Either I am blind or it is something else, can someone help me to find the source of the problem.
Here is the code that maps the numbers...
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function scramble() {
var children = $('#container').children();
var randomId = randomFromTo(1, children.length);
moveRandom("char" + randomId);
}
function moveRandom(id) {
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var bWidth = $('#' + id).width();
var bHeight = $('#' + id).css(
'top', '400px'
).fadeIn(1000).animate({
' top': '-100px'
}, 10000).fadeOut(1000);
maxWidth = cPos.left + cWidth - bWidth;
minWidth = cPos.left;
newWidth = randomFromTo(minWidth, maxWidth);
$('#' + id).css({
left: newWidth
}).fadeIn(1000, function () {
setTimeout(function () {
$('#' + id).fadeOut(1000);
window.cont++;
}, 1000);
});
Here is also a working fiddle so you can see the issue I am talking about: http://jsfiddle.net/pUwKb/26/
The problem is that you are re-entering your moveRandom function for an ID that is already animated. The new width calculation causes the piece to seem to jump when it is reassigned during the already animated movement. One way to fix this is to reject new piece movements for pieces you are already animating. I modified your jsFiddle and fixed it with this code:
// Keep track of the pieces actually moving
var currentMoving = [];
function moveRandom(id) {
// If this one's already animating, skip it
if ($.inArray(id, currentMoving) !== -1) {
return;
}
// Mark this one as animating
currentMoving.push(id);
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var bWidth = $('#' + id).width();
var bHeight = $('#' + id).css('top', '400px').fadeIn(1000).animate({
'top': '-100px'
}, 10000).fadeOut(1000);
maxWidth = cPos.left + cWidth - bWidth;
minWidth = cPos.left;
newWidth = randomFromTo(minWidth, maxWidth);
$('#' + id).css({
left: newWidth
}).fadeIn(1000, function () {
setTimeout(function () {
$('#' + id).fadeOut(1000);
// Mark this as no longer animating
var ix = $.inArray(id, currentMoving);
if (ix !== -1) {
currentMoving.splice(ix, 1);
}
window.cont++;
}, 1000);
});
}
Forked jsFiddle here.
Edit: The OP wanted to show more divs at once without speeding the animation up. To do this I added 20 more character divs (each a duplicate of the first 10 numbers), fixed the guarding code a bit, altered the CSS to specify the image of the character by class, and then put a limit of 20 animations at a given time. I also put a loop around the rejection of an already animated piece, to pick another. I made some other minor improvements. Updated JSFiddle here.

Categories