How do I stack buttons on top of each other [closed] - javascript

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
In a project that I'm currently working on, I need to stack several buttons on top of each other. The way that I want to stack them is so that there is no gap in between them. This is what I currently have:
This is what I want:
Is there a way to do this in CSS?
The code that I am using is a function which takes in a height and a width and makes a grid of buttons.
function createBoard (height, width)
{
for (let h = 0; h < height; h++)
{
for (let w = 0; w < width; w++)
{
let button = document.createElement('button');
button.setAttribute('class', 'pixel');
document.body.appendChild(button)
if (w == width - 1)
{
let br = document.createElement('br');
document.body.appendChild(br)
}
}
}
}
createBoard(5,10);
.pixel {
margin:0;
background-color: rgb(31, 31, 31);
padding: 10px;
display:inline-block;
border: none;
}
.pixel:hover {
background-color: rgb(73, 73, 73);
}

Here you go.
I have adjusted your js to create rows of buttons and then those rows are added to container. Both rows and container have display: flex.
You could ommit rows but it will be harder to create nice grid.
function createBoard (height, width)
{
for (let h = 0; h < height; h++) {
const row = document.createElement('div');
row.classList.add('row');
for (let w = 0; w < width; w++) {
const button = document.createElement('button');
button.classList.add('pixel');
row.append(button);
}
document.querySelector('.container').append(row);
}
}
createBoard(10, 10);
.container {
display: flex;
flex-direction: column;
}
.container .row {
display: flex;
}
.container .row .pixel {
margin: 0;
background-color: rgb(31, 31, 31);
padding: 10px;
display: inline-block;
border: none;
}
.container .row .pixel:hover {
background-color: rgb(73, 73, 73);
}
<div class="container"></div>
other way will be to use grid, but you will have to specify width of pixel and use it to create your container grid
function createBoard (height, width)
{
document.querySelector('.container').style.gridTemplateColumns = 'repeat('+width+', 20px)';
for (let h = 0; h < height; h++) {
for (let w = 0; w < width; w++) {
const button = document.createElement('button');
button.classList.add('pixel');
document.querySelector('.container').append(button);
}
}
}
createBoard(10, 10);
.container {
display: grid;
flex-direction: column;
}
.container .pixel {
margin:0;
background-color: rgb(31, 31, 31);
display:inline-block;
border: none;
width: 20px;
height: 20px;
}
.container .pixel:hover {
background-color: rgb(73, 73, 73);
}
<div class="container"></div>

You can use CSS grid for the job:
function createBoard(height, width) {
const board = document.getElementById('board');
while (board.lastChild) board.lastChild.remove();
board.style.gridTemplateColumns = `repeat(${width}, min-content)`;
for (let h = 0; h < height; h++) {
for (let w = 0; w < width; w++) {
let button = document.createElement('button');
button.setAttribute('class', 'pixel');
board.appendChild(button)
}
}
}
createBoard(16,9);
.pixel {
margin: 0;
background-color: rgb(31, 31, 31);
padding: 10px;
display: inline-block;
border: none;
}
.pixel:hover {
background-color: rgb(73, 73, 73);
}
#board {
display: grid;
}
<div id="board"></div>

Related

Optimized solution for filling entire page with DIVs

