Flexbox items internal size ratio - javascript

I'm beginning my self-studying on web development. Currently working out on JS and trying to create a little "etch-a-sketch" app. I wrote a code which creates canvas with predefined "pixel" size. It work's fine to me. However, in order to make script file as narrow as possible I'm wondering if I can set size properties of pixels entirely by Flexbox properties in CSS. Mathematically speaking I'd like every new element inside parent container to have properties: Width = parentsWidth/nthChild, Height = Width (so every item could always be the biggest possible square regardless of items number).
let initialValue = 100;
const radioValues = document.querySelectorAll('input[name=size]');
let pixelSize = initialValue;
// event listener for chosing size value and setting new canvas
for (let i = 0; i < 3; i++) {
radioValues[i].addEventListener('click', function(e){
initialValue = Number((e.target.value));
pixelSize = initialValue;
//erasing existing pixels
if (document.querySelectorAll('.pixel').length > 0) {
const existingPixel = document.querySelectorAll('.pixel')
for(let i = 0; i < existingPixel.length;i++){
existingPixel[i].remove();
}
}
// building new pixels
for (let i = 0; i < pixelSize; i++){
let container = document.querySelector('.container');
let pixel = document.createElement('div');
pixel.setAttribute('class', 'pixel');
// part to get rid of
if (pixelSize == 400) {
pixel.style.height = "25px";
pixel.style.width = "25px";
}
if (pixelSize == 1600) {
pixel.style.height = "12.5px";
pixel.style.width = "12.5px";
}
container.appendChild(pixel);
}
})
}
.container {
width: 500px;
height: 500px;
margin-left: auto;
margin-right: auto;
border: 1px solid grey;
margin-top: 10%;
display: flex;
flex-wrap: wrap;
padding: 0;
}
.pixel {
height: 50px;
width: 50px;
border:1px solid grey;
margin: 0;
box-sizing: border-box;
}
<form>
<input class="size_selection" type="radio" name="size" value="100" checked> <label for="size_selection">10x10</label>
<input class="size_selection" type="radio" name="size" value="400"> <label for="size_selection">20x20</label>
<input class="size_selection" type="radio" name="size" value="1600"> <label for="size_selection">40x40</label>
</form>
<div class="container"></div>
<button id="clear">Clear</button>

I just refactored your script to use css grid and the corresponding grid-template-rows and grid-template-columns properties to match your grid.
Using grid in this instance makes it much more easy to get the result you want.
Just look around, I made some changes not only to CSS but also to HTML and JS. Hope it helps
let initialValue = 100;
const radioValues = document.querySelectorAll('input[name=size]');
let pixelSize = initialValue;
let container = document.getElementById('container');
// event listener for chosing size value and setting new canvas
for (let i = 0; i < 3; i++) {
radioValues[i].addEventListener('click', function(e) {
initialValue = Number((e.target.value));
pixelSize = initialValue;
//erasing existing pixels
if (document.querySelectorAll('.pixel').length > 0) {
const existingPixel = document.querySelectorAll('.pixel')
for (let i = 0; i < existingPixel.length; i++) {
existingPixel[i].remove();
}
}
// building new pixels
// setting the correct number of columns and rows in the grid
container.style.gridTemplateColumns = 'repeat(' + pixelSize + ', 1fr)';
container.style.gridTemplateRows = 'repeat(' + pixelSize + ', 1fr)';
for (let i = 0; i < pixelSize * pixelSize; i++) {
let pixel = document.createElement('div');
pixel.classList.add('pixel');
container.appendChild(pixel);
}
})
}
radioValues[0].click();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
form {
text-align: center;
margin: 2rem auto;
}
#container {
width: 300px;
height: 300px;
margin: 2rem auto;
border: 1px solid grey;
display: grid;
}
.pixel {
border: 1px solid gray;
}
<body>
<form>
<input class="size_selection" type="radio" name="size" value="10" checked> <label for="size_selection">10x10</label>
<input class="size_selection" type="radio" name="size" value="20"> <label for="size_selection">20x20</label>
<input class="size_selection" type="radio" name="size" value="40"> <label for="size_selection">40x40</label>
</form>
<div id="container">
</div>
<button id="clear">Clear</button>
</body>

Related

Needing help forcing the div to be stuck right under this other div in CSS

