At some point in the redesign process of Arpia.be, I started to consider the idea of a "book-like" feel, where the content would be presented in two columns, and users could flip to the next page of content seamlessly.

The way I see it, this is something that has so far only been done using Flash, so it may be of interest to web designers & developers to see how they can achieve this without Flash, in a cross-browser compatible manner.

 

Basics: sources, …

The web page that helped me the most with this design process was an article on A List Apart: "Introducing the CSS3 Multi-Column Module", written by Cédric Savarese. In this article, Cédric not only explains the CSS3 multi-column module, but also the manners in which to emulate this behaviour using Javascript on browsers that do not support this CSS3 module. Cédric wrote a script in Javascript for this purpose.

There is, however, one issue with this article: the website to which it links, CSScripting.com, does not seem to respond (I've tried often). Using Google (or your favourite search engine), you can nevertheless easily find other websites using the script ("css3-multi-column.js").

These annotated files are used in this article:

 

The HTML - containers

This may sound ugly, but the best way I've found to implement this trick, after much trial & error, involves as many as 4 DIVs around the content.

First, the DIV you would normally have used for the content. Second, the first DIV required by Cédric Savarese's Multi-Column system (there must be a difference between these two, for reasons linked to the page turning effect - see below). Third, a DIV used for the transition between pages, for non-Internet Explorer browsers (this is linked to a fade effect, which doesn't work as smoothly in IE - see below for the page turning effect). Fourth, the second DIV required by the Multi-Column system.

The HTML doesn't look all that pretty, but I didn't want to change the JS code too much, hence the remains of the "one" and "two" classes from the Multi-Column system:

<div id="article">
	<div class="one" id="content">
	<!--[if !IE]><!--><div id="columniser" style="margin-left: 0px;"><!--<![endif]-->
		<div id="columnified" class="two">
			[put content here]
		</div>
	<!--[if !IE]><!--></div><!--<![endif]-->
	</div>
</div>

 

The CSS - all browsers

There is one major issue with the CSS3 multi-column module: so far, the browsers implementing it are Firefox (since v3), Safari (since v3.1) and Chrome (since v2). Neither Internet Explorer nor Opera support it.

- Source

We will therefore define general CSS rules for all browsers, and then add some stylesheets specifically for respectively Opera and IE, as the latter will use the Javascript method of multi-column, whereas the others will not.

The first element for you to think about is the total amount of content you want visible. In the case of Arpia.be, I wanted two columns of a height of 430px and of a width of 330px to be visible, with a separation of 60px, i.e. a total width of 720px.

In experimenting, I found it better to add some "padding-top" to the columns, for better display of certain typefaces with a higher baseline. The height of the containing DIVs therefore takes into account this "padding-top".

Non-annotated CSS:

div.one {
	height: 435px;
	width: 720px;
	overflow: hidden;
}
div#columniser {
	height: 435px;
	width: 720px;
	overflow: visible;
	opacity: 1;
}
div.two {
	column-count: auto;
	column-width: 330px;
	column-gap: 60px;
	-webkit-column-count: auto;
	-webkit-column-width: 330px;
	-webkit-column-gap: 60px;
	-moz-column-count: auto;
	-moz-column-width: 330px;
	-moz-column-gap: 60px;
	height: 430px;
	overflow: visible;
	display: block;
	padding: 0;
	padding-top: 4px;
}
#columnified {
	margin-left: 0;
}

See the annotated CSS here.

 

The CSS - IE & Opera

Because of the way the Javascript method for multi-columns work (see below), some additional CSS entries are necessary for Opera and IE. In addition, IE and Opera do not handle the multi-column Javascript in the same way, so it is necessary to have different CSS for each, in addition to the main CSS file.

Opera:

#article {
	width: 720px;
	text-align: justify;
	overflow: hidden;
}
div.one,#columniser {
	max-height: 435px;
	width: 15600px !important;
	overflow: hidden;
	margin: 0;
}
div.two {
	column-count: auto;
	column-width: 330px;
	column-gap: 60px;
	max-height: 430px;
	overflow: hidden;
	margin: 0 0 0 0px;
	padding: 0;
	padding-left: 0px;
	padding-top: 4px;
}
div.two .columns, div.two .wrapper {
	width: 330px !important;
	height: 430px !important;
}
.columns {
	padding-left: 0 !important;
	padding-right: 60px !important;
}

See the annotated CSS here.

IE:

I have only attempted to get this to work on IE7 and IE8, as IE6 would most probably require a whole lot more work.

#article {
	width: 740px;
	height: 470px;
	text-align: justify;
	overflow: hidden;
}
div.one {
	max-height: 435px;
	width: 15600px;
	overflow: visible;
	margin: 0;
}
div.two {
	column-count: auto;
	column-width: 330px;
	column-gap: 60px;
	max-height: 430px;
	overflow: visible;
	margin: 0 0 0 0px;
	padding: 0;
	padding-left: 0px;
	padding-top: 0px;
}
div.two .columns, div.two .wrapper {
	width: 330px !important;
	height: 430px !important;
}
.columns {
	padding-left: 0 !important;
	padding-right: 60px !important;
}
div#columnified {
	float: left;
}

See the annotated CSS here.

 

The Javascript - Adapting css3-multi-column.js

Now that those basic CSS properties are defined, we must examine the multi-column script, to see how it will be used in Opera and IE, before looking at how we switch between pages.

Cédric Savarese's script is brilliant at enabling multi-column layouts on browsers not supporting this CSS3 module. However, it was made with the idea of a fluid layout in mind, and one where all the content is visible. We, on the other hand, wish to hide some of the content.

There are some issues with the multi-page adaptation of multi-columns that led me to change certain elements within the code of the Javascript I use.