I want to have a webpage whose entire viewable area is filled with divs. I am currently using the following code:
var wh= window.innerHeight;
var ww= window.innerWidth;
var area= wh * ww;
i= 1;
while(area > 0) {
document.getElementById("map").innerHTML+= "<div class='map-box' id='box" + i + "'></div>";
area-= 20 * 20;
i+=1;
}
.map-box {width: 20px; height: 20px; border-color: grey; border-width: 1px; border-style: solid; display: inline-block; margin: 0; padding: 0;}
<body>
<div id='map'></div>
</body>
If you try to use this code is your browser, you will see that there are two flaws in this:
First, it creates too many extra divs which go outside the viewable screen.
Second, this code is also somewhat slow.
Can someone here help me address both of these flaws and also optimize this code for faster performance?
1.) That <div> is not 20x20, because of the border:
let d = document.getElementById("test");
console.log(d.offsetWidth, d.offsetHeight);
.map-box {
width: 20px;
height: 20px;
border-color: grey;
border-width: 1px;
border-style: solid;
display: inline-block;
margin: 0;
padding: 0;
}
<div id="test" class="map-box"></div>
2.) There's still the default border around the entire thing, and also some spacing between the lines:
var wh = window.innerHeight;
var ww = window.innerWidth;
var area = wh * ww;
i = 1;
while (area > 0) {
document.getElementById("map").innerHTML += "<div class='map-box' id='box" + i + "'></div>";
area -= 22 * 22; // hardcoding is not that nice
i += 1;
}
.map-box {
width: 20px;
height: 20px;
border-color: grey;
border-width: 1px;
border-style: solid;
display: inline-block;
margin: 0;
padding: 0;
}
#map {
background: blue;
}
body {
background: red;
}
<div id='map'></div>
3.) Half cells are evil, so the width/height should be rounded downwards to a multiple of 22. Suddenly the grid is becoming an actual rectangle, at least in Chrome/Edge. The between-spacing is still a problem:
var wh = Math.floor(window.innerHeight / 22) * 22; // <--!!
var ww = Math.floor(window.innerWidth / 22) * 22; // <--!!
var area = wh * ww;
i = 1;
while (area > 0) {
document.getElementById("map").innerHTML += "<div class='map-box' id='box" + i + "'></div>";
area -= 22 * 22;
i += 1;
}
.map-box {
width: 20px;
height: 20px;
border-color: grey;
border-width: 1px;
border-style: solid;
display: inline-block;
margin: 0;
padding: 0;
}
#map {
background: blue;
}
body {
background: red;
margin: 0; // <--!!
padding: 0; // <--!!
}
<div id='map'></div>
I don't actually know how to use line-height properly, this one works on my machine with my scaling/DPI, in Chrome/Edge, but that's all I can say about it. The 22-s are cut back, area now simply stores the number of <div>s to generate.
var wh = Math.floor(window.innerHeight / 22);
var ww = Math.floor(window.innerWidth / 22);
var area = wh * ww;
i = 1;
while (area > 0) {
document.getElementById("map").innerHTML += "<div class='map-box' id='box" + i + "'></div>";
area--;
i += 1;
}
.map-box {
width: 20px;
height: 20px;
border-color: grey;
border-width: 1px;
border-style: solid;
display: inline-block;
margin: 0;
padding: 0;
}
#map {
line-height: 0.6;
}
body {
margin: 0;
padding: 0;
}
<div id='map'></div>
Instead of accessing dom element's inner html on each loop iteration - do it once after the loop with "prepared" data to set there
const wh = window.innerHeight;
const ww = window.innerWidth;
let area = wh * ww;
i = 1;
const ms = Date.now();
const divs = [];
while (area > 0) {
divs.push("<div class='map-box' id='box" + i + "'></div>");
area -= 20 * 20;
i += 1;
}
document.getElementById("map").innerHTML = divs.join("");
console.log("done fast", Date.now() - ms);
js fiddle with comparison https://jsfiddle.net/aL7zqwy9/
The final solution, not ideal but
<html>
<body>
<div id='map'></div>
</body>
<style>
body {
margin: 0;
padding: 0;
/* Overflow appears when last row is added and shrinks the "width" */
overflow-y: hidden;
}
#map {
/* To exclude space between rows */
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.map-box {
width: 20px;
height: 20px;
border: 1px solid grey;
display: block;
margin: 0;
padding: 0;
/* So border thickness will not affect element size */
box-sizing: border-box;
}
</style>
<script>
const cellSize = 20; // px
const wh = window.innerHeight;
const ww = window.innerWidth;
// not always divisible by cell size without a remainder
const columnsCount = Math.floor(ww / cellSize);
const rowsCount = Math.floor(wh / cellSize);
const cellsCount = columnsCount * rowsCount;
console.log(`wh: ${wh}, ww: ${ww}, cols: ${columnsCount}, rows: ${rowsCount}`);
const divs = [];
for (let i = 0; i < cellsCount; i++) {
divs.push(`<div class='map-box' id='box${i}'></div>`);
}
document.getElementById("map").innerHTML = divs.join("");
</script>
</html>

Yet again, CSS being problematic again

