Function Not Outputting Stack of Boxes in Javascript on Browser - javascript

I'm using JavaScript to create a stack of 16 boxes. I don't think I have the makeBox() function in the right place.
let makeBox = function() {
let box = document.createElement('div');
document.body.appendChild(box);
box.style.width = '28px';
box.style.height = '28px';
box.style.border = '1px solid black';
return box;
};
let makeGrid = function(numberOfRows) {
let y = 0;
let x = 0;
while (y < numberOfRows) {
x = 0;
while (x < numberOfRows) {
x = x + 1;
}
y = y + 1;
}
makeBox();
};
makeGrid(16);
I'm just getting one box in the browser. If anyone has any experience with this, if they could please help.

If you want to make a grid of boxes CSS Grid can help. It saves effort on creating nested loops. Just loop from 0 to the number passed in as the argument multiplied that same number, and create a box on each iteration. Then add it to the element that's been set up to control the grid.
I would also use a class for the box too.
function makeBox(x) {
const box = document.createElement('div');
box.classList.add('box');
box.textContent = x;
return box;
};
// The grid will be the argument (a number)
// multiplied by that number again, so you just need
// to loop from 0 to that number
function makeGrid(n) {
const grid = document.querySelector('#grid');
for (let x = 0; x < n * n; x++) {
grid.appendChild(makeBox(x));
}
};
makeGrid(16);
#grid { display: grid; grid-template-columns: repeat(16, 1fr); gap: 2px; }
.box { width: 28px; height: 28px; border: 1px solid black; text-align: center; }
<div id="grid"></div>

Related

Add missing squares when div is resized

I want to create a div, which has many smaller squares inside of it as such:
The problem is, is that when I resize my screen, the squares re-adjust making
the rows uneven:
In that image, I could add 4 more squares, and the div is filled again equally with squares.
So I am wondering how would I add the missing squares on resize.
Here is a jsfiddle of my project
below is my code laid out:
const container = document.querySelector(".container");
for (let i = 1; i <= 20; i++) {
// create dom elem
const square = document.createElement("div");
square.setAttribute("class", "square");
container.appendChild(square);
}
.container {
border: 3px solid black;
width: 50%;
height: 50%;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
justify-content: center;
padding: 10px;
gap: 10px;
}
.square {
min-width: 40px;
min-height: 40px;
background-color: black;
}
<div class="container">
</div>
Add flex-grow: 1; on the item (the .square) see if the result fits your needs
Not sure if that what you want but this code will let u keep constant number of rows of boxes with constant size for each box
const container = document.querySelector(".container");
const boxWidth = 40;
const gap = 10;
const padding = 10;
const numberOfRows = 4;
let numberOfBoxes = 0;
window.addEventListener("resize", (e) => {
renderBoxes();
});
function renderBoxes() {
const containerWidth = container.clientWidth;
const boxesPerRow = ~~((containerWidth - padding) / (boxWidth + padding));
const n = boxesPerRow * numberOfRows;
if (n != numberOfBoxes) {
container.innerHTML = "";
for (let i = 1; i <= n; i++) {
const square = document.createElement("div");
square.setAttribute("class", "square");
container.appendChild(square);
}
}
}
renderBoxes();
padding constant refers to padding in one side only, the rest are clear

Centering multiple images in a <tr> element

