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>
Related
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>
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>
Here is a beautiful Number Ticker. the whole day I was wondering and trying to modify the code to make it as I want but no success till now!
if you work with numbers with two or more digits then the code creates separate black squares to hold each digit ( run code snippet to have a look ), but I want only a single square as the container to hold multiple digit numbers. So if we have a two-digit number like 10 the Number Ticker should be something like this:
And the next move should look like :
I don't want those parallel animations that move two digits like this (Only the single animation is required not both):
Here is the code:
let counters = document.getElementsByClassName('number-ticker');
let defaultDigitNode = document.createElement('div');
defaultDigitNode.classList.add('digit');
for (let i = 0; i < 11; i++) {
defaultDigitNode.innerHTML += i + '<br>';
}
[].forEach.call(counters, function(counter) {
let currentValue = 10;
let digits = [];
generateDigits(currentValue.toString().length);
setValue(currentValue);
setTimeout(function() {
setValue(8);
}, 2000);
setTimeout(function() {
setValue(7);
}, 5000);
function setValue(number) {
let s = number.toString().split('').reverse().join('');
let l = s.length;
if (l > digits.length) {
generateDigits(l - digits.length);
}
for (let i = 0; i < digits.length; i++) {
setDigit(i, s[i] || 0);
}
}
function setDigit(digitIndex, number) {
digits[digitIndex].style.marginTop = '-' + number + 'em';
}
function generateDigits(amount) {
for (let i = 0; i < amount; i++) {
let d = defaultDigitNode.cloneNode(true);
counter.appendChild(d);
digits.unshift(d);
}
}
});
:root {
background-color: #555;
color: white;
font-size: 25vh;
font-family: Roboto Light;
}
body,
html {
margin: 0;
height: 100%;
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.number-ticker {
overflow: hidden;
height: 1em;
background-color: #333;
box-shadow: 0 0 0.05em black inset;
}
.number-ticker .digit {
float: left;
line-height: 1;
transition: margin-top 1.75s ease;
border-right: 1px solid #555;
padding: 0 0.075em;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Number Ticker</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="number-ticker.css">
</head>
<body>
<div class="container">
<div class="number-ticker" data-value="0"></div>
</div>
<script src="number-ticker.js"></script>
</body>
</html>
Your css has this
.number-ticker .digit {
float: left;
line-height: 1;
transition: margin-top 1.75s ease;
border-right: 1px solid #555;
padding: 0 0.075em;
}
You need to change it to this
.number-ticker .digit {
float: left;
line-height: 1;
transition: margin-top 1.75s ease;
padding: 0 0.075em;
text-align: center;
}
If you remove border-right: 1px solid #555 you will have it look like 1 box.
Also I added text-align: center to center the numbers.
Hope this solves your problem :)
I think the main issue in your code is the digits variable. It creates an array of HTML elements that holds two blocks.
Also, for this line:
let s = number.toString().split('').reverse().join('');
Why do you need to convert number to a string. You can just pass it as is. Once you add to a string using + it will be converted.
I made few changes to your code and commented out the non-relevant part. Please see below:
let counters = document.getElementsByClassName('number-ticker');
let defaultDigitNode = document.createElement('div');
defaultDigitNode.classList.add('digit');
for (let i = 0; i < 11; i++) {
defaultDigitNode.innerHTML += i + '<br>';
}
[].forEach.call(counters, function(counter) {
// let currentValue = 10;
// let digits = [];
let currentValue = counter.getAttribute("data-value");
let digit = null;
generateDigits(currentValue.toString().length);
setValue(currentValue);
setTimeout(function() {
setValue(8);
}, 2000);
setTimeout(function() {
setValue(7);
}, 5000);
setTimeout(function() {
setValue(10);
}, 8000);
function setValue(number) {
// let s = number.toString().split('').reverse().join('');
// let l = s.length;
/*if (l > digits.length) {
generateDigits(l - digits.length);
}*/
/*for (let i = 0; i < digits.length; i++) {
setDigit(i, s[i] || 0);
}*/
digit.style.marginTop = '-' + number + 'em';
}
/*function setDigit(digitIndex, number) {
console.log(number);
digits[digitIndex].style.marginTop = '-' + number + 'em';
}*/
function generateDigits(amount) {
// console.log("generat", amount);
// for (let i = 0; i < amount; i++) {
let d = defaultDigitNode.cloneNode(true);
digit = counter.appendChild(d);
// digits.unshift(d);
// }
}
});
:root {
background-color: #555;
color: white;
font-size: 25vh;
font-family: Roboto Light;
}
body,
html {
margin: 0;
height: 100%;
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.number-ticker {
overflow: hidden;
height: 1em;
background-color: #333;
box-shadow: 0 0 0.05em black inset;
}
.number-ticker .digit {
float: left;
line-height: 1;
transition: margin-top 1.75s ease;
border-right: 1px solid #555;
padding: 0 0.075em;
text-align: center;
}
<div class="container">
<div class="number-ticker" data-value="0"></div>
</div>
Your final JS could be like this:
let counters = document.getElementsByClassName('number-ticker');
let defaultDigitNode = document.createElement('div');
defaultDigitNode.classList.add('digit');
for (let i = 0; i < 11; i++) {
defaultDigitNode.innerHTML += i + '<br>';
}
[].forEach.call(counters, function(counter) {
let currentValue = counter.getAttribute("data-value");
let d = defaultDigitNode.cloneNode(true);
let digit = counter.appendChild(d);
setValue(currentValue);
function setValue(number) {
digit.style.marginTop = '-' + number + 'em';
}
});
This question already has answers here:
How to add a background image on top of a previous background image?
(1 answer)
Can I have multiple background images using CSS?
(8 answers)
Why does z-index not work?
(10 answers)
Closed 3 years ago.
I want to set a z-index to a background img.
I want the .player background to be always displayed, even when in the same box I have another class with another background.
If you click the right button you'll see that at the the .player goes to the blue box another class will be added. The .player background must be always displayed.
Why z-index is not working? is there any alternative?
Thank you
let moveCounter = 0;
let playerOne = {
currentWeapon: "w1"
}
var grid = document.getElementById("grid-box");
for (var i = 0; i <= 8; i++) {
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
$("#square" + 0).addClass("player")
$("#square" + 3).addClass("w3")
function getWeapon(ele) {
let classList = $(ele).attr("class").split(' ');
for (let i = 0; i < classList.length; i += 1) {
if (classList[i][0] === "w") {
$(ele).addClass(playerOne.currentWeapon)
playerOne.currentWeapon = classList[i];
$(ele).removeClass(playerOne.currentWeapon)
return classList[i]
}
}
}
$('#right-button').on('click', function() {
$("#square" + moveCounter).removeClass("player")
moveCounter += 1;
$("#square" + moveCounter).addClass("player")
getWeapon("#square" + moveCounter);
});
#grid-box {
width: 420px;
height: 220px;
}
#grid-box>div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
.player {
background: url(http://placekitten.com/200/300) no-repeat 0 0;
z-index: 1;
}
.w1 {
background: url(https://preview.ibb.co/ntRarR/watermark3.png) no-repeat center center;
z-index: 0;
}
.w3 {
background-color: blue;
}
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<div id="grid-box"></div>
<button class="d-pad-button" id="right-button">Right button</button>
Thank you very much guys.
The problem is the class order in the css file. More info can be found here
Change:
.player {
background: url(http://placekitten.com/200/300) no-repeat 0 0;
z-index: 1;
}
.w1 {
background: url(https://preview.ibb.co/ntRarR/watermark3.png) no-repeat center center;
z-index: 0;
}
To:
.w1 {
background: url(https://preview.ibb.co/ntRarR/watermark3.png) no-repeat center center;
z-index: 0;
}
.player {
background: url(http://placekitten.com/200/300) no-repeat 0 0;
z-index: 1;
}
Demo
let moveCounter = 0;
let playerOne = {
currentWeapon: "w1"
}
var grid = document.getElementById("grid-box");
for (var i = 0; i <= 8; i++) {
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
$("#square" + 0).addClass("player")
$("#square" + 3).addClass("w3")
function getWeapon(ele) {
let classList = $(ele).attr("class").split(' ');
for (let i = 0; i < classList.length; i += 1) {
if (classList[i][0] === "w") {
$(ele).addClass(playerOne.currentWeapon)
playerOne.currentWeapon = classList[i];
$(ele).removeClass(playerOne.currentWeapon)
return classList[i]
}
}
}
$('#right-button').on('click', function() {
$("#square" + moveCounter).removeClass("player")
moveCounter += 1;
$("#square" + moveCounter).addClass("player")
getWeapon("#square" + moveCounter);
});
#grid-box {
width: 420px;
height: 220px;
}
#grid-box>div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
.w1 {
background: url(https://preview.ibb.co/ntRarR/watermark3.png) no-repeat center center;
z-index: 0;
}
.player {
background: url(http://placekitten.com/200/300) no-repeat 0 0;
z-index: 1;
}
.w3 {
background-color: blue;
}
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<div id="grid-box"></div>
<button class="d-pad-button" id="right-button">Right button</button>
Hi I am trying to make the columns and rows in the mainContent div but the problem is the it is getting out of the mainContent after 2-3 clicks. I want it to remain inside and should create equally sized columns and rows inside of it. here is my code.
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
console.log('clicked');
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
});
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
}
#grid {
margin: 0;
padding: 0;
border: none;
outline: 5px dashed #aba4a4;
display: inline-block;
height: 100%;
width: 50%;
}
<div id="contentArea">
</div>
<button id="btn">
create
</button>
Because you are using fixed height for the appending element.
You should resize the element after every click using some logic or you can use the display of your parent as flex and flex wrap true.
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
});
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
#grid {
margin: 0;
padding: 0;
border: none;
outline: 5px dashed #aba4a4;
display: inline-block;
width: 50%;
}
<div id="contentArea">
</div>
<button id="btn">create</button>
or
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
resizeDiv();
});
var maxInRow = 2;
function resizeDiv() {
var allGrids = document.querySelectorAll("#contentArea > #grid");
var width = 100 / maxInRow;
var len = allGrids.length;
var colNo = Math.floor(len / maxInRow);
colNo = colNo - (len / maxInRow) == 0 ? colNo : colNo + 1;
var height = 100 / colNo;
for (var i = 0; i < len; i++) {
allGrids[i].style.width = width + "%";
//"calc(" + width + "% - 10px)"; --- if doesn't want box-sizing to be borderbox
allGrids[i].style.height = height + "%";
//"calc(" + height + "% - 10px)"; --- if doesn't want box-sizing to be borderbox
//reduce the size of box which increased due to outline
}
}
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
position: relative;
}
#grid {
margin: 0;
padding: 0;
border: 5px dashed #aba4a4;
display: inline-block;
height: 100%;
width: 50%;
position: relative;
box-sizing: border-box;
}
<div id="contentArea">
</div>
<button id="btn">
create
</button>