loading...

Dynamically Filling in a CSS Grid with JavaScript

kaelscion profile image kaelscion ・5 min read

Why hello there! Today, I finally submitted. As many of you know, I am a Python developer. However, I have resolved that from now on, I will not outsource my front-end development and am forcing myself to learn JavaScript.

HOWEVER, I refuse to start by learning a "new hotness" framework or tooling system (React folks, stop typing. I'm just not digging into it yet, I'm sorry. One miracle at a time 😛) and will only be using Javascript to the ES6 standard. Please note, I just started actually writing JS code with any sort of real use today so there will plenty of "Hey, you can do that better!" opportunities in the code samples. But, I sincerely ask that you follow up those cherry-picks with how it can be better so that I can learn the right way and I thank this wonderful community in advance for any constructive criticism they might have! With that in mind, let's dive in!

So, what little project have I decided to try out first? Well, many options came to mind. But in the end, I decided to try and find a practical use for the idiotic matrix and grid traversal algorithms all of us are forced to do in the whiteboard challenge round of the dev interview process. The tiny project that I'm about to share simply creates a CSS Grid with a specified number of equal rows and columns. A separate script then places square blocks of a pseudo-random (but limited) size into the grid at a pseudo-random x and y location. This creates a fun randomized section of a page that changes on every page load. I've had great fun with it and hope you do too! The personal website for my wife and I's development studio will soon feature a version of this on our portfolio page that displays the blocks as thumbnails of our past projects.

This project will also be available on GitHub and free for anybody to modify for their own projects. If you do end up using it, please show us what you've created as we get so much more joy out of seeing what you guys build than what we come up with on our own!

So to start, let's lay out our HTML page which will serve as nothing more than a vessel for our javascript

<html>
    <head>
        <script type="text/javascript" src="gridTraversal.js"></script>
    </head>
    <body>
    </body>
</html>

Pretty basic. Bare-bones even. With that done, we can get on to the fun stuff. As I am a huge advocate of compartmentalization of code, we are going to write three functions, one to create the div that will hold our grid, one that will lay out the rows and columns of that grid, and a final one to place the blocks. Our grid creation div is created with the following JS code

const createGrid = () => {
    const markup = `
    <div class="container" id="container" style="display: grid;
                                                 border: 1px black solid; 
                                                 height:100%; 
                                                 width: 100%">
    </div> `
    document.body.innerHTML += markup
}

As you can see, this is very ES6 what with the arrow functions and template strings. But we are not importing any libraries or frameworks in this entire project other than the JS core.

I've found the easiest way, for me personally, to create HTML in JS without having to involve JSX is to simply write the markup code as a variable (in this case const markup) and append it to the body's DOM via document.body.innerHTML += markup.

All this script does is create a div that takes up the whole viewport, that has a grid display with a black border around it.

To lay out the grid columns and rows, we involve this guy

const layoutGrid = (height, width) => {
    const container = document.getElementById('container')
    container.style.gridTemplateColumns = `repeat(${width}, 1fr)`
    container.style.gridTemplateRows = `repeat(${height}, 1fr)`
}

Those of you familiar with JS should be able to see what this does pretty clearly right off. For those that aren't familiar with JS, this script selects our grid container from the DOM, and assigns two styles to it, gridTemplateColumns and gridTemplateRows, and uses the CSS repeat function to assign equally sized columns and rows using the fr css unit. The number of columns and rows are determind by the height and width arguments passed to the layoutGrid function.

So now we have a grid div that contains a grid. What about the blocks? The following script addresses that:

const fillGrid = (x, y, blockSize, numOfBlocks, color) => {
    const container = document.getElementById('container')
    const test = Array(num_of_blocks).keys()
            for (var i of test){
                let markup = `<div id="card ${i}" 
                                   style="grid-column: ${Math.floor(Math.random() * y) + 1} / span ${blockSize}; 
                                          grid-row: ${Math.floor(Math.random() * x) + 1} / span ${blockSize};
                                          background-color: ${color};
                                          border: 1px black solid;">${i}</div>`
                container.innerHTML += markup
            };
}