First, Cédric's script creates DIVs within an "available width" generally limited to the browser window's horizontal length. It is our aim, however, to have all these DIVs sitting right next to each other on a horizontal line, for as long as it takes to display all the content. It is therefore to fix the available width to an amount far greater than the browser window width. In the Javascript, the line is "var availableWidth = 15600;", but you can choose to make it far larger. In my case, it was because I did not believe I would ever go beyond 40 columns of text (at 330px for a column, 60px for the gap between columns).

A second important change was fixing the height of the columns within the Javascript. I found this to be necessary, as the reading of data from the CSS did not seem to force the Javascript to create the DIVs exactly as I wished them to be (see "var newHeight = 430;" in the script, az well as all the instances of "430", which force the value instead of "newHeight").

These values, "430" and "15600", are the two main points in my modified version of the script that you will want to change to your own values.

As regards height, another important change was to replace "findSplitPoint(elem, newHeight*i, wrapper);" in the "processElement" function with "findSplitPoint(elem, newHeight, wrapper);", to avoid the creation of DIVs greater than the newHeight limit (430px in my case), even if there are only 10px more to complete a tag (a "P", a "DIV", …).

Furthermore, whereas in Cédric's script, the containing DIV, the "wrapper", could have a random ID, in our case it is necessary to define this ID for the purpose of sliding it in Internet Explorer (see below for the page-turning effect). Therefore, 'if(!wrapper.id) wrapper.id = "columnified";'. On a similar note, to allow better control of the columns, it is useful to define a class: "elem.className = "columns";" (I also defined the wrapper class: "wrapper.className = "wrapper";").

Finally, at the top of the script, there is a list of "splitableTags". I expanded this list to contain table-related elements, because I use tables in certain areas.

You can find this adapted script here: css3-multi-column.js.

 

The HTML - Turning pages

Now that the infrastructure is there, it's time to work on allowing the user to go between pages, i.e. to slide the content so he/she can view the hidden columns.

We'll be using Javascript for this effect, so here is the HTML for calling the page-turning function:

<div class="nav">
	<ul class="arrows">
		<li id="previous"><a href="#" id="aprevious" onclick="changeColumns(1,'previous');return false;"><span>Previous page</span></a></li>
		<li id="next"><a href="#" id="anext" onclick="changeColumns(-1,'next');return false;"><span>Next page</span></a></li>
	</ul>
</div>

Why "-1" for "Next"? When the user clicks on "Next", we want the two visible columns to go left, to leave space for the next two columns. Because we're playing on "margin-left", as you'll see in the Javascript file, we want the value for "Next" to be negative.

 

The Javascript - Turning pages

To turn pages, one can proceed in several steps. First, we must ensure that when the user clicks on the "Next" or "Previous" button/link, the content moves, and it moves to the correct place. Second, to create a better experience, we can animate this change.

The essence of the sliding of the content is that we're playing on "margin-left" of the relevant DIV (if IE: columnified; if not IE: columniser - this is linked to my "fade out/slide/fade in" animation, which is sluggish on IE).

As there is lots of code involved here, I prefer to simply refer you to an annotated "pageturn" script for an in-depth look at the functions.

 

Serving the CSS and the Javascript

Finally, how do we combine all of this? We want Opera users to have the main CSS file and the Opera one, as well as the multi-column Javascript, but we don't want Safari users to have the additional Javascript, given that they have CSS3 Multi-Column support.

The solution is to feed different files to different browsers, using Javascript:

if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { //test for IE x.x;
	document.write("<!--[if IE 8]><link rel='stylesheet' type='text/css' href='http://www.arpia.be/public/wp/main.css' media='screen, projection' /><link rel='stylesheet' type='text/css' href='http://www.arpia.be/public/wp/ie_multicolumn.css' media='screen, projection' /><![endif]-->");
	document.write("<!--[if IE 7]><link rel='stylesheet' type='text/css' href='http://www.arpia.be/public/wp/main.css' media='screen, projection' /><link rel='stylesheet' type='text/css' href='http://www.arpia.be/public/wp/ie_multicolumn.css' media='screen, projection' /><![endif]-->");
	document.write("<!--[if lte IE 6]><link rel='stylesheet' type='text/css' href='http://universal-ie6-css.googlecode.com/files/ie6.0.3.css' media='screen, projection' /><![endif]-->");
	document.write("<script type='text/javascript' src='http://www.arpia.be/public/wp/css3-multi-column.js'><\/sc" + "ript>");
}
else {
	document.write("<link rel='stylesheet' type='text/css' href='http://www.arpia.be/public/wp/main.css' media='screen, projection' />");
}
if (/Opera[\/\s](\d+\.\d+)/.test(navigator.userAgent)) { //test for Opera;
	document.write("<link rel='stylesheet' type='text/css' href='http://www.arpia.be/public/wp/multicolumn.css' media='screen, projection' />");
	document.write("<script type='text/javascript' src='http://www.arpia.be/public/wp/css3-multi-column.js'><\/sc" + "ript>");
}

Given that having this script in the <head> element of your HTML is invalid in XHTML 1.0 Strict, I made it an external script, and put this in my <head> element:

<script type="text/javascript" src="http://www.arpia.be/public/wp/stylesheet.js"></script>

And… there we go, a fully functional multi-page, multi-column layout, applicable to any number of web pages, without the need to resort to Flash.

The only issue, as far as I can tell, is that it's impossible to determine in advance how long the content will be, and one cannot therefore avoid having the "Next" button still visible beyond the end of the content. My workaround, as can be seen on Arpia.be, is to add "easter eggs" for those who click beyond that limit.

Perhaps you can come up with another solution…