Dan Cotton

Displaying a list of elements in a grid

Overview

Say you have an array of elements that you wish to display as a grid. The general formula for this would be:

var numColumns = Math.floor(viewport.width / itemWidth);
for(var i = 0; i < items.length; i++)
{
    var item = items[i];
    item.x = ((i % numColumns) * itemWidth);
    item.y = (Math.floor(i / numColumns) * itemHeight);
}

Applying this to the web

The above is a generic formula you can apply to most languages. Below we will apply this to Javascript to make something like this:

1

(Holy assortment of colours Batman!)

First lets create our basic HTML

...
<body>
    <h1>HTML Grid of Elements</h1>
    <div id="container" class="container"></div>
</body>
...

and some CSS

body { width:100%; margin:0; padding:0; }
.container { position:relative; }
.item 
{
    width: 64px;
    height:32px; 
    position:absolute; 
    top:0; left:0; 
    transition:all 0.75s ease; 
}

We now have a simple HTML page with a container div that will span the width of the body. We will add our grid elements to this container and assign them a class of ‘item’. This ‘item’ class will set the elements position to absolute so we can set a top & left value with code. The container element has a relative position so its children’s coordinates will start from the containers top/left bounds, and not the global page bounds.

-Quick note-

We could have just given the item class a ‘float:left’, however because we’re awesome we are going to add some animation later on. You can only animate an elements position if you have top/left/right/bottom values assigned to them.

Now lets create our elements. Add a script tag below the container div in your HTML and add the following:

(function()
{
    var container = document.getElementById("container");
    var items = [];
    var numItems = 500, i = 1, item = null;
    while(numItems--)
    {
        item = document.createElement("div");
        item.classList.add("item");
        item.style.background = "#" + (Math.floor(Math.random() * 0xFFFFFF)).toString(16);
        item.style.transform = "translate3d(0, 0, 0)";
        item.innerHTML = (i++).toString();
        items.push(item);
        container.appendChild(item);
    }
})();

If you were to save and run this code now we would see all the item elements stacked on top of each other so it only looks like there is actually one element. You should however see the top most element will have the number 500.

Now lets apply the grid formula code above to position the item elements. Add this code underneath the while loop:

function arrange()
    {
        if(items.length == 0) return;

        var numColumns = Math.floor(window.innerWidth / items[0].offsetWidth);
        var item;
        for(var i = 0; i < items.length; i++)
        {
            item = items[i];
            item.style.left = ((i % numColumns) * item.offsetWidth) + "px";
            item.style.top = (Math.floor(i / numColumns) * item.offsetHeight) + "px";
        }
    }

    arrange();

Save and run the page and you should see the items spread across a grid the width of the container element. Yay!

Padding

What if we want to space the items away from each other? To add some padding around each element, we need to modify the arrange function like so:

function arrange()
    {
        if(items.length == 0) return;

        var padding = 8;
        var numColumns = Math.floor(container.offsetWidth / items[0].offsetWidth);
        numColumns = Math.floor((container.offsetWidth - (padding * numColumns)) / items[0].offsetWidth);
        var item;
        for(var i = 0; i < items.length; i++)
        {
            item = items[i];
            item.style.left = ((i % numColumns) * (item.offsetWidth + padding)) + "px";
            item.style.top = (Math.floor(i / numColumns) * (item.offsetHeight + padding)) + "px";
        }
    }

Responsiveness

We have a problem though - if you resize your browser the grid isn’t responsive. Lets fix that by adding a resize listener underneath the arrange() call:

var timeoutId = -1;
window.addEventListener("resize", onResize);

function onResize(e)
{
    clearTimeout(timeoutId);
    timeoutId = setTimeout(arrange, 100);
    arrange();
}

Save and refresh the web page and now when you resize your browser you should see your grid re-flow itself. Neat!

For performance sake I added in a setTimeout trick so the arrange function doesn’t run unnecessarily whilst the user is dragging the window around. The arrange() function is quite expensive especially over 500 items so it is best to run it as little as possible.

Cowbell

How can we take the above grid further? By adding animation of course! The best thing about modern browsers is we can created the desired animation effect by adding one single line to the CSS ‘item’ class:

.item 
{
    width: 64px;
    height:64px; 
    position:absolute; 
    top:0; left:0; 
    transition:all 0.75s ease; /* animation! */
}

Save and reload the web page and have a go at resizing your browser. Pretty awesome, right!

Hardware Acceleration

By default I believe the latest builds of desktop browsers auto hardware accelerate CSS transitions wherever possible. If you are having performance problems try adding

transform:translateZ(0);

to the ‘item’ CSS class. The other possibility is that 500 items is too much of a strain for your device so you can always dial this down.

Wrap up

You can see a demo of this here. Feel free to adapt and change the code as you wish. You aren’t limited to squares either - try adjusting the width & height values in the ‘item’ CSS class!

Tagged with
Javascript , Animation