Resize a group of variably sized images both horizontally & vertically - need help optimising - javascript

WARNING CODE CRASHES IN EVERYTHING EXCEPT GOOGLE CHROME
I'm trying to create a feature on our website that takes 8 random images and places them in two rows and dynamically resizes the images to take up the full width of the page.
I've created a jsbin for this to try and demonstrate the issue.
https://jsbin.com/yijemazovi/edit?html,css,js,output
The comments in the code should give you an good idea of what I'm doing. What seems to be happening for everything but Google Chrome is that the while condition is never satisfied so it goes on infinitely and crashes the browser.
Perhaps it is something as simple as I am doing the do/while loop incorrectly or I should just be using a while loop???
Any help is appreciated!
/*****
* Get the overall width of the container that we want to match
**********/
var ContainerWidth = $('.feature-inim-collage .col.span_1_of_1').width();
/*****
* Increase the height of the images until the total sum of the width
* if the 4 images + the gutters is larger than ContainerWidth - then
* stop
**********/
/*****
* Increment in jumps of 10px until we get within 80% of the width of
* the ContainerWidth and then go to a more precise increment of 1px.
* We can increase the px from 10 to 20 or 30 so there are less loops
* but this can cause issues when we look at mobile and there is less
* overall width in the containers and jumping by 30px will be too much
**********/
var i = 0;
do {
$('.feature-inims-top-row .growable-container').css('height', i);
var RowWidth1 = CalculateTotalWidth(1);
if(RowWidth1 < (ContainerWidth*0.8)){
i = i+10;
}else{
i++;
}
}
while (RowWidth1 < (ContainerWidth - 3));
/*****
* Repeat above for the 2nd row
**********/
var i = 0;
do {
$('.feature-inims-bottom-row .growable-container').css('height', i);
var RowWidth2 = CalculateTotalWidth(2);
if(RowWidth2 < (ContainerWidth*0.8)){
i = i+10;
}else{
i++;
}
}
while (RowWidth2 < (ContainerWidth - 3));
/*********
* Calculate the combined width of the images + the gutters
****/
function CalculateTotalWidth(Row) {
var Image1Width = $('.growable-container-1').width();
var Image2Width = $('.growable-container-2').width();
var Image3Width = $('.growable-container-3').width();
var Image4Width = $('.growable-container-4').width();
var Image5Width = $('.growable-container-5').width();
var Image6Width = $('.growable-container-6').width();
var Image7Width = $('.growable-container-7').width();
var Image8Width = $('.growable-container-8').width();
var GutterSize = 24; // (3 gutters # 8px each)
if(Row == 1){
var RowWidth = GutterSize + Image1Width + Image2Width + Image3Width + Image4Width;
}else{
var RowWidth = GutterSize + Image5Width + Image6Width + Image7Width + Image8Width;
}
return RowWidth
}

It turns out the issue with this was that in the CalculateTotalWidth() function I was checking the width of the container the image was in rather than the image itself. As soon as I changed this it worked perfectly.
var Image1Width = $('.growable-container-1 img').width();
instead of
var Image1Width = $('.growable-container-1').width();

Related

How do you avoid the "RangeError: Maximum call stack size exceeded" error?