So I'm building this app which is an implementation of the game "Mancala". In a position of the board there can be "seeds" (game's piece) which I chose to represent as images.
In the initial setup of the game, there are N seeds in each position of the board. I represent this as N equal images ("seed.png") printed randomly in the respective position.
I want images to overlap, so even when N is a big number, they will all fit in the position ("see image nrº1"). What I accomplished so far is a random distribution with little to none overlapping and some "seeds" are getting out of the circle.
This is the code I have, built in JavaScript:
function init_board() {
const board = document.getElementById("board");
for(let i = 0; i < 2; i++) {
const tr = board.insertRow();
if(i == 0) {
tr.insertCell().setAttribute("rowSpan", "2");
}
for(let j = 0; j < 6; j++) {
var x = tr.insertCell();
for(let k = 0; k < 20; k++) {
var img = document.createElement("img");
img.src = "images/seed.png";
img.height = "10";
img.width = "10";
img.style.position = "relative";
img.style.left = Math.floor(Math.random() * 7) + "px";
img.style.top = -7 + Math.floor(Math.random() * 14) + "px";
x.appendChild(img);
}
}
if(i == 0) {
tr.insertCell().setAttribute("rowSpan", "2");
}
}
With the following formatting:
#board {
margin-left: auto;
margin-right: auto;
position: relative;
top: 30%;
}
td {
width: 75px;
height: 75px;
border: 3px solid darkred;
border-radius: 40px;
background: antiquewhite;
}
table {
border: 5px solid darkred;
border-radius: 20px;
background: burlywood;
}
Image Nrº1 N=20: https://imgur.com/a/7aNVsUb,
Image Nrº2 where N=30 and the seeds change the size of the circle: https://imgur.com/a/2iHXwyd
Thank you in advance!
To apply width and height correctly to td tag, you need to make it an inline-block element.
td:not([rowspan="2"]) {
display: inline-block;
}
Your pen updated (with seeds=30): CodePen

how to get cubes to change to black when mouse moves over them