So this is day 2 of making an HTML game. I am honestly convinced I'm making a lot of progress and I am, except, yet again, I run into another styling problem.
So there is a grid in the game that updates every time the game is loaded. Basically, the grid's length and width to the word with the most letters, as shown below:
As you can see, the word everyday is 8 letters long, so the game puts 8 spaces available.
Now here's two problems with this in general:
I want the word bank to be directly UNDER the grid, no matter the length of the grids.
I want the grids to have NO space under them, so you see the little space between every new row? Basically that needs to go poof, and not be there.
What have you tried so far?
Placing the word bank div under the game area div didn't work, so I started to look up some solutions on Google, and it told me to try to add position: absolute; and position: relative; to div 1 and 2, but that just created a mess when it came to the word bank (spacing it out WAY too much) and did nothing to the grids. Also, display: block; can't help, because the code is already using flex for a different reason.
I also tried using margin-bottom for the grid space problem, but did nothing.
Code:
// definitely didn't get the grid part from Stack Overflow
var score = 0;
var scoreDisplay = document.getElementById("score");
scoreDisplay.innerHTML = "<p>Score: " + score;
var wordBank = document.getElementById("wordBank")
var gameArea = document.getElementById("gameArea")
let rows = document.getElementsByClassName("gridRow");
let cells = document.getElementsByClassName("cell");
// sparing you word array, nobody wants to read that list to the very last bits
var selectedWords = []
for (let i = 0; i < 5; i++) {
const selectedWord = words[Math.floor(Math.random() * words.length)]
if (selectedWord.length <= 9) {
wordBank.innerHTML += "<span>" + selectedWord + "</span>"
selectedWords.push(selectedWord)
}
}
var longestWord = selectedWords.reduce((a, b) => a.length < b.length ? b : a, "")
var charCount = longestWord.length
function makeRows(rowNum) {
for (r = 0; r < rowNum; r++) {
let row = document.createElement("div");
gameArea.appendChild(row).className = "gridRow";
};
};
function makeColumns(cellNum) {
for (i = 0; i < rows.length; i++) {
for (j = 0; j < cellNum; j++) {
let newCell = document.createElement("div");
rows[j].appendChild(newCell).className = "cell";
};
};
};
function defaultGrid() {
makeRows(charCount);
makeColumns(charCount);
}
defaultGrid();
body {
margin: 0px;
}
.content {
width: 512px;
height: 512px;
margin-left: auto;
margin-right: auto;
font-family: Arial;
}
.score {
font-size: 24px;
text-align: right;
}
.wordBank {
border: 2.5px solid black;
border-radius: 5px;
font-size: 24px;
display: flex;
width: 100%;
justify-content: space-between;
height: 13%;
}
.wordBank> :nth-of-type(even) {
align-self: flex-end;
}
.gameArea {
width: 100%;
height: 70%;
}
.cell {
border: 1px solid black;
width: 50px;
height: 50px;
display: inline-block;
}
<div class="content" id="content">
<div class="gameArea" id="gameArea">
</div>
<div class="wordBank" id="wordBank">
</div>
<div class="score" id="score">
</div>
</div>
How can I fix this issue? Any help is appreciated!
(Example for David):
One approach is below, with explanatory comments in the code:
// replaced all uses of 'var' with either let (if I anticipated the value would change), or const
// (if the value was likely to be unchanging):
let score = 0;
const scoreDisplay = document.getElementById("score");
const wordBank = document.getElementById("wordBank")
const gameArea = document.getElementById("gameArea")
const rows = document.getElementsByClassName("gridRow");
const cells = document.getElementsByClassName("cell");
// created an Array of words (though ideally a minimal, demonstrative Array would have been in the
// posted MCVE demo code); obviously: replace with your own Array:
const words = ['hello', 'thrifty', 'gaol', 'maester', 'mandible', 'osteoarthritic', 'venerable', 'the', 'cursive'];
let selectedWords = []
// moved this line out of the variable assignments/initialisation, in order that it's easier to
// maintain the code, because related things/actions are in the same/similar place(s):
scoreDisplay.innerHTML = "<p>Score: " + score;
// the rest of the JavaScript I left alone, with the exception of adding a 'let' declaration in the
// for loops after this first one:
for (let i = 0; i < 5; i++) {
const selectedWord = words[Math.floor(Math.random() * words.length)]
if (selectedWord.length <= 9) {
wordBank.innerHTML += "<span>" + selectedWord + "</span>"
selectedWords.push(selectedWord)
}
}
let longestWord = selectedWords.reduce((a, b) => a.length < b.length ? b : a, "")
let charCount = longestWord.length
function makeRows(rowNum) {
for (let r = 0; r < rowNum; r++) {
let row = document.createElement("div");
gameArea.appendChild(row).className = "gridRow";
}
}
function makeColumns(cellNum) {
for (let i = 0; i < rows.length; i++) {
for (let j = 0; j < cellNum; j++) {
let newCell = document.createElement("div");
rows[j].appendChild(newCell).className = "cell";
}
}
}
function defaultGrid() {
makeRows(charCount);
makeColumns(charCount);
}
defaultGrid();
/* added a simple, minimal CSS reset to normalise all element defaults
to a similar layout-sizing method, and font-family: */
*,
::before,
::after {
box-sizing: border-box;
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
/* added this, to help lay out the various elements more clearly: */
.content {
display: grid;
/* defining three rows, each of which is sized to the maximum size
needed to clearly display the content within: */
grid-template-rows: repeat(3, max-content);
/* setting a margin around the element on the block-axis, which is
perpendicular to the inline-axis, the inline-axis being the
direction of writing in the local language; so in left-to-right
languages this results in a top, and bottom, margin of 1em: */
margin-block: 1em;
/* setting a margin of auto on the inline-axis, the left and right
margins of the element in a left-to-right language: */
margin-inline: auto;
/* I retained the width, but removed the height constraint: */
width: 512px;
}
.score {
font-size: 24px;
text-align: right;
}
.wordBank {
border: 2.5px solid #000;
border-radius: 5px;
display: flex;
/* I left this part more ore less alone, other than adjusting
the font-size to an 'em' based sizing for responsive purposes: */
font-size: 1.6em;
/* added a minimum height, in order to allow room for the words
to move to the end within the space: */
min-height: 3em;
justify-content: space-between;
padding: 0.25em;
}
.wordBank span:nth-child(even) {
align-self: end;
}
.gameArea {
/* removing the spaces below/between each .gridRow element, which are caused by
the newline and whitespace characters between the .gridRow elements: */
font-size: 0;
/* placing the game area 'board' horizontally centered in the layout */
justify-self: center;
max-width: 100%;
}
.cell {
border: 1px solid black;
width: 50px;
/* resetting the font-size, so that text is visible once more (despite the parent
having a font-size of 0): */
font-size: 1rem;
height: 50px;
display: inline-block;
}
<div class="content" id="content">
<div class="gameArea" id="gameArea">
</div>
<div class="wordBank" id="wordBank">
</div>
<div class="score" id="score">
</div>
</div>
JS Fiddle demo.
Further to the question in the comments (below):
[...]one problem, why do only four words appear [in] certain instances?
This is a result of your loop, and its check:
// here, i is initialised to 0 (first iteration),
// the assessment is then executed; if it evaluates
// to true the loop runs an iteration, otherwise
// if the assessment returns false the loop stops;
// after the assessment i is incremented:
for (let i = 0; i < 5; i++) {
// selecting a random word:
const selectedWord = words[Math.floor(Math.random() * words.length)]
// testing the length of that random word:
if (selectedWord.length <= 9) {
// if the 'if' statement evaluates to true:
wordBank.innerHTML += "<span>" + selectedWord + "</span>"
// adding the selectedWord to the selectedWords Array
selectedWords.push(selectedWord)
// if the 'if' statement evaluates to false nothing
// happens, the loop runs another iteration; this
// 'consumes' a loop but no word was added hence
// a smaller selectedWords Array
}
}
To guard against this, you could modify your loop:
let score = 0;
const scoreDisplay = document.getElementById("score");
const wordBank = document.getElementById("wordBank")
const gameArea = document.getElementById("gameArea")
const rows = document.getElementsByClassName("gridRow");
const cells = document.getElementsByClassName("cell");
const words = ['hello', 'thrifty', 'gaol', 'maester', 'mandible', 'osteoarthritic', 'venerable', 'the', 'cursive'];
let selectedWords = [];
scoreDisplay.innerHTML = "<p>Score: " + score;
// using a while() loop, and testing the length of the selectedWords Array, so that
// while the condition is true (and the Array-length is less than 5) the loop will
// continue running:
while (selectedWords.length < 5) {
// select random word:
const selectedWord = words[Math.floor(Math.random() * words.length)];
// test the length of that word is less than 9 characters:
if (selectedWord.length <= 9) {
// adding content to the wordBank element:
wordBank.innerHTML += "<span>" + selectedWord + "</span>"
// pushing the word to the Array:
selectedWords.push(selectedWord);
// if no word is added to the Array, the length of the Array doesn't change
// and so the while loop will run again.
}
}
let longestWord = selectedWords.reduce((a, b) => a.length < b.length ? b : a, "")
let charCount = longestWord.length
function makeRows(rowNum) {
for (let r = 0; r < rowNum; r++) {
let row = document.createElement("div");
gameArea.appendChild(row).className = "gridRow";
}
}
function makeColumns(cellNum) {
for (let i = 0; i < rows.length; i++) {
for (let j = 0; j < cellNum; j++) {
let newCell = document.createElement("div");
rows[j].appendChild(newCell).className = "cell";
}
}
}
function defaultGrid() {
makeRows(charCount);
makeColumns(charCount);
}
defaultGrid();
*,
::before,
::after {
box-sizing: border-box;
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.content {
display: grid;
grid-template-rows: repeat(3, max-content);
margin-block: 1em;
margin-inline: auto;
width: 512px;
}
.score {
font-size: 24px;
text-align: right;
}
.wordBank {
border: 2.5px solid #000;
border-radius: 5px;
display: flex;
font-size: 1.6em;
min-height: 3em;
justify-content: space-between;
padding: 0.25em;
}
.wordBank span:nth-child(even) {
align-self: end;
}
.gameArea {
font-size: 0;
justify-self: center;
max-width: 100%;
}
.cell {
border: 1px solid black;
width: 50px;
font-size: 1rem;
height: 50px;
display: inline-block;
}
<div class="content" id="content">
<div class="gameArea" id="gameArea">
</div>
<div class="wordBank" id="wordBank">
</div>
<div class="score" id="score">
</div>
</div>
JS Fiddle demo.
Note that there is an infinitesimally small chance that this may lead to an infinite loop – though to do so would require that every iteration of the while loop selects a random word longer than 9 characters in length – so it may be worth modifying further, to filter the Array and first remove all words with more than 9 characters:
// you didn't include your own Array, so I'm not sure how
// it's assigned; but you should be able to use
// Array.prototype.filter():
const words = ['hello', 'thrifty', 'gaol', 'maester', 'mandible', 'osteoarthritic', 'venerable', 'the', 'cursive']
// here we use an Arrow function to filter the words
// of the words Array:
.filter(
// passing in a reference to the current Array-element
// ('word') of the Array over which we're iterating;
// here we're testing that the length of the current
// word is less than 9; if so this assessment returns
// Boolean true, and the word is retained in the Array,
// otherwise it returns false and the word is discarded:
(word) => word.length < 9
);
// ...code omitted for brevity...
// again, using a while loop, to ensure that we
// have five Array-elements in the selectedWords
// Array:
while (selectedWords.length < 5) {
// no 'if' to check the length, as it's now
// unnecessary to do so:
const selectedWord = words[Math.floor(Math.random() * words.length)];
wordBank.innerHTML += "<span>" + selectedWord + "</span>"
selectedWords.push(selectedWord);
}
let score = 0;
const scoreDisplay = document.getElementById("score");
const wordBank = document.getElementById("wordBank")
const gameArea = document.getElementById("gameArea")
const rows = document.getElementsByClassName("gridRow");
const cells = document.getElementsByClassName("cell");
const words = ['hello', 'thrifty', 'gaol', 'maester', 'mandible', 'osteoarthritic', 'venerable', 'the', 'cursive'];
let selectedWords = [];
scoreDisplay.innerHTML = "<p>Score: " + score;
// using a while() loop, and testing the length of the selectedWords Array, so that
// while the condition is true (and the Array-length is less than 5) the loop will
// continue running:
while (selectedWords.length < 5) {
// select random word:
const selectedWord = words[Math.floor(Math.random() * words.length)];
// test the length of that word is less than 9 characters:
if (selectedWord.length <= 9) {
// adding content to the wordBank element:
wordBank.innerHTML += "<span>" + selectedWord + "</span>"
// pushing the word to the Array:
selectedWords.push(selectedWord);
// if no word is added to the Array, the length of the Array doesn't change
// and so the while loop will run again.
}
}
let longestWord = selectedWords.reduce((a, b) => a.length < b.length ? b : a, "")
let charCount = longestWord.length
function makeRows(rowNum) {
for (let r = 0; r < rowNum; r++) {
let row = document.createElement("div");
gameArea.appendChild(row).className = "gridRow";
}
}
function makeColumns(cellNum) {
for (let i = 0; i < rows.length; i++) {
for (let j = 0; j < cellNum; j++) {
let newCell = document.createElement("div");
rows[j].appendChild(newCell).className = "cell";
}
}
}
function defaultGrid() {
makeRows(charCount);
makeColumns(charCount);
}
defaultGrid();
*,
::before,
::after {
box-sizing: border-box;
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.content {
display: grid;
grid-template-rows: repeat(3, max-content);
margin-block: 1em;
margin-inline: auto;
width: 512px;
}
.score {
font-size: 24px;
text-align: right;
}
.wordBank {
border: 2.5px solid #000;
border-radius: 5px;
display: flex;
font-size: 1.6em;
min-height: 3em;
justify-content: space-between;
padding: 0.25em;
}
.wordBank span:nth-child(even) {
align-self: end;
}
.gameArea {
font-size: 0;
justify-self: center;
max-width: 100%;
}
.cell {
border: 1px solid black;
width: 50px;
font-size: 1rem;
height: 50px;
display: inline-block;
}
<div class="content" id="content">
<div class="gameArea" id="gameArea">
</div>
<div class="wordBank" id="wordBank">
</div>
<div class="score" id="score">
</div>
</div>
JS Fiddle demo.
You probably want to do something like this:
So, what I did was to place the .game-grid(the n by n grid) and .words-wrapper (the zig zag word cloud) in a .container. This .container is a flex that flows in a column. This shows the 2 items inside the .container one by one from top to bottom.
.game-grid itself is a grid. This lets you easily create a grid.
grid-template-colums: repeat(8, 1fr) tells the browser that this grid is going to have 8 columns (this you will have to control by the length of the longest word). I set the grid to have a fixed size and all the items inside have place-items: stretch which means they take all the available space, so they will all be equal size.
Hope this helps.
.container {
display: flex;
flex-direction: column;
width: 100%;
height: 100vh;
align-items: center;
}
.game-grid {
width: 50vh;
height: 50vh;
display: grid;
grid-template-columns: repeat(8, 1fr);
place-items: stretch;
place-content: stretch;
}
.game-grid-item {
border-width: 1px;
border-style: solid;
border-color: chocolate;
width: 100%;
height: 100%;
display: grid;
place-items: center;
}
.words-wrapper {
display: flex;
width: 100%;
justify-content: space-between;
height: 10vh;
border-width: 1px;
border-style: solid;
border-color: blueviolet;
}
.even {
align-self: flex-end;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div class="container">
<div class="game-grid">
<div class="game-grid-item">1</div>
<div class="game-grid-item">2</div>
<div class="game-grid-item">3</div>
<div class="game-grid-item">4</div>
<div class="game-grid-item">5</div>
<div class="game-grid-item">6</div>
<div class="game-grid-item">7</div>
<div class="game-grid-item">8</div>
<div class="game-grid-item">9</div>
<div class="game-grid-item">10</div>
<div class="game-grid-item">11</div>
<div class="game-grid-item">12</div>
<div class="game-grid-item">13</div>
<div class="game-grid-item">14</div>
<div class="game-grid-item">15</div>
<div class="game-grid-item">16</div>
<div class="game-grid-item">1</div>
<div class="game-grid-item">2</div>
<div class="game-grid-item">3</div>
<div class="game-grid-item">4</div>
<div class="game-grid-item">5</div>
<div class="game-grid-item">6</div>
<div class="game-grid-item">7</div>
<div class="game-grid-item">8</div>
<div class="game-grid-item">9</div>
<div class="game-grid-item">10</div>
<div class="game-grid-item">11</div>
<div class="game-grid-item">12</div>
<div class="game-grid-item">13</div>
<div class="game-grid-item">14</div>
<div class="game-grid-item">15</div>
<div class="game-grid-item">16</div>
<div class="game-grid-item">1</div>
<div class="game-grid-item">2</div>
<div class="game-grid-item">3</div>
<div class="game-grid-item">4</div>
<div class="game-grid-item">5</div>
<div class="game-grid-item">6</div>
<div class="game-grid-item">7</div>
<div class="game-grid-item">8</div>
<div class="game-grid-item">9</div>
<div class="game-grid-item">10</div>
<div class="game-grid-item">11</div>
<div class="game-grid-item">12</div>
<div class="game-grid-item">13</div>
<div class="game-grid-item">14</div>
<div class="game-grid-item">15</div>
<div class="game-grid-item">16</div>
<div class="game-grid-item">1</div>
<div class="game-grid-item">2</div>
<div class="game-grid-item">3</div>
<div class="game-grid-item">4</div>
<div class="game-grid-item">5</div>
<div class="game-grid-item">6</div>
<div class="game-grid-item">7</div>
<div class="game-grid-item">8</div>
<div class="game-grid-item">9</div>
<div class="game-grid-item">10</div>
<div class="game-grid-item">11</div>
<div class="game-grid-item">12</div>
<div class="game-grid-item">13</div>
<div class="game-grid-item">14</div>
<div class="game-grid-item">15</div>
<div class="game-grid-item">16</div>
</div>
<div class="words-wrapper">
<span class="item">multiply</span>
<span class="item even">step</span>
<span class="item">kiss</span>
<span class="item even">force</span>
<span class="item">ago</span>
</div>
</div>
</body>
</html>

JavaScript functions not working together to display image

I am trying to get user input and then print that number of boxes on the screen, I can get the boxes spawning if I do no checks and just set them to spawn whenever I click one, However, once I start adding in checks the boxes just stop spawning.
var count = 1;
function spawnBox() {
var container = document.getElementById("container");
var newBox = document.createElement("div");
newBox.className = "box";
newBox.innerHTML = count;
container.appendChild(newBox);
count++
}
function checkIfCanSpawn() {
while (count < inputNumber) {
spawnBox();
}
}
div.box {
width: 10vw;
height: 8vw;
background: rgb(8, 144, 168);
margin: 1vw;
float: left;
text-align: center;
font-size: 5vw;
padding-top: 1vw;
font-family: sans-serif;
}
<label id="type in a number" name="Input a number"> Type In a number </label>
<input id="inputNum" type="number" name="inputNumber"> </input>
Tweaked a little to make it work.
Added onchange event on input class.
then instead of using the count, i made use of the available while loop and passed the count instead.
then clear the "container" every run.
function spawnBox(count) {
// Get the container
var container = document.getElementById("container");
// Create a new div
var newBox = document.createElement("div");
newBox.className = "box";
newBox.innerHTML = count;
// Append it to the container
container.appendChild(newBox);
// Increment count
count++
}
function checkIfCanSpawn() {
document.getElementById("container").innerHTML = "";
var inputNumber = document.getElementById("inputNum").value;
var x = 1;
while (x <= inputNumber) {
spawnBox(x);
x++;
}
}
div.box {
width: 10vw;
height: 8vw;
background: rgb(8, 144, 168);
margin: 1vw;
float: left;
text-align: center;
font-size: 5vw;
padding-top: 1vw;
font-family: sans-serif;
}
<label id="type in a number" name="Input a number"> Type In a number </label>
<input id="inputNum" type="number" name="inputNumber" onchange="checkIfCanSpawn()" />
<div id="container"></div>
https://jsfiddle.net/Lqj4dktw/
Add a button with a click-eventHandler to call the spawnBox():
$(document).ready(function() {
$("#btn" ).click(function(){
let inputNumber = $("#inputNum").val();
while (count < inputNumber) {
spawnBox();
}
})
})
Her is a working fiddle

How can I make HTML table footer take available space on each page when printing on multiple pages?

I like to print a very long table on multiple pages. On each page I have a fixed header, multiple entries in body section with random heights, and a footer. Random entries should not break across pages and the footer should use the available remaining space. I created a sample code with a print button. When I click on the print button, I want the orange box to start right after its preceding red box and take all the space to the bottom of the page. Any ideas how to fix the issue?
const main = document.querySelectorAll(".table-body")[0];
const newDiv = document.createElement("div");
for (let i = 0; i < 30; i++) {
newDiv.innerHTML += `<div class="box" style="height: ${getRandomIntInclusive(120,250)}px">Line ${i+1} </div>`
}
main.appendChild(newDiv);
document.getElementById("print").addEventListener("click", () => {
window.print();
});
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
table {
width: 100%;
border: 1px solid blue;
padding: 10px;
}
.table-body {
border: 1px dashed green;
padding: 10px;
}
.box {
border: 1px solid red;
page-break-inside: avoid;
}
table > tfoot > tr > td {
border: 2px solid orange;
}
.table-footer {
border: 1px solid black;
min-height: 100px;
vertical-align: top;
}
#media print {
#page {
size: letter;
}
#print {
display: none;
}
}
<button id='print'>Print</button>
<table>
<thead>
<tr>
<th>
Header
</th>
<tr>
</thead>
<tbody>
<tr>
<td>
<div class='table-body'></div>
</td>
<tr>
</tbody>
<tfoot>
<tr>
<td>
<div class='table-footer'>Footer</div>
</td>
<tr>
</tfoot>
</table>
I was not able to solve the issue using a table so I ended up solving my problem using divs. I kept track of the random height of divs on each page and assigned the remaining space to the footer. My footers have some min-height limitation to ensure that I have a footer on each page. The content of each page is placed in another div to give me more control on styling each page; e.g: page-break-after and overflow. The solution is not ideal and hoping that someone comes with a better approach but it satisfies my need for the moment. Below you can find the updated code:
const main = document.getElementById("main");
const newDiv = document.createElement("div");
const paperHeight = 11 // paper height in inch
const paperMargin = 1; // sum of top and bottom paper margins in inch
const overflow = 0.11; // used to protect page overflow to the next page
const dpi = 96; // Display dots per inch (DPI)
const maxHeight = (paperHeight - paperMargin) * dpi; // max page height
const footerMinHeight = 40; // Min height of footer
const headerHeight = 100; // Height of header in px
const numOfItems = 20; // Number of items
let j = 0;
let items = '';
let pageCount = 1;
do {
items += `<div class="page" style="max-height: ${paperHeight - paperMargin - overflow}in"><div class="box header" style="height: ${headerHeight}px">Page ${pageCount} - Header</div>`;
let pageHeight = headerHeight;
do {
const elementHeight = getRandomIntInclusive(60, 250);
if (elementHeight + pageHeight > maxHeight - footerMinHeight || j === numOfItems) {
items += `<div class="box footer" style="height: ${maxHeight - pageHeight}px">Footer</div></div>`;
break;
} else {
pageHeight += elementHeight;
j++;
}
items += `<div class="box" style="height: ${elementHeight}px">Item ${j} </div>`;
} while (j <= numOfItems)
pageCount++;
} while (j < numOfItems)
newDiv.innerHTML = items;
main.appendChild(newDiv);
document.getElementById("print").addEventListener("click", () => {
window.print();
});
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
* {
box-sizing: border-box;
}
.page {
border: 1px dashed green;
page-break-after: always;
overflow: hidden;
}
.box {
border-bottom: 1px solid red;
}
.header {
background: #e6ffe6
}
.footer {
background: #fdfed6;
}
#media print {
#page {
size: letter;
margin: 0.5in;
}
#print {
display: none;
}
}
<button id='print'>Print</button>
<div id='main'></div>