So basically this is Day 3 (other days, I pretty much did nothing to complete the game) of making a game from HTML5. So I'm making a moves system right now, and I guess I'm doing well? (mainly because I'm not sure if I provided the user with too many moves...) But the thing about it is that, I'm kind of having ANOTHER styling issue.
As you can see in the image: I've CLEARLY set dimensions up for the headerDisplay class/id, but NO, it goes out of the div's dimensions and even goes on the grid. I'm also aiming for the time and moves text to be stuck right on top of the grid, similarly to how the word bank is stuck to the bottom of the grid.
I was also aiming for a button that says refresh right under the word bank, however no matter what I tried, the button would just be right the score text, which looks like this:
When I am aiming for this:
Code:
<div class="content" id="content">
<div class="headerDisplay" id="headerDisplay">
</div>
<div class="gameArea" id="gameArea">
</div>
<div class="wordBank" id="wordBank">
</div>
<div class="bottomMenu" id="bottomMenu">
</div>
</div>
::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;
}
.bottomMenu {
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;
}
.headerDisplay {
width: 100%;
height: 76.8px;
text-align: right;
font-size: 1.6em;
}
let score = 0;
const headerDisplay = document.getElementById("headerDisplay")
const bottomMenu = document.getElementById("bottomMenu");
const wordBank = document.getElementById("wordBank")
const gameArea = document.getElementById("gameArea")
const rows = document.getElementsByClassName("gridRow");
const cells = document.getElementsByClassName("cell");
const words = [ // snippet
"ability",
"able",
"about",
"above",
"abroad",
"absence",
"absent",
"absolute",
"accept",
"accident",
"accord",
"account",
"accuse",
"accustom",
"ache",
"across",
"act"
]
let selectedWords = [];
bottomMenu.innerHTML = "<p>Score: " + score;
bottomMenu.innerHTML += "<button>Refresh"
while (selectedWords.length < 5) {
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
var moves = charCount * 5
headerDisplay.innerHTML += "<p>Time: "
headerDisplay.innerHTML += "<p>Moves: " + moves
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();
To fix header you need to set its height to fit content, so it will be over your grid even if you change it later:
.headerDisplay {
width: 100%;
height: content-fit; /* previous: 76.8px */
text-align: right;
font-size: 1.6em;
}
And to fix bottom menu you need to add flexbox:
.bottomMenu {
font-size: 24px;
text-align: right;
display: flex; /* new */
flex-direction: row-reverse; /* new */
justify-content: space-between; /* new */
align-items: center; /* new */
}
For the button, you could try this:
button {
position: relative;
right: 400px;
bottom: 50px;
transform: scale(2,2)
}

Reveal html text letter by letter smoothly on page scroll?

I need help trying to complete a JavaScript effect. I'm looking to accomplish the effect on this site https://www.lucidmotors.com/ - in the third section down on the home page you can see the text scroll/reveal is smooth over the other text.
I found this option on codepen https://codepen.io/Bes7weB/pen/zYKoexK similar to the effect I need, but its a little to choppy I need it to be smoother.
JS
var textWrapper = document.querySelector(".ml3");
textWrapper.innerHTML = textWrapper.textContent.replace(
/\S/g,
"<span class='letter'>$&</span>"
);
var letter = document.querySelectorAll(".letter");
var i = 0;
var currentID = 0;
var slideCount = letter.length;
document.addEventListener("scroll", (e) => {
let scrolled =
document.documentElement.scrollTop /
(document.documentElement.scrollHeight -
document.documentElement.clientHeight);
// var nextID = currentID + 1;
// if (nextID < slideCount) {
// letter[nextID].style.setProperty(
// "--percentage",
// `${scrolled / 1}` * nextID
// );
// }
// currentID = nextID;
letter.forEach(function (l, i) {
// console.log("====",i / letter.length, i, letter.length)
if (i / letter.length < scrolled) {
l.style.setProperty("--percentage", 1);
} else {
l.style.setProperty("--percentage", 0);
}
});
});
CSS
:root {
--percentage: 0;
}
body {
background-color: #000;
margin: 0;
height: 600vh;
}
.ml3 {
position: sticky;
top: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
span {
font-family: Helvetica;
margin: 0;
padding: 0;
font-size: 48px;
color: #fff;
letter-spacing: -0.3px;
}
.ml3 span {
opacity: var(--percentage);
}
HTML
<div class="ml3">
<h1>THIS IS MY TEXT THAT IT'S GOING TO SHOW IN SCROLL</h1>
</div>
Any assistance would be great
This is probably what you're looking for, find it quite interesting and I think my answer can be improved, if you're interested only in the vertical scroll you should check the window.scrollY variable as well.
var textWrapper = document.querySelector(".ml3");
textWrapper.innerHTML = textWrapper.textContent.replace(
/\S/g,
"<span class='letter'>$&</span>"
);
var letter = document.querySelectorAll(".letter");
document.addEventListener("scroll", (e) => {
let scrolled =
document.documentElement.scrollTop /
(document.documentElement.scrollHeight -
document.documentElement.clientHeight) *
letter.length;
letter.forEach(function(l, i) {
if ((scrolled - i) > 1)
l.style.setProperty("--percentage", 1);
else if ((scrolled - i) < 0.2)
l.style.setProperty("--percentage", 0.2);
else
l.style.setProperty("--percentage", (scrolled - i));
});
});
:root {
--percentage: 0.2;
}
body {
background-color: #000;
margin: 0;
height: 600vh;
}
.ml3 {
position: sticky;
top: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
span {
font-family: Helvetica;
margin: 0;
padding: 0;
font-size: 48px;
color: #fff;
letter-spacing: -0.3px;
}
.ml3 span {
opacity: var(--percentage);
}
<div class="ml3">
<h1>THIS IS MY TEXT THAT IT'S GOING TO SHOW IN SCROLL</h1>
</div>

How to make each box in grid to respond to on('click')?

I'm building turn-based game in javascript and I have css grid and it is looped over to create the grid. I need to make each box clickable but when I try it it returns an error saying grid-item is null.
I tried to loop over the grid and added a click listener and I got an error every time. I managed only to make whole grid field to respond to click event and that's all.
<div id="game-container" class="game-container">
<div id="grid-container" class="grid-container"></div>
</div>
.grid-container {
display: grid;
max-width: 620px;
max-height: 620px;
grid-template: repeat(10, 1fr)/repeat(10, 1fr);
margin: 50px auto;
background-color: #16eeca;
}
.grid {
border: 1px solid #000;
width: 60px;
height: 60px;
}
let gridContainer = $('#grid-container');
let gridAccess = document.getElementsByClassName('grid');
let gridTile = document.getElementById('grid-tile-0');
// Function that draws the map
function drawGrid() {
for (let x = 0; x < 1; x++) {
for (let y = 0; y < 1; y++) {
for (let i = 0; gridAccess.length < 100; i++) {
gridContainer.append('<div id="grid-tile-' + i + '"' + ' class="grid"' + '>Hey</div>');
}
}
}
}
drawGrid();
I can't find why I got gridTile as a null.
I've moved your drawGrid() up a bit:
let gridAccess = document.getElementsByClassName('grid');
drawGrid();
let gridTile = document.getElementById('grid-tile-0');
Demo
When you run let gridTile = document.getElementById('grid-tile-0'); in your current code, the element grid-tile-0 has not been created.
let gridContainer = $('#grid-container');
let gridAccess = document.getElementsByClassName('grid');
drawGrid();
let gridTile = document.getElementById('grid-tile-0');
console.log(gridTile)
// Function that draws the map
function drawGrid() {
for (let x = 0; x < 1; x++) {
for (let y = 0; y < 1; y++) {
for (let i = 0; gridAccess.length < 100; i++) {
gridContainer.append('<div id="grid-tile-' + i + '"' + ' class="grid"' + '>Hey</div>');
}
}
}
}
.grid-container {
display: grid;
max-width: 620px;
max-height: 620px;
grid-template: repeat(10, 1fr)/repeat(10, 1fr);
margin: 50px auto;
background-color: #16eeca;
}
.grid {
border: 1px solid #000;
width: 60px;
height: 60px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="game-container" class="game-container">
<div id="grid-container" class="grid-container"></div>
</div>
The gridTile is null because you try to find it before create it. To get clickable all boxes change gridContainer.append to
gridContainer.append(`
<div onclick="clickBox(${x},${y},${i},this)" id="grid-tile-${i}" class="grid" >
Hey
</div>
`);
let gridContainer = $('#grid-container');
let gridAccess = document.getElementsByClassName('grid');
let gridTile = document.getElementById('grid-tile-0');
// Function that draws the map
function drawGrid() {
for (let x = 0; x < 1; x++) {
for (let y = 0; y < 1; y++) {
for (let i = 0; gridAccess.length < 100; i++) {
gridContainer.append(`<div onclick="clickBox(${x},${y},${i},this)" id="grid-tile-${i}" class="grid" >Hey</div>`);
}
}
}
}
drawGrid();
function clickBox(x,y,i,btn) {
console.log(i)
}
.grid-container {
display: grid;
max-width: 620px;
max-height: 620px;
grid-template: repeat(10, 1fr)/repeat(10, 1fr);
margin: 50px auto;
background-color: #16eeca;
}
.grid {
border: 1px solid #000;
width: 60px;
height: 60px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="game-container" class="game-container">
<div id="grid-container" class="grid-container"></div>
</div>

Trying to fully understand this function before moving to my next lesson

Question 1. is "makeGrid" just a random name that was given to this function or is there some sort of functionality behind it like "math.random"?
Question 2. "$('.square').css({'width': squareSize, 'height': squareSize});" If the loop already made a Y row to 16 and a X row to 16 and appended it to the container isn't the mission accomplished? Why is this line making the width and height of .square 45 when that was already established in the CSS?
JQUERY
var numberSquares = 16;
var squareSize = 45;
function makeGrid() {
for (var x = 0; x < numberSquares; x++) {
for (var y = 0; y < numberSquares; y++) {
$("<div class='square'></div>").appendTo('.container');
}
}
$('.square').css({'width': squareSize, 'height': squareSize});
}
CSS
.container {
background-color: white;
border: 5px solid black;
border-radius: 5px;
width: 720px;
height: 720px;
margin: 20px auto;
}
.square {
background-color: white;
height: 45px;
width: 45px;
float: left;
opacity: 0;
}

Categories