how does one get the cubes made in the grid to turn black with mouse running over? any help would be appreciated.
function Grid(z) {
for (y=0; y < z; y++) {
for (x=0; x < z; x++) {
size = 700 / z;
var div = document.querySelector('#container');
var block = document.createElement('div');
block.style.height = size + 'px';
block.style.width = size + 'px';
block.classList.add('cube');
div.appendChild(block);
}
}
}
function changeBlockColor() {
Grid(16);
var s = document.querySelector('.cube');
s.addEventListener('onmouseover', function(){
s.setAttribute('style', 'background: black');
});
}
changeBlockColor();
I would use CSS to achieve this effect.
.cube {
background-color:red;
}
.cube:hover {
background-color: black;
}
The following is for if you want the cubes to stay black after you finished hovering over them (and then hovered out).
First, your s = document.querySelector('.cube'); will make s only point to the first element with class cube. To solve that, make s an array of all elements of class cube, by using s = document.querySelectorAll('.cube'); instead.
Next, you need to loop through the array s and add the event listener to all its elements:
for(var i = 0; i < s.length; i++) {
s[i].addEventListener('mouseover', function(){
this.setAttribute('style', 'background: black');
});
}
Notice the use of this inside the handler. Inside handler code, this refers to the object that triggered the event (the cube moused over in your case).
No need to have hundred of mouseover Event Listener. Just one is enough
var
divContainer = document.getElementById('container'),
CubeClass = 'cube';
function Grid(z)
{
var sizePx = Math.floor(700 / z) + 'px';
for (let y = 0; y < z; y++)
{
for (let x = 0; x < z; x++)
{
let block = document.createElement('div');
block.style.height = sizePx;
block.style.width = sizePx;
block.className = CubeClass;
divContainer.appendChild(block);
};
};
}
divContainer.onmouseover = function(e)
{
if (!e.target.classList.contains( CubeClass )) return;
e.target.setAttribute('style', 'background: black');
}
divContainer.onmouseout = function(e) // if you need it...
{
if (!e.target.classList.contains( CubeClass )) return;
e.target.removeAttribute('style');
}
The event name you want is mouseover, not onmouseover
Also, querySelector will only find the first matching element, so you need to use querySelectorAll or getElementsByClassName instead
Finally, you need to iterate over all the elements you matched, which are returned in an Object, not an Array, so you need to use a for loop.
Solution
function createGrid(z) {
for (var y = 0; y < z; ++y) {
for (var x = 0; x < z; ++x) {
var size = 700 / z;
var div = document.getElementById('container');
var block = document.createElement('div');
block.style.height = size + 'px';
block.style.width = size + 'px';
block.classList.add('cube');
div.appendChild(block);
}
}
changeBlockColor()
}
function changeBlockColor(){
var cubes = document.querySelectorAll('.cube')
for (var i = 0; i < cubes.length; i++) {
cubes[i].addEventListener("mouseover", function(e) {
e.target.classList.add('active')
})
}
}
createGrid(16);
#container {
display: flex;
flex-wrap: wrap;
}
.cube {
background-color: red;
}
.active {
background-color: black;
}
<div id="container"></div>
Implementation Details
Naming is all preference in JS, but traditionally, names that start with a capital letter are for a class, so I renamed you function to createGrid
At the end of createGrid I call changeBlockColor, rather than call createGrid from inside changeBlockColor, logically it make more sense.
I created a CSS class called active to handle changing the color, as using setAttribute('style') was erasing the height and width styles you applied inside you Grid function.
Feedback
You use querySelector exclusively, you should get to know getElementById as well.
You use var a few times, but don't declare y, x, or size
You can define y and x in your for loop with for(var y=0 and for(var x=0
CSS Solution
Assuming you want the color to revert on mouseout, you can achieve this same effect with CSS using .cube:hover
.cube {
display: inline-block;
width: 50px;
height: 50px;
background-color: red;
}
.cube:hover {
background-color: black;
}
<div class="cube"></div>
<div class="cube"></div>
<div class="cube"></div>
<div class="cube"></div>
<div class="cube"></div>
Performance
A side note about using querySelector('#container') vs getElementById('container'). The first has to traverse the entire DOM looking for the selector, the latter can just go to the internal list of ids and return the reference.
https://jsperf.com/so53824751
Documentation
https://developer.mozilla.org/en-US/docs/Web/Events/mouseover
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById

Retrieve an element inside an object collection

I'm making board game, and I want that when I click in one of the places I turn them red. I have this array of divs, but I don't know how to retrieve the element given the number to turn it red. How can I do that, I'm trying to use .element but it's not working
var number = 3;
const board = [];
const boardWidth = boardHeight = 10;
(function() {
const boardElement = document.getElementById('board');
for (var y = 0; y < boardHeight; ++y) {
var row = [];
for (var x = 0; x < boardWidth; ++x) {
var cell = {};
cell.element = document.createElement('div');
boardElement.appendChild(cell.element);
row.push(cell);
}
board.push(row);
}
painting();
})();
function painting() {
board[number][number].element.style.backgroundcolor = 'red';
}
#board {
width: calc(10 * 30px);
margin: auto;
}
#board div {
background-color: black;
border: 1px solid white;
box-sizing: border-box;
float: left;
width: 30px;
height: 30px;
}
<div id="board"></div>
Your code look very confusing. board is an element and you are using it as an array.
Next come some code. I hope this is what you need:
let cellW = 100;
let cellH = 100;
function init(){
let boardArray = [];
let bStyle = window.getComputedStyle(board, null);
let bWidth = parseInt(bStyle.getPropertyValue("width"));
let bHeight = parseInt(bStyle.getPropertyValue("height"));
for (let y = 0; y < bHeight; y+=cellH) {
let row = [];
for (let x = 0; x < bWidth; x+=cellW) {
let cell = {};
cell.element = document.createElement('div');
cell.element.style.width = cellW +"px";
cell.element.style.height = cellH +"px";
board.appendChild(cell.element);
row.push(cell);
}
boardArray.push(row);
}
}
init();
let cells = Array.from(document.querySelectorAll("#board div"));
cells.map( cell => {
cell.addEventListener("click", e =>{
cell.style.background = "red"
})
})
#board{width: 1000px; height:500px; display:flex;flex-wrap:wrap;}
#board div{outline:1px solid;}
<div id="board"></div>
UPDATE:
I understand that you need to make the 4-th cell in the cells array red:
var number = 3;
const board = [];
const boardWidth = 10, boardHeight = 10;
function init() {
const boardElement = document.getElementById('board');
for (var y = 0; y < boardHeight; ++y) {
var row = [];
for (var x = 0; x < boardWidth; ++x) {
var cell = {};
cell.element = document.createElement('div');
boardElement.appendChild(cell.element);
row.push(cell);
}
board.push(row);
}
board[number][number].element.style.background = "red"
}
window.addEventListener("load", init);
#board {
width: calc(10 * 30px);
margin: auto;
}
#board div {
background-color: black;
border: 1px solid white;
box-sizing: border-box;
float: left;
width: 30px;
height: 30px;
}
<div id="board"></div>
I've addressed all of your issues but thought you might be interested in making things configurable. I reworked your code to make things config driven.
This will:
Take a config (or not) and merge it with a default config.
Build your board dynamically based on config values v. setting the dimensions via CSS
Allows for cell toggle (select/unselect) and matric position assignments
document.addEventListener('DOMContentLoaded', function(){
const extend = function(target, config, defaults){
defaults && extend(target, defaults);
if(target && config && typeof(config) === 'object'){
for(const i in config){
target[i] = config[i];
}
}
return target;
};
function Game(config){
const defaultConfig = {
boardElement: '#board',
// if a single digit is passed it will be duplicated for pos y 3 => 3,3
startPosition: 3,
cellSize: 30,
boardWidth: 10,
boardHeight: 10
};
// merge the default and user-defined config into a new config
this.config = extend({}, config || {}, defaultConfig);
// cache ref to your board element
this.boardElement = document.querySelector(this.config.boardElement);
// stores our collection of board items
this.board = [];
// draw the board
this.draw();
// set initial marker
if(this.config.startPosition){
if(this.config.startPosition instanceof Array){
this.paint.apply(this, this.config.startPosition);
}else{
this.paint(this.config.startPosition);
}
}
return this;
}
extend(Game.prototype, {
draw(){
for (let y = 0; y < this.config.boardHeight; ++y) {
const row = [];
for (var x = 0; x < this.config.boardWidth; ++x) {
const element = document.createElement('div');
const cell = {
element,
position: {
x: x + 1,
y: y + 1
}
};
// set cell width and height
element.style.height = element.style.width = `${this.config.cellSize}px`;
// handle selecting/unselecting cells
element.addEventListener('click', () => this.paint(cell.position.x, cell.position.y));
this.boardElement.appendChild(cell.element);
row.push(cell);
}
this.board.push(row);
}
// set board width and height
this.boardElement.style.width = `${this.config.boardWidth * this.config.cellSize}px`;
},
paint(x, y){
if(y === undefined){
y = x;
}
const element = this.board[y-1][x-1].element;
if(element){
const isSelcted = element.style.backgroundColor === 'red';
element.style.backgroundColor = isSelcted ? 'black' : 'red';
}
}
});
new Game({
startPosition: [5,4],
boardWidth: 8,
boardHeight: 8
});
});
#board {
margin: auto;
}
#board div {
background-color: black;
border: 1px solid white;
box-sizing: border-box;
float: left;
}
<div id="board"></div>

