Wednesday, November 13, 2013

Multiple columns in HTML/CSS (and JS for poor old Internet Explorer)

A quickie on how I solved a need to display multiple columns, before I forget (and may be helpful to some of you, my dear readers).

The problem — I have a dynamically generated text (parameter fields in a settings form), which I need to display on the screen utilising as much width as possible. If the fields were not dynamically created, it would of course be better to layout the form manually — then you can make intelligent choices about what goes where. Not in this case, though, so I needed an automated way to display this content in columns.



Cascade Style Sheet FTW!

Of course, there is CSS3, which is really cool and can do it nicely and fairly easily:
#content
{
    -moz-column-count: 3;
    -moz-column-gap: 20px;
    -webkit-column-count: 3;
    -webkit-column-gap: 20px;
    column-count: 3;
    column-gap: 20px;
}

column-count defines how many columns CSS will attempt to lay the content out (utilising the width that is available to the element), column-gap is the margin between the columns. Of course there is the -moz and -webkit prefix madness that we've all learnt to expect already.

This works surprisingly well on all desktop browsers (didn't test on mobile, not required for this project, yet I hope the newer ones will render this too… or — see below), except for… drumroll… opening the envelope… ah, you knew it already — IE9 and below.

Poor things, they've worked enough and need their rest, but people keep using them. Anyway, we need a solution that would work on these too.

JavaScript FTW!

I don't go with a JS solution for everything because it is less transparent (and, therefore, error-prone and harder to fix if things go wrong in some obscure situation), may slow the page down, and, well, is not the tool for the job. Unless you're on IE.

So, I used a combination of Columnizer (a jQuery plugin) and Modernizr (what's with all the -zr fashion?).

Modernizr is a very nice library that can be used to detect support of the CSS columns feature, and, if it is not present, introduce a fallback to JS.

Even better — and I love this part, — Modernizr can load the Columnizer library if it is required — so this improves page load time for the browsers that do support columns. Here's how it is done (in your JS code, on page load):
Modernizr.load({
   test: !Modernizr.csscolumns,
   yep: '/js/jquery.columnizer.js',
   nope: ''
});

Very simple — we test support for CSS columns, and if not supported — load the additional JS code.

Note that the .load method is not included in Modernizr development build (a very odd decision, I'd say), so if you plan on using this feature — you need to select it in the main Modernizr download builder (in the Extras section).

Now, where you need to apply the columns, you can use the following JS code:
if (!Modernizr.csscolumns && $('#content .column').length == 0) {
   $('#content').columnize({ columns: 3 });
}

What it does is again check whether CSS columns are supported, and applies columnization (isn't a word, I know) to our element of choice, to create three columns. The element in my case may be shown multiple times, and this script is called each time, this is why I check for presence of .column class in our element ($('#content .column').length equals zero) — Columnizer add this class to its generated columns for easy styling, and thus I can also detect whether it has done its job before or not.

Note that I am not checking “if the browser is IE” — this is the beauty of Modernizr. Instead we can check for support of a specific feature, so this will work on other browsers that don't support it, not just limited to IE (I am thinking about older mobile browsers here). Nice, isn't it?

Then I just need to apply my column-gap style to the HTML generated by JS:
#content .column /* Needed for columns created by the Columnizer script */
{
   padding-right: 20px;
   -webkit-box-sizing: border-box;
   -moz-box-sizing: border-box;
   box-sizing: border-box;
}

box-sizing (with unavoidable entourage of prefixes) is needed to make sure the padding does not increase the width of the columns, or else it all falls apart.

That's it, it works :)

This is obviously not the only solution, something may suit you better in your situation. On another occasion I needed more complex rules for splitting items between columns, so had to write my own JavaScript code to handle the whole thing.

Let me know if you have any feedback / questions, and take care.

No comments:

Post a Comment