How to add div's inside a div dynamically until the width and height of parent div is full/completed

Background: I am making a web-page where User will enter the tile Width & Height and Floor Width & Height.
The Floor Width & Height is used to calculate the area of Floor.
The Tile input is in INCHES & the Floor Input is in FEET.
Technical Info: I have set 1 foot equals to 60 pixels & 1 inch equals to 5 pixels for calculations.
Where Am I Now ?
Right now I am stuck in drawing all the tiles (child div's) in the area (parent div). For now I am using simple for loop for making the tiles (div's).
For Now Output is Something Like this...
What I Want ? Well I am trying to make a functionality that when user clicks the Calculate Button, he/she see's the design of the floor. I will be coloring & adding patterns later on.
The output should be like this (Beg me your pardon, if the borders are not align, this was made with Windows Paint) :
Code:
$(document).ready(function () {
$("#btnCalculate").click(function (e) {
e.preventDefault();
$("#area").empty();
const foot = 60, inch = 5;
let tileW = parseFloat($("#tileWidth").val());
let tileH = parseFloat($("#tileHeight").val());
let areaW = parseFloat($("#areaWidth").val());
let areaH = parseFloat($("#areaHeight").val());
$("#area").css("height", (foot * areaH));
$("#area").css("width", (foot * areaW));
for (let r = 0; r<10 ; r++) {
// const element = array[r];
$("#area").append("<div id='tile_"+r+"' style='width:"+((inch * tileW))+"px; height:"+((inch * tileH))+"px;' class='border_color'> </div>");
}
});
});
#area {
border: 1px solid black;
height: 25px;
width: 25px;
}
.border_color{
/* border: 1px solid black; */
outline: 1px solid; /* use instead of border */
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Tile Width (inches): </p><input type="numbers" id ="tileWidth" placeholder="Tile Width" value="6">
<p>Tile Height (inches): </p><input type="numbers" id ="tileHeight" placeholder="Tile Height" value="4">
<br>
<p>Area Width (foot): </p><input type="numbers" id ="areaWidth" placeholder="Area Width" value="11.5">
<p>Area Height (foot): </p><input type="numbers" id ="areaHeight" placeholder="Area Height" value="6.5">
<button id="btnCalculate" >Calculate</button>
<div id="area">
</div>
External Link of Fiddle: https://jsfiddle.net/22gLkguL/
I Have tried, to archive all of this but failed..!
Would someone please help me OR guide me in right path...?
Use display: flex and flex-wrap: wrap
#area {
border: 1px solid black;
height: 25px;
width: 25px;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
and calculate the numbers of the divs that each side (width or height) can be filled the most.
$(document).ready(function() {
$("#btnCalculate").click(function(e) {
e.preventDefault();
$("#area").empty();
const foot = 60,
inch = 5;
let tileW = parseFloat($("#tileWidth").val());
let tileH = parseFloat($("#tileHeight").val());
let areaW = parseFloat($("#areaWidth").val());
let areaH = parseFloat($("#areaHeight").val());
var areaHeight = (foot * areaH)
var areaWidth = (foot * areaW)
var divHeight = (inch * tileH)
var divWidth = (inch * tileW)
$("#area").css("height", areaHeight);
$("#area").css("width", areaWidth);
var nums = Math.floor(areaWidth/divWidth) * Math.floor(areaHeight/divHeight)
for (let r = 0; r < nums; r++) {
var $div = $('<div>', {
id: 'tile_' + r,
class: 'border_color',
height: divHeight,
width: divWidth,
})
$("#area").append($div);
}
});
});
#area {
border: 1px solid black;
height: 25px;
width: 25px;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.border_color {
outline: 1px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Tile Width (inches): </p><input type="numbers" id="tileWidth" placeholder="Tile Width" value="6">
<p>Tile Height (inches): </p><input type="numbers" id="tileHeight" placeholder="Tile Height" value="4">
<br>
<p>Area Width (foot): </p><input type="numbers" id="areaWidth" placeholder="Area Width" value="11.5">
<p>Area Height (foot): </p><input type="numbers" id="areaHeight" placeholder="Area Height" value="6.5">
<button id="btnCalculate">Calculate</button>
<div id="area">
</div>
use display: flex; flex-wrap: wrap for area element.
and calculate the number of Tiles as ---
(areaWidthInPixels/tileWidthinPixels) *
(areaHeightInPixels/tileHeightinPixels)
$(document).ready(function () {
$("#btnCalculate").click(function (e) {
e.preventDefault();
$("#area").empty();
const foot = 60, inch = 5;
let tileW = parseFloat($("#tileWidth").val());
let tileH = parseFloat($("#tileHeight").val());
let areaW = parseFloat($("#areaWidth").val());
let areaH = parseFloat($("#areaHeight").val());
$("#area").css("height", (foot * areaH));
$("#area").css("width", (foot * areaW));
let noOfTiles = Math.floor( ((foot * areaW)/(inch * tileW)) )* Math.floor( ((foot * areaH)/(inch * tileH)) );
alert("noOf TIles :: " + noOfTiles);
for (let r = 1; r <= noOfTiles ; r++) {
// const element = array[r];
var bgColor = r % 2 == 0 ? "red" : "green";
$("#area").append("<div id='tile_"+r+"' style='width:"+((inch * tileW))+"px; height:"+((inch * tileH))+"px; background-color: " + bgColor + "'' class='border_color'> </div>");
}
});
});
#area {
border: 1px solid black;
height: 25px;
width: 25px;
display: flex;
flex-wrap: wrap;
}
.border_color{
/* border: 1px solid black; */
outline: 0px solid; /* use instead of border */
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Tile Width (inches): </p><input type="numbers" id ="tileWidth" placeholder="Tile Width" value="6">
<p>Tile Height (inches): </p><input type="numbers" id ="tileHeight" placeholder="Tile Height" value="4">
<br>
<p>Area Width (foot): </p><input type="numbers" id ="areaWidth" placeholder="Area Width" value="11.5">
<p>Area Height (foot): </p><input type="numbers" id ="areaHeight" placeholder="Area Height" value="6.5">
<button id="btnCalculate" >Calculate</button>
<div id="area">
</div>

Image series bar

I have for example image of Coin.
I want to give a value from 0 to example 20.
0: no coin
1: to one coin
2: 2 coin etc
20: 20 coin
The collection of 6 coins should looks like this:
How can I achieve this goal?
if you prefer pure JS:
var maxI = Math.floor(Math.random() * (21 - 1)) + 1;
var marginCoins = 0;
var coin = [];
for (var i = 0; i < maxI; i++) {
coin[i] = document.createElement('img');
coin[i].src = 'http://placehold.it/75x75';
coin[i].style.position = 'fixed';
coin[i].style.top = marginCoins+'px';
coin[i].style.height = '75px';
coin[i].style.border = '1px solid black';
document.body.appendChild(coin[i]);
marginCoins += 45;
}
$(".coins").each(function(){
var $self = $(this);
var applyCoins = function(){
$self.empty();
var coins = $self.data("coins");
for (var i=coins-1; i>=0; i--) {
var $coin = $("<div></div>").addClass("coin").css("top", (i*20)+"px");
$self.append($coin);
}
};
$self.on("loadCoins", function(){
applyCoins();
});
applyCoins();
});
$("#button").click(function(){
$(".coins").data("coins", $("#input").val()).trigger("loadCoins");
});
.coins {
width: 120px;
height: 120px;
position: relative;
overflow: visible;
}
.coins .coin {
width: 120px;
height: 120px;
position: absolute;
top: 0;
left: 0;
background-color: #FF0000;
border-bottom: 3px solid #333333;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p>
<input type="text" value="3" id="input" />
<input type="button" value="change" id="button" />
</p>
<div class="coins" data-coins="10"></div>

Categories