To pro JS devs: please be gentle. To new JS devs: I'm sorry. I am not sure what it is, but I feel like this script is very ugly. Putting that aside, let me at least explain what it does.

First, we define a function named fillGrid that accepts the arguments x, y, blockSize, numOfBlocks, and color. x and y define the starting axes of each block we're going to place. blockSize determines how many spaces in the grid each block will occupy. numOfBlocks is how many blocks we're going to be tossing into this grid and finally, color is tells the browser what the background color of these blocks will be.

Our first line selects the DOM element that contains our grid. We then generate an Array that has numOfBlocks items in it and iterate over it using a for...of loop that creates our blocks.

Each block has its size and placement defined by the Math.floor() function (which generates pseudo-random whole numbers) that are between 0 and the maximum block size established by the blockSize argument.

Each block then receives a 1px black solid border and the specified background color, and is appended to the Grid's markup.

Now, let's include our scripts in our HTML document like so

<html>
    <head>
        <script type="text/javascript" src="gridTraversal.js"></script>
    </head>
    <body>
        <script>createGrid();</script>
        <script>layoutGrid(5, 5)</script>
        <script>fillGrid(5, 5, 4, 2, "green")</script>
    </body>
</html>

Run the script and watch the fun! Every page load (or refresh) will rearrange the blocks in the grid. Play around with the size of the blocks, the number of blocks to place, and the number of columns and rows in the grid! Many times the blocks overlap and create really unique-looking grid layouts. Give it a try and let me know what you get!

If you like this kind of content, remember to give this post a like, comment, of bookmark it for reading so that I know what you all want to see next! Thanks so much for reading and I look forward to providing more content for all you Devvers in the future!

Discussion

pic
Editor guide
Collapse
thetos_dastil profile image
Thetos

Hi, thanks for the post, it helped me learn a thing or two.
I think I may be able to help with, or give advice.

For example, when you write your createGrid function, you write the entire grid style in your markup constant, however it would maybe be better if you only put the classes and id and actually define the rules in a css file, using either your id or class as a selector (whichever is more appropriate).

Secondly, I don't know if you actually know it but maybe instead of using lambdas with the const name = (params) => { /*code*/} syntax you probably could use the function keyword which lets you define a function with code like:

function createGrid() {
     //code
}

This code will define a named function, the use would not be different in code.
This is up to your personal preference in the end.

Then, as a tip, when using a for loop to go through an array, the in keyword lets you go through the keys. For example:

let arr = array(10);
let keys = arr.keys();
for (let i of keys) {
    //code
}

can become :

let arr = array(10)
for (let key in arr) {
   //code
}

Thirdly, document.querySelector and document.querySelectorAll can retrieve any element using css selectors for example:

let firstDiv = document.querySelector("div");
// you can search inside any other element you retrieved as this is a function with HTMLElement type objects
let titles = firstDiv.querySelectorAll("p .title"); // querySelectorAll returns an HTMLCollection, not a simple array!

Lastly, for a bit of nitpicking, at the end, you use three script tags in your HTML to call each individual functions, you can put it all in one and it will work just fine, as long as you don't forget newlines/semi-colons.
I would advise you to put the code in it's own "main.js" file though.

Now maybe that in the time between your original post and this comment you have picked on a bit of what I say here, and I'd like to say that what I say is not an absolute truth you should follow either, that's just how I learned, and I hope I could help you learn too!

Collapse
kaelscion profile image
kaelscion Author

Thank you so much for your reply! To be totally honest, I am a Python developer and wrote this post to try and encourage myself to get better acquainted with the JS standard library.

1) thanks so much for offering constructive criticism! It is one of my greatest joys to interact with better developers than myself and I really appreciate your suggestions!

2) I would hardly call your criticisms "nitpicky", but thank you for being tactful 😁. I'm sure it was easy to see that I am making there Cardinal mistake of approaching my target language (JS) the same way I would my source language (Python) so feel free to give me "the business" about how to make it right.

😁😁