Introduction
Just about every web developer has been there: you try to be a good CSS purist, you eschew (mis)using tables for page layout, and you just want some child element to center vertically and horizontally within its parent, or respect a percentage-based size under adverse conditions, and maybe vertical-align
refuses to work unless you use fixed measurements for line-height
or a parent dimension. You try setting height
and min-height
, you fiddle with margin
and padding
, you try some IE-specific hacks, you make sure you've set the height on your html
and body
elements to 100%... you think you have it, then you preview it in 7 different browsers and now it's completely b0rk3d in one where it worked before. This is a solution which discards all finesse and brings a sledgehammer to the playground; instead of being clever or elegant, it simply beats every browser into submission when it comes to rendering the damn thing the way you want.
Approach
The overall goal here is simple: Visually simulate percentage units in CSS using fixed (pixel) measurements. There are obviously several ways to skin this cat, and several choices to make. You could adjust everything every time the browser window gets resized (I didn't). You could do the entire thing in javascript and skip ASP.NET completely. I chose otherwise because I like having the viewport dimensions available on the server-side, e.g. for cases where there's custom rendering happening there. No matter what you do though, you're going to need at least some JavaScript to get those dimensions effectively. Then you'll want to hand them to the server, which should respond with a stylesheet containing measurement units custom-tailored to the browser's dimensions.
About the code
Thanks to Andy Langton's article at http://andylangton.co.uk/articles/javascript/get-viewport-size-javascript/ , you can easily get the size of the browser's viewport with one big ol' line of javascript:
var win = window, d = document, e = d.documentElement,
g = d.getElementsByTagName('body')[0],
x = win.innerWidth || e.clientWidth || g.clientWidth,
y = win.innerHeight || e.clientHeight || g.clientHeight;
Now you have the width in the x
variable and the height in the y
variable, both of which you can feed to the server using your personal method of choice -- e.g. an Ajax call, hidden field, etc. I just write in a link
element pointing to my dynamic CSS handler, and tack the x
and y
values on as query string parameters:
document.write("<link rel=\"stylesheet\" type=\"text/css\" href=\"/css/DynHeightCSS.ashx"
+ "?w=" + x
+ "&h=" + y
+ "\"" + " />");
This adds a stylesheet link to the page with a URL like this:
http://localhost:8080/css/DynHeightCSS.ashx?w=1920&h=1012
.
The dynamic CSS handler (DynHeightCSS.ashx) calculates pixel dimensions for every percent value of the given browser dimensions, and embeds these into custom classes for setting height
, line-height
and width
. For example, the first three classes in the generated response -- all representing 1% of height or width, respectively -- look like this (given my current browser dimensions of 1920 x 1012):
.height-1-pct {
height<span class="code-none">: 10px<span class="code-none">;
<span class="code-none">}
.line-height-1-pct <span class="code-none">{
height<span class="code-none">: 10px<span class="code-none">;
<span class="code-none">}
.width-1-pct <span class="code-none">{
width<span class="code-none">: 19px<span class="code-none">;
<span class="code-none">}</span></span></span></span></span></span></
Here's an example of where you might want to use this tactic. The
white box below is 45% of the page height, and you want to vertically
center a span of text within it. Simply setting vertical-align: middle
doesn't do anything (right), even with height
or line-height
set to 100%:
In the example (image above, markup below), the text block on the right is incorrectly aligned vertically. The only difference between that and the correctly aligned one (left) is the class
attribute of height-45-pct
(provided by our dynamic CSS) added to the left span. We know the white
box is 45% of the page height, and we want our text block to be the
same height -- but measured in pixels -- so we just use the
dynamically-generated class corresponding to that height:
<div style="width: 50%; margin-top: 4px; float: left; text-align: center;">
<span class="height-45-pct" style="display: table-cell; vertical-align: middle;">
This text is supposed to vertically <br>align middle. The height is 400px.</span>
</div>
<div style="width: 50%; margin-top: 4px; float: left; text-align: center;">
<span style="height: 100%; display: table-cell; vertical-align: middle;">
So is this text, but line-height 100% <br>doesn't work quite the same.</span>
</div>
Using the code
To use the code, simply download DynHeightCSS.ashx.cs, add it to your /css directory as an HTTP handler, and drop the aforementioned javascript into the head of your page. Then when you need to force an incorrigible element to obey a percentage measurement, use one of the dynamically generated classes to do so. NB -- percentages are always relative to the page, not the containing element; you'll need to do some math here & there to convert to page-relative fractions.
Points of Interest
There are obviously other use cases besides height
, line-height
and width
where a brute force approach like this could be useful. Min and max heights come to mind. Having a percentage-to-pixel calculation in your CSS could free you up to use absolute positioning where it would otherwise be difficult to.
This is a first-draft proof-of-concept, not a fully fleshed out utility. I use it in production code, but I'm sure there are drawbacks that I haven't run into and you will. It is a bit bloated for sure, so it might be slimmed down a bit by only outputting the sizes and attributes you actually use. Some embellishments could also come in handy; for instance, if the javascript function removed and replaced the stylesheet <link> element when the browser was resized, or if it even even just changed the styles on the fly if the whole thing were done in JavaScript. Leave your thoughts in the comments, and I hope this has been useful!
History
First version posted 12/31/2012.