I want to display a multiple columns document in HTML. A multiple column "box" is restarted in print after a page break. I would like to emulate this behavior (page break) on-screen, so column heights should not be higher than the user-agent window's height.
Just using a new container for each virtual page would be easy but I don't know where exactly the screen will end so I want text to be able to flow between these virtual pages.
I'm aware that today's JS has the abilities necessary to pull this off, but I would like to use CSS as this is a layout problem.
CSS Multi-column Layout is a module of CSS that adds support for multi-column layouts.
The basic idea of multicol, is that you can take a chunk of content and flow it into multiple columns, as in a newspaper.
The W3C multi-column module is a CSS level-three working draft, proposed by the W3C to extend the current CSS box model. The module's intent is to allow content to flow into multiple columns inside an element.
I had precisely the same desire as described in this question. Here is an HTML/CSS-only solution that worked well enough for my purposes, and may be useful to others. It has a few potential drawbacks, which I'll describe before giving the code. In the discussion below, I use the word "subpage" for what the questioner called a "virtual page," namely one horizontal set of contiguous columns of content of the desired height (note the text continues from the bottom right of one subpage to the top left of the next subpage.)
OK, to the actual code. The basic strategy here is simple: Break the same text multiple times into columns of the desired subpage height. Stack the resulting horizontal sets of columns on top of each other, but move each successive copy exactly one subpage width page to the left. Finally, clip the whole stack (with overflow:hidden;
) to the width of the subpage. The key difficulty is getting that clipping div
to be the correct height. It would be straightforward if it were possible to CSS select all of the subpage units that end up with no ink inside them when they are clipped and then simply not display them, but I couldn't find a way to do this. The rough approximation I ended up with is to invisibly lay out all of the content one more time, in the desired subpage width and column structure, and use the height of that layout to determine the height of the clipping. However, that layout is always shorter, because it's more efficient to lay content out in a bunch of long columns than in successive blocks of short columns, and plus you need some kind of subpage separators or the subpaged layout becomes super confusing. So I fudged this by just throwing in a big bottom padding in that clipping div
. One could make a better approximation if CSS calc() allowed integer division or rounding. And finally, the whole task would be trivial if CSS Regions ever took off, but that's looking unlikely.
Sorry about the tl;dr; -- here's the code, html first, then CSS:
<div class="column-paginator">
<div class="vertical-strut mypage-h vertical-fuzz">
[COPY OF CONTENT HERE]
</div>
<div class="column-paginator-clipper">
<div class="mypage-h mypage-v column-paginator-0">
[COPY OF CONTENT HERE]
</div>
<div class="mypage-h mypage-v column-paginator-1">
[COPY OF CONTENT HERE]
</div>
<div class="mypage-h mypage-v column-paginator-2">
[COPY OF CONTENT HERE]
</div>
<div class="mypage-h mypage-v column-paginator-3">
[COPY OF CONTENT HERE]
</div>
<div class="mypage-h mypage-v column-paginator-4">
[COPY OF CONTENT HERE]
</div>
</div>
</div>
And now the CSS:
div.column-paginator {
position:relative; /* Ensure we can anchor positions to this element */
}
div.vertical-strut {
visibility: hidden; /* Only laying this out to get its height */
}
div.vertical-fuzz {
padding-bottom: 300px; /* extra height because unpaginated text
* takes less vertical height. This is a
* crude measure. */
}
div.mypage-h {
/* The desired horizontal setup of the column pagination */
columns: 2 200px;
column-gap: 2em;
}
div.column-paginator-clipper {
/* The div that actually constrains the output */
position: absolute;
top: 0px;
left: 0px;
height: 100%;
max-height: 100%;
overflow: hidden;
}
div.mypage-v {
/* the desired vertical setup of the column pagination */
height: 300px;
position: relative;
overflow: visible;
margin-bottom: 1.5em;
}
div.mypage-v:after {
/* Automatically throw in subpage separator. */
content: "";
height:1em;
position: absolute;
left: 0;
top: 100%;
width:500%; /* must be MAX_SUBPAGES * 100% */
background: lightcyan;
}
/* Note the 2em below is precisely the column-gap above; not sure how
* to enforce/encode that equality in CSS.
*/
div.column-paginator-0 { left:calc(0 * (-100% - 2em)); }
div.column-paginator-1 { left:calc(1 * (-100% - 2em)); }
div.column-paginator-2 { left:calc(2 * (-100% - 2em)); }
div.column-paginator-3 { left:calc(3 * (-100% - 2em)); }
div.column-paginator-4 { left:calc(4 * (-100% - 2em)); }
div.column-paginator-5 { left:calc(5 * (-100% - 2em)); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With