1
Feb.

Tutorial: Multi-page, multi-column web pages

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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.

Our aim? To use CSS3 native support for multiple columns on the most "advanced" browsers (here: Safari, Chrome and Firefox - Opera has since also joined the party), and to find a backwards-compatible technique for the others (in this case, Internet Explorer).

 

Basics: sources, …

These annotated files are used in this article:

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").

 

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), Chrome (since v2), Opera (since v11). Internet Explorer only supports it since IE9, so we need to have alternative solutions there.

- Source

We will therefore define general CSS rules for all browsers, and then add some stylesheets specifically for IE, as the latter will use the Javascript method of multi-column, whereas the others will not (I haven't yet tested the general method for IE9+, but if it works, you could make the Javascript only apply to IE7 & IE8).

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;
	width: auto;
}

See the annotated CSS here.

 

The CSS - IE

Because of the way the Javascript method for multi-columns work (see below), some additional CSS entries are necessary for IE.

IE:

Disclaimer: I have only attempted to get this to work on IE7 and IE8, as IE6 would most probably require a whole lot more work (I found that IE6 crashed using the current, modified multi-column JavaScript - I did not seek to find the source of the issue).

#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;
	width: 15540px;
}

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 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>

In the function "changeColumns()", the first value shows the direction, while the second is the title of the ID of the element clicked.

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 the animation).

For my website, I chose to do a "fade out/slide/fade in" animation, but found that it was sluggish on Internet Explorer. This is the reason for which IE users do not need the "columniser" DIV.

Other animation choices are possible: you may choose to fade out the entire content and show a page-turning effect, if you have a book design. This is what I did for a "book" proof-of-concept design on which I worked earlier (here, it seems as though the user is actually flipping through pages of a book).

Here are the three sets of functions (4 functions in total) we shall be using:

changeColumns()

In our HTML for turning pages, we defined the function as "changeColumns()", with two values: first, a value showing the direction, and in second instance the ID of the element clicked.

animateSlide()

animateSlide(), as its name indicates, will animate the transition between two pages by sliding the content. As was seen previously, IE users are not faced with this function, because of the transition lag it causes.

setOpacity() and fade()

The two last functions relate to the fading in/out of the content. These are very standard, and work in all browsers (through the use of "filter" for IE - I have retained this here for the sake of completeness, but it is not used, as IE users do not see the fade animation).

Full pageturn.js

As the JavaScript involved is long, I have not reproduced it in this post. You may view an annotated "pageturn" script to see the functions with explanations for each step.

 

Serving the CSS and the Javascript

Finally, how do we combine all of this? We want IE users to have the main CSS file and the IE 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='main.css' media='screen, projection' /><link rel='stylesheet' type='text/css' href='ie_multicolumn.css' media='screen, projection' /><![endif]-->");
	document.write("<!--[if IE 7]><link rel='stylesheet' type='text/css' href='main.css' media='screen, projection' /><link rel='stylesheet' type='text/css' href='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='css3-multi-column.js'><\/sc" + "ript>");
}
else {
	document.write("<link rel='stylesheet' type='text/css' href='main.css' media='screen, projection' />");
}

Note: the reason for which the closing "script" tags are split is because they break HTML code if kept intact.

As you can see, IE6 is given the "Universal IE6 CSS" file, but you are free to define a different stylesheet for these users.

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>

Then, the "pageturn" script must go in the footer:

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

 

Adding page numbers

It may be useful in some cases to add page numbers. In my book demo, I achieved this by adding the following line in the HTML to "div class nav", right before "ul class arrows":

<div class="pagenumbers"><span id="pageleftnumber">1</span><span id="pagerightnumber">2</span></div>

Next, in the CSS, I added the following lines:

.pagenumbers {
			position: absolute;
			width: 630px;
			z-index: 1;
			top: 30px;
			left: 80px;
			font-size: 1em;
			letter-spacing: normal;
			font-weight: normal;
			color: #000;
		}
			.pagenumbers #pageleftnumber {
				float: left;
			}
			.pagenumbers #pagerightnumber {
				float: right;
			}

In the "pageturn.js" Javascript file, the following bits of code needs to be added twice:

leftPage = (changeValue * -2) + 1;
rightPage = (changeValue * -2) + 2;
document.getElementById('pageleftnumber').innerHTML = leftPage;
document.getElementById('pagerightnumber').innerHTML = rightPage;

It goes right after the following line:

document.getElementById("columnified").style.marginLeft = newValue + 'px';

… and right before the following one (or its equivalent, basically the "fade back" animation):

fade('columniser', 50, 100, 500);