I'm currently working on a maze generating algorithm called recursive division. The algorithm is quite simple to understand: Step 1: if the height of your chamber is smaller than the width, divide your grid/chamber with a vertical line. If the height is greater than the width, then divide your chamber with a horizontal line. Step 2: Repeat step 1 with the sub-chambers that were created by the lines. You want to repeat these steps until you get a maze (until the width or height equals 1 unit).
The problem that I have with this algorithm is that JavaScript prints out a RangeError, meaning that I called the function that creates the maze too many times (I'm trying to implement this algorithm with a recursive function). Is there any way to avoid/prevent this from happening? Or am I missing something important in my code that makes the algorithm not work properly?
I have tried to implement a trampoline function, but since I'm a beginner I just don't understand it well enough to implement my self. I have also restarted my entire project ruffly 3 times with some hope that I will come up with a different approach to this problem, but I get the same error every time.
My code here:
//leftCord = the left most x coordinate of my chamber/grid, upCord = the upmost y coordinate of my
grid etc.
//(0, 0) IS POSITIONED IN THE LEFT TOP NODE OF MY GRID
function createMaze(leftCord, rightCord, upCord, downCord) {
var height = Math.abs(downCord - upCord);
var width = Math.abs(rightCord - leftCord);
if (height < 2 || width < 2) {
//The maze is completed!
return;
} else {
if (height < width) {
//cut the chamber/grid vertically
//Getting a random number that's EVEN and drawing the function x = 'random number' on the grid
var x = randomNum(leftCord / 2, rightCord / 2) * 2;
var lineX = [];
for (i = upCord; i < downCord; i++) {
lineX.push(grid[i][x]);
}
//Making a random door/passage and making sure it's ODD
var randomDoor = randomNum(0, lineX.length / 2) * 2 + 1;
lineX.splice(randomDoor, 1);
//Drawing the line
for (i = 0; i < lineX.length; i++) {
lineX[i].className = "wall";
}
//Making the same thing again, but with the left and right sub-chambers that were created by the line
createMaze(leftCord, x, upCord, downCord);
createMaze(x, rightCord, upCord, downCord);
} else {
//cut the chamber/grid horizontally
//Getting a random number that's EVEN and drawing the function y = 'random number' on the grid
var y = randomNum(0, downCord / 2) * 2;
var lineY = [];
for (i = leftCord; i < rightCord; i++) {
lineY.push(grid[y][i]);
}
//Making a random door/passage and making sure it's ODD
var randomDoor = randomNum(0, lineY.length / 2) * 2 + 1;
lineY.splice(randomDoor, 1);
//Drawing the line
for(i = 0; i < lineY.length; i++){
lineY[i].className = "wall";
}
//Making the same thing again, but with the upper and lower-chambers that were created by the line
createMaze(leftCord, rightCord, upCord, y);
createMaze(leftCord, rightCord, y, downCord);
}
}
}
This is happening because you never initialize i with var- it is sent into the global scope and is overwritten each function call.

Javascript onclick event on newly created elements

I've got several functions all linked so it will ...
create new elements and set their properties and stuff
once elements are in place they should trigger function.
And they do! Kind of...
More like they trigger half of a function that's attached to them upon creation. The part where they onclick trigger a function that starts a loading on my progress bar (which is their purpose) is alright. But the much simpler part, where they hide after click, doesn't.
As the code is quite complex I'll place here larger part of it, so don't panic. Problem might be somewhere else then I expect. Here it is...
// defines function for checkpoint
function checkpointed() {
this.style.display = 'none'; // !here dwells the douch part!
if (toLoad < 100) {
toLoad += 100/$(this).attr('numButs');
var sim = setInterval(progressSim, 50);
}
// defining creation of checkpoints
function checkpoints(num) {
var angle = 4.72,
step = (2 * Math.PI) / num;
for (i = 1; i < num + 1; i++) {
var x = $('#progressBar').width()/2 + radius * Math.cos(angle) ;
var y = $('#progressBar').height()/2 + radius * Math.sin(angle);
angle += step;
var newContent = document.createElement('IMG');
var newCheckpoint = document.createElement('SPAN');
var numButs = document.createAttribute('numButs');
numButs.value = num;
var Class = document.createAttribute('class');
Class.value = 'checkpoint';
var img = document.createAttribute('src');
img.value = 'img/lock.png';
newContent.setAttributeNode(img);
newCheckpoint.setAttributeNode(numButs);
newCheckpoint.setAttributeNode(Class);
$(newCheckpoint).append(newContent);
$('.projectBar').append(newCheckpoint);
x -= 24;
y -= 24;
$(newCheckpoint).offset({top:y, left: x});
newCheckpoint.onclick = checkpointed;
};
};
// creates checkpoints upon clicking on create button
document.getElementById('create').onclick = function(){
checkpoints(document.getElementById('numCheckpoint').value);
$(this).hide();
$('#numCheckpoint').hide();
};
I should probably sum up what is this all about.
I have circular progressBar that measures progression of users project. User says "Hey, my project has like 5 steps (or 20 idc)" and "create" button will make 5 checkpoints placed on the progressBar evenly. By clicking on checkpoints you load the progress bar by 20% per clicked checkpoint.
Don't worry though, I've already figured out the code for loading and the geometrics.
However I'm bit stuck here... on simple onclick functions. Please if you have an idea try achieve it with plain JavaScript or jQuery (trying to do this without other frameworks, libraries or plugins).
EDIT: Just found out that checkpoint are set alright, as they really hide after clicking. Problem is in creation of checkpoints as the loop creates about 15 more checkpoints stacked one on another. So you have to click each of them to hide them all... So problem is in the loop.
EDIT2: Figured it out. The loop for (i = 1; i < num + 1; i++) had the numparameter as a String coming from input field. So simple parseInt() did the trick.
The mixed Jquery and plain Javascript is messing with my head... Any way how about when you create a new element, give it some sort of class. Instead of giving setting onclick, use jQuery's on selector to bind click events to those dynamic elements. Try The Following:
$(document).on("click", ".Checkpoint", function(event) {
$(event.target).hide();
if (toLoad < 100) {
toLoad += 100 / $(this).attr('numButs');
var sim = setInterval(progressSim, 50);
}
});
// defining creation of checkpoints
function checkpoints(num) {
var angle = 4.72,
step = (2 * Math.PI) / num;
for (i = 1; i < num + 1; i++) {
var x = $('#progressBar').width() / 2 + radius * Math.cos(angle);
var y = $('#progressBar').height() / 2 + radius * Math.sin(angle);
angle += step;
var newContent = $('<img></img>');
var newCheckpoint = $('<span></span>');
$("body").append(newCheckpoint);
newContent.attr("numButs", num);
newContent.attr("src", 'img/lock.png');
newContent.addClass("Checkpoint");
$(newCheckpoint).append(newContent);
$('.projectBar').append(newCheckpoint);
x -= 24;
y -= 24;
$(newCheckpoint).offset({
top: y,
left: x
});
}
}
// creates checkpoints upon clicking on create button
$(document).on("click","#create",function(e) {
checkpoints($('#numCheckpoint').val());
$(e.target).hide();
$('#numCheckpoint').hide();
});
Changed stuff to work more in jQuery, hope you don't mind...

Can anyone help me make my JavaScript faster?

In my program, I am making divs boxes with JavaScript and it seems to be taking quite a bit of time making a 50X50 grid.It even takes some time to make 20x20 grid. I have looked up how to make my code faster , but none of the suggestions have made an insignificance difference.
This an Odin Project
https://jsfiddle.net/Mulk/yc5rsf1m/#&togetherjs=dVAh1FK7On
$(document).ready(function(){
// Defalut Grid is 16x 16 Grid
CreateBox(16);
CallMeMaybe();
$("#gridbtn").click(function(){
$(".odd").remove();
var numbox =parseInt(prompt("How many boxes would like?"));
CreateBox(numbox);
});
function CreateBox(a) {
var wh = (500/a), i , j ;
for(i=0;i<a;i++){
for(j=0;j<a;j++){
$div = $('<div/>').appendTo('#container').addClass(".odd").attr('class','odd').width(wh).height(wh);
CallMeMaybe();
}}
};
// Play with me
function CallMeMaybe(a){
$(".odd").hover(function(){
var r = Math.floor(Math.random() * (256 - 0) + 0);
var g = Math.floor(Math.random() * (256 - 0) + 0);
var b = Math.floor(Math.random() * (256 - 0) + 0);
var color = "rgb("+r+","+g+","+b+")"
$(this).css("background-color", color);
});
};
// Play with me
});
Instead of appending each new element to the DOM, which is very expensive, append all the elements to a document fragment first, then append the whole thing to the DOM.
This should be faster. It creates an array of the divs and appends them to the DOM all at once. The divs are created with the attributes instead of changing their attributes after they have been added to the DOM. It also attaches the hover handler at the time of creation instead of attaching it after all the boxes have been created. This should significantly reduce the number of DOM manipulations that need to happen.
$(function(){
function createBox(boxesInRow) {
var wh = (500/boxesInRow), i , j, divs = [];
for(i = 0; i < boxesInRow; i++){
for(j = 0; j < boxesInRow; j++){
divs.push($('<div/>', {
"class":'odd',
height: wh + 'px',
width: wh + 'px',
hover: hoverCallback
}));
}
}
$('#container').empty().append(divs);
}
function hoverCallback() {
var r = Math.floor(Math.random() * 256),
g = Math.floor(Math.random() * 256),
b = Math.floor(Math.random() * 256);
$(this).css("background-color", "rgb("+r+","+g+","+b+")");
}
// Default Grid is 16x16 Grid
createBox(16);
$("#gridbtn").click(function() {
var numbox = parseInt(prompt("How many boxes would you like per row?"));
createBox(numbox);
});
});

Fill window with divs. Div pixel height displayed incorrectly in google chrome. Width works

I want to fill the window size with divs. For a specified div size in px, the screen will be filled as much as it can be, leaving a remainder edge amount of px on the side and bottom. This remainder amount is then divided by the number of cells in the row (or column) and that is then added to the height (or width) of each cell in the row (or column).
For the width this works perfectly but when the same logic is applied to the height, it breaks. Both width and height work in firefox.
Screenshot: http://i.imgur.com/mpDCM0G.png
JSfiddle of making the divs: https://jsfiddle.net/xb82c4zt/
Live: http://conwaygameoflife.heroku.com/
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var size = 100;
// Calculate the number of cells we can fit in the width
//and height (there will be extra space)
w = Math.floor(windowWidth / size);
h = Math.floor(windowHeight / size);
// Calculate the extra space
var widthDiff = windowWidth % size;
var heightDiff = windowHeight % size;
// Add the needed amount of height and width to each cell to fill the window
var widthSize = size + widthDiff / w;
var heightSize = size + heightDiff / h;
// Begin to alter the DOM
var parentDiv = document.createElement('div');
parentDiv.className = 'grid';
for(var y = 0; y < h; y++) {
for(var x = 0; x < w; x++) {
var cellDiv = document.createElement('div')
cellDiv.className = 'cellDiv'
cellDiv.style.height = heightSize + 'px';
cellDiv.style.width = widthSize + 'px';
parentDiv.appendChild(cellDiv)
}
}
document.body.appendChild(parentDiv)
In Chrome (and probably other browsers), height and width pixel values are truncated! See this stackoverflow answer with the related jsFiddle
Precentage values are truncated too, but not as severely. So, to solve this you can convert pixels to percentages as I did in this jsFiddle.
The main thing I added was:
var widthPercent = widthSize / windowWidth * 100;
var heightPercent = heightSize / windowHeight * 100;
Because we're using percentages now, the parent container must have width/height:
parentDiv.style.height = windowHeight + 'px';
parentDiv.style.width = windowWidth + 'px';
And changed the loop to:
for(var x = 0; x < w*h; x++) {
var cellDiv = document.createElement('div');
cellDiv.className = 'cellDiv';
cellDiv.style.height = heightPercent + '%';
cellDiv.style.width = widthPercent + '%';
parentDiv.appendChild(cellDiv)
}
Now this doesn't always work in chrome perfectly. However, it does make it perfect in some cases... basically depends on when (and how drastic) the truncation of percentages is.
After further reflection, it looks like percentages get resolved to fractional pixel values as well... which still get truncated in Chrome. So, let's make our math better, and figure out the biggest non-fractional pixel value we can use... it's actually really easy. See here
Basically, we just floor the values, then center the grid so that we can make it look nice.
edit: wasn't very happy with this answer, so screwed with it some more. Added a function that found the closest multiple of window size and made it so that it would prefer that number. Makes it work in most screen sizes, and has a fallback to the percentage method if it doesn't perfectly work. See here. However, because it relies on a recursive (naive) algorithm to find the closest multiple, it's really easy to screw your browser performance. Limiting to only 5-10 pixels of search space helps. The gist of it:
function closestMultiple(width, size, n, limit) {
if(n > limit) {
return {m: width/size, s:size};
}
if((width % (size+n)) == 0) {
return {m: width / (size+n), s: size+n};
} else if((width % (size-n)) == 0) {
return {m: width / (size-n), s: size-n};
}
return closestMultiple(width, size, n+1, limit);
}
It's very naive and ignores things like "an odd width will never be divisible by an even number"... so there's a ton of room for improvement. Check out this discussion and this discussion for more on this.

JavaScript Plugin Help - ContentFlow

I recently downloaded a JavaScript plugin (found here: http://www.jacksasylum.eu/ContentFlow/download.php) and I'm trying to change it up a bit. So far, all the edits that I've made to the code have been done by reading the walkthroughs under the documentation tab on that site. I've searched and searched and I can only find one piece of code that I THINK could be what I need to change. I just don't have enough JavaScript knowledge to do it.
If you have downloaded that plugin before, or you read some of the documentations tab, here's the picture of what's going on.
The icons are spread out way too far. (Ignore the icons, I just used the Facebook and Twitter because they were easy.) I need them to be closer. (They are spreading out over a 960px wide div!)
I found this code that may be what I'm looking for. It's called the StepWidth. I have no clue if that's what I need or not. Can anyone fill me in?
calcStepWidth: function(diff) {
var vI = this.conf.visibleItems;
var items = this.items.length;
items = items == 0 ? 1 : items;
if (Math.abs(diff) > vI) {
if (diff > 0) {
var stepwidth = diff - vI;
} else {
var stepwidth = diff + vI;
}
} else if (vI >= this.items.length) {
var stepwidth = diff / items;
} else {
var stepwidth = diff * ( vI / items);
//var stepwidth = diff/absDiff * Math.max(diff * diff,Math.min(absDiff,0.3)) * ( vI / this.items.length);
//var stepwidth = this.flowSpeedFactor * diff / this.visibleItems;
//var stepwidth = this.flowSpeedFactor * diff * ( this.visibleItems / this.items.length)
//var stepwidth = this.flowSpeedFactor * diff / this._millisecondsPerStep * 2; // const. speed
}
return stepwidth;
This might be easier if you have the plugin, but any knowledge will help.
First, this is not a JQuery plugin. Your issue is both with size of your images and the width of your contentFlow container. The small icons spread out to fill the 960px container. You need to increase the size of your images and/or reduce the width of your the containing div for the contentflow. You can use px or %. As an example, on the contentflow site, you can see that the gallery on the right on the main page has a div width:70%;.
First Install ContentFlowAddOn_DEFAULT.js, then search on " calcCoordinates: function (item) " you will find:
calcCoordinates: function (item) {
var rP = item.relativePosition;
//var rPN = item.relativePositionNormed;
var vI = this.conf.visibleItems;
var f = 1 - 1/Math.exp( Math.abs(rP)*0.75);
var x = item.side * vI/(vI+1)* f;
var y = 1;
return {x: x, y: y};
},
try to minimize the value 0.75 (i.e 0.3) or less till you reach the proper distance you are looking for

Categories