This post is a continuation of the "
How Many Pixels in an EM?" series I have been working on, although now that I know exactly how to calculate the number of pixels in an em, I've moved on to refining other aspects of the expression itself, and so I've decided to move away from that title.
I contacted
Svend Tofte himself regarding this work, and he took time away from his very busy schedule to give me some very valuable feedback (many thanks to Svend!). He pointed out to me that expressions that may work well in quirks mode can cause the browser to crash in strict mode, due to differences in the box model than can trigger infinite loops.
As I am no stranger to danger, I slapped a strict doctype on my test page and... voila... brought my mighty workstation to its knees. CTRL-ALT-DELETE did nothing. I actually had to manually switch off the box for the first time ever.
As soon as I could power back up, I removed all margins, padding, and borders from my test elements, loaded the page, and started resizing my browser like a madman. The divs scaled perfectly this time... No locked-up IE, no smoke coming off my CPU, just good old-fashioned max-width joy.
To make this long story short (or at least a reasonable length) I did some investigating and found that you have to factor in the total width of your margins, padding, and borders in order to use the expression safely in strict mode. Since you're testing the offsetWidth and then setting the CSS width property, you have to compensate for
the differences between the two. Otherwise, it is possible for the expression to enter an infinite loop where setting the width of the element causes the parent element to change its size, which again fires the expression, and so on.
Without further adieu, here is the expression in all its hairy glory:
width:expression((this.parentElement.offsetWidth - (parseInt(this.parentElement.currentStyle.paddingLeft)+parseInt(this.parentElement.currentStyle.paddingRight)+parseInt(this.parentElement.currentStyle.borderLeftWidth)+parseInt(this.parentElement.currentStyle.borderRightWidth)))
> ( Math.round((30*(screen.deviceXDPI?screen.deviceXDPI:96)/72)*parseInt(document.body.currentStyle.fontSize)) + (parseInt(this.currentStyle.marginLeft)+parseInt(this.currentStyle.marginRight)+parseInt(this.currentStyle.paddingLeft)+parseInt(this.currentStyle.paddingRight)+parseInt(this.currentStyle.borderLeftWidth)+parseInt(this.currentStyle.borderRightWidth)))
? "30em":"auto");
Quite a beast, no? Pretty it's not, but it works really well in strict mode, in IE 5, 5.5, and 6. With any combination of margins, borders, and padding you care to throw at it, and at any screen resolution. Don't believe me? Take a look at my
test page.
There are two places where you have to specify the desired max-width. Just replace the number 30 with the number of your choosing. You can also replace "auto" with another value, but I would be very careful when doing so. If that value should somehow evaluate to greater than your max-width value, I would expect to start seeing flames shooting out of your USB ports. ;) (ok, so maybe I'm being dramatic).
I'll be testing variations on this expression in the near future, so stop back and check out my progress. Also, please check out
the test page and let me know how it works for you!