Finally, please note that it may be necessary to adapt the IE-specific CSS stylesheets.

 

Closing comments

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. See a demo, without all the graphics of Arpia.be and using these files only, or a "book" proof-of-concept design on which I worked earlier.

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…

Last updated on 26 August 2013.

Comments

17
Nov.

Nz aung:

Hi, this is very cool

30
Nov.

jinsun:

cool!

29
Dec.

Michael Blankenberg:

Great tutorial!
But how do I change the font-size? Wherever I giva a style font-size:12px, text fail to appear in the second column in IE…

29
Dec.

Peter Craddock:

Dear Michael, could you update your test website with the code in question? Then I’ll be able to take a look at what might be the problem.

23
Feb.

Ben Bunch:

HI,

Great tutorial! I have looked at a lot of the columnizing scripts and this combo is really versatile and portable. However I am having a small adapting is to a site. Is the value of the padding-right directly related to the value of the column gap? I am keeping them the same but finding that the page turning doesn’t line up correctly.

23
Feb.

Peter Craddock:

Dear Ben,

I seem to recall that the padding-right must indeed be the same… If you post the link to a test website, I’ll see whether I can help out.

23
Feb.

Ben Bunch:

Thanks Peter,

I appreciate any advice you have.

Here’s my link:

http://bunchwebdevelopment.com/adhocvox/column_paginate.html

23
Feb.

Peter Craddock:

Dear Ben,

The issue stems from the fact that you are calling up the CSS in your “” section through a style declaration on top of calling it up through the stylesheet.js file. This means that all browsers get served the IE-specific and Opera-specific code, which gives awry results.
In theory, if you remove that code (just keep the Javascript reference – remove the style links), it should already make things much better.

25
Feb.

Ben Bunch:

I got you now. It is working great. Thanks for your support on the script. Will pass this one on.
Cheers!

1
Mar.

Ben Bunch:

Hi Peter,
You don’t happen to know of any instances where this JS and CSS is used twice on the same page. Each section with a unique column width and height. Would if be just a matter of changing the ID and Class references in both css and js files?

2
Mar.

Peter Craddock:

Good question… I’ll see whether I can’t play around with the JS a bit this weekend.

5
Mar.

Milan:

Commented by Milan

[Admin-edit: care to further elaborate?]

6
Mar.

Peter Craddock:

Dear Ben,

I have modified my “CSS3-Book” to show how two sections can be multi-column/multi-page (the top one uses a page flip animation, while the lower one uses a slide animation).

I haven’t yet got it to work properly in Internet Explorer, though.

In Opera, the code is very strange, but it works: I have two css3-multi-column.js files, each mentioning a different size, but pointing towards the same container ID (“columnified”). Normally, it should be “columnifiedtwo” for the second group of elements.

I guess I’ll have to delve in deeper into the Javascript to figure out how to get IE to work, and to figure out why Opera behaves that way.

7
Mar.

Ben Bunch:

Thanks Peter,

Really appreciate the retooling for multiple layouts. I’ll see if I can get this shaped to my needs.

Cheers,
B

21
Mar.

Chris:

Hi, first, great tutorial! It doesn’t seem to work in Chrome, though. Have you noticed this issue?

21
Mar.

Peter Craddock:

Well, so it is. I do not recall encountering that beforehand… maybe Chrome’s behaviour changed? I have fixed the issue on this website by declaring a large width for “columnified”, and it still works in Safari, Firefox & Opera. I’ll be posting the modifications into the tutorial and other websites using the code.

Thanks Chris for pointing that out!

13
Apr.

K1GP:

Great tutorial!I use it in firefox4 and chromium12 and it work greatly.
Opera11.10 supports CSS3 multiple columns but still have a problem.It can’t handle huge content(more than 75,000 characters).

28
May.

Peter Craddock:

Small update: pagination added. See last pages of the tutorial.

29
May.

Shrimpsoup:

Peter, Thanks for update, I Iike..it. Very usefull for me.

30
Sep.

Herman van Gestel:

Great Article clear:
some points to improve?
a) a certain code to move the start of an article (or section/title ) to the start of a new column

b) a balanced version? if needs be for articles that would fit in less than a page?

just some little suggestions…otherwise just great!

16
Apr.

Kevin Kraemer:

pages look great on the screen. Is there a way to adapt it for printing so that the columns are maintained but break correctly on printed pages?

18
Apr.

Peter Craddock:

Hi Kevin,
I haven’t given thought to that. I’ll take a look and see what I can find.

13
Nov.

Ciaran:

Great work and excellent tutorial, thanks.
Is it possible to define the number of pages the page increment will occur?
The demo page number stops at 20….

Leave a comment