Create custom square grid using jquery

I am working on a project trying to make something resembling an etch-a-sketch. I have a 780x780px square, and I am trying to get a 16x16 grid, using a series of smaller square divs.
It is on this grid that I have the hover effect. I keep getting a 15x17 grid of square divs because the last square of the row won't fit. I have margins set to 1px and padding set to 0 so I figured that to fit 16 squares on a 780px wide row, it would require me to take into account the margins (15 1px margins) and from there I could divide (780-15) by 16, the number of squares I want.
That isn't working, and the next step of this project is to have a button where the user could input any number of squares for the row/column and have either a larger or smaller squared grid STILL ON the 780x780 square. Does anyone have any ideas? I'm pretty stumped.
$(document).ready(function() {
var original = 16;
for (var y = 0; y < original * original; y++) {
$(".squares").width((780 - 15) / original);
$(".squares").height((780 - 17) / original);
$("<div class='squares'></div>").appendTo('#main');
}
$('.squares').hover(
function() {
$(this).addClass('hover');
}
)
});
function gridq() {
$('.squares').removeClass('hover');
$('div').remove('.squares');
var newgrid = prompt("How many squares on each side?");
var widthscreen = 192;
if (newgrid > 0) {
for (var x = 0; x < newgrid * newgrid; x++) {
$(".squares").width(widthscreen / newgrid);
$(".squares").height(widthscreen / newgrid);
$("<div class='squares'></div>").appendTo('#main');
}
$('.squares').hover(
function() {
$(this).addClass('hover');
}
)
}
}
#main {
height: 780px;
width: 780px;
background-color: antiquewhite;
position: relative;
}
.squares {
margin: 1px;
padding: 0;
background-color: aquamarine;
display: inline-block;
float: left;
}
.hover {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id=main>
</div>
<button onclick="gridq()">Go Again!</button>
Try this snippet? Grid initialisation is set in the grid() function and then called later when necessary. The width is set dynamically to the 16th square's right side.and the remaining squares fill out as necessary.
var wide = (780 - 15) / 16,
tall = (780 - 17) / 16; // set the square dimensions. this can be incorporated into the grid() function with 16 replaced by 'original'
function grid(x, y) {
var original = x,
y = y;
$("#main").empty(); // empty and restart
$("#main").width(wide * (original + 1));
for (var i = 0; i < original * y; i++) {
$("<div class='squares'></div>").appendTo('#main');
}
var square = $(".squares");
square.width(wide);
square.height(tall);
var side = square.eq(original - 1).position().left + square.width() + 2; // tighten the #main width
$("#main").width(side);
$('.squares').hover(
function() {
$(this).addClass('hover');
}
)
}
grid(16, 16); // starting dimension
function gridq() {
$('.squares').removeClass('hover');
$('div').remove('.squares');
var newgrid = prompt("How many squares on each side?");
var widthscreen = 192;
if (newgrid > 0) {
grid(newgrid, newgrid);
}
}
#main {
background-color: antiquewhite;
position: relative;
}
.squares {
margin: 1px;
padding: 0;
background-color: aquamarine;
display: inline-block;
float: left;
}
.hover {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id='main'>
</div>
<button onclick="gridq()">Go Again!</button>
just got beat to it...ill post this as it answers the question slightly differently and I feel is a little cleaner. Also I added in a width and a height prompt.
see the codepen here
As a side note...its good practice to make the names of your variables make sense. Also I find that breaking down your code problems into smaller more manageable chunks makes it seem less overwhelming...one step at a time :)
Enjoy and good luck!
$(document).ready(function() {
//declare the variables at the top of your functions...it enables us to change them later
var columnWidthCount = 16;
var columnHeightCount = 16;
function makeBoxes() {
//boxcount lets us set how many times we want the for loop to run...when we change the columns/rows later this variable will be updated
var boxCount = columnWidthCount * columnHeightCount;
//
for (var i = 0; i < boxCount; i++) { //loop through each box
//any code you place in here will execute each time we loop around
$("<div class='squares'></div>").appendTo('#main');
}
//we only want to declare this once so we place it after the loop
$(".squares").width((780 / columnWidthCount) - 2);
$(".squares").height((780 / columnHeightCount) - 2);
$('.squares').hover(
function() {
$(this).addClass('hover');
}
);
}
//fire the initial function
makeBoxes();
// fire function after click
$('button').on("click", function() {
$('div').remove('.squares');
var squaresHigh = prompt("How many squares high? (must be a number)");
var squaresWide = prompt("How many squares wide? (must be a number)");
//prompt returns a string...use parseInt to turn that number string into an integer
columnWidthCount = parseInt(squaresWide);
columnHeightCount = parseInt(squaresHigh);
makeBoxes();
});
});
#main {
height: 780px;
width: 780px;
background-color: antiquewhite;
position: relative;
font-size:0;
white-space:nowrap;
}
.squares {
margin: 1px;
padding: 0;
background-color: aquamarine;
display: inline-block;
float: left;
}
.hover {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id=main>
</div>
<button>Go Again!</button>

Categories