Thursday, March 09, 2006

How hard could it be?

Imagine that you have a 18px wide by 1px high tileable gradient png/gif. You also have a horizontally centered div holding your content. You want to tile the 1px high image along the vertical length on both sides of your div. I found myself in exactly such a situation only a few days ago. I could've used a large tile, say like a 6000 px by 1 px image and tiled it in the body's background, like how gamespot.com did it. But, my gradient is double-edged. Meaning, the 18px gradient goes from completely non-transparent around middle to 100% transparent in the left and the right ends. This is done so that I can overlap one part of the gradient over the content while the other part hangs outside the content. The final effect is shown farther below. Come back up after you've seen it.

For those who didn't get the few words above, here's a thousand words of the same:
The 'double-edged' gradient, enlarged.

The image is scaled 1000 times proportionally. So if you want to use them, just scale them down and use it. The center is around 10px. This is the left-side shadow. For the right side one, just flip it horizontally. You then need to find a way to tile the image vertically along and over both sides of your content. For this, I used floated divs. It had to be a div as I had swore to keep tables out of my XHTML life. So, I added two floated divs with IDs "shadow-left" and "shadow-right". Here are the HTML structure and the css:


<body>
<div id="main">
<div id="shadow-left"></div>
<div id="header1"></div>
<div id="menu"></div>
<div id="content">
<hr id="footer" />
<div id="shadow-right"></div>
<hr class="clear" />
</div>
</body>
The HTML structure



#shadow-left {
margin: 0 auto 0 -9px;
position:absolute;
min-height:100%;
height:100%;
left:5%;
top:0px;
width:18px;
background:url(shadow-left.png) repeat-y;
}


#shadow-right {
margin: 0 -9px 0 auto;
position:absolute;
height:100%;
min-height:100%;
right:5%;
top:0px;
width:18px;
background:url(shadow-right.png) repeat-y;
}

/* \*/

* html #shadow-left {
display: none;
}

* html #shadow-right {
display: none;
}

/* */

My sloppy CSS



You might've noticed from the last few lines that I had decided to hide the neat transparent gradient thing from Internet Explorer. This is because among the web image formats, only in png can a pixel have a 24bit color value as well as an 8bit alpha value. Gif do support alpha value, but in gif a pixel is either 100% transparent or not. This is not the only place I had to account for IE's ignorance. IE has to be constantly kept from it's own ignorance, missing features, incomplete standards conformance, buggy implementation and other asprin-sellers. If ever I get hold of the neck of the person responsible for CSS implementation in IE 5 (or 5.5, 6.0, 5.5 for mac), I won't hesitate to close my hand. CSS is a very easy and neat language to learn and code. But IE takes all the fun and intuitiveness out of it. How hard could it be to read the CSS specification and implement it? That's such a monky job, or a no-brainer. Suddenly, though I never liked them, all those 'Promote Firefox' and 'Promote Opera' campaigns seem to make sense from a developer perspective. Of course, all browser implementations have bugs. Richinstyle.com lists all bugs. But I don't care as long as I don't notice them.

Switching to the thousand words mode, here's a lame website with no gradients attached:
A lame page with no gradients

...and here's the same lame page with glitzy gradients attached:
The lame page with extra fancy gradients

So, the above CSS + XHTML combination will work as promised and you get a gradient the height of the page. Or do you? What you get is a gradient that extends only to the height of the viewport/browser window. Meaning, if your page has a vertical scrollbar, then scrolling down will reveal that the newly uncovered portion was not covered by the gradient. What trick is this? Actually, it seems that it is the specification.

While searching around, I found that if you set the heights of all the parent elements (here, they are #main, #body, #html), then your gradient will have a hight equal to the height of the entire page. It didn't work for me and worse, making #main's "height:100%" messed up the already messed up display in IE still more. So I went into hack mode and used javascript to do a little trick. Here's the simple javascript I conjured up:


<script type="text/javascript">
<!--
function doOnScroll()
{
document.getElementById('shadow-right').style.top = window.scrollY + "px";
document.getElementById('shadow-left').style.top = window.scrollY + "px";
}

window.onscroll = doOnScroll;
//-->
</script>
The javascript hack



Javascript allows you to hook onto the onscroll event. This is the event that happens everytime you scroll down/up/left/right. So I just shift the gradient div's 'top' value to the scrolled value of the window. This trick, of course, requires your visitor to have javascript enabled and have Mozilla Firefox. There will be a very small and, hopefully, unobstrusive flicker near the divs as the divs gets realigned. I myself want to use some other trick for my site as, as google analytics points out, more than 78% of the visitors browse my site on IE. I remeber those <table> times when webpages had the "Viewed best in IE" in their footers. With the <div> times, the tables (pardon the pun) might've been turned.

[Edit]
Here's a spot of trouble I ran into. Though I was smart enough to hide the translucent gradient flanges from IE, I forgot to turn off the javascript that moved the gradients as the user scrolled the webpage. It is necessary to hide the javascript too from Internet Explorer as otherwise a script error occurs for every tick the scroll bar scrolls, which is pretty lame. So here's the same javascript hack as above, but only nicely wrapped inside a IE proofed if block:


<script type="text/javascript">
<!--
if (navigator.userAgent.toLowerCase().indexOf('msie') == -1)
{
function doOnScroll()
{
document.getElementById('shadow-right').style.top = window.scrollY + "px";
document.getElementById('shadow-left').style.top = window.scrollY + "px";
}

window.onscroll = doOnScroll;
}
//-->
</script>
The javascript hack



What I had done is pretty simple. Every browser has a userAgent property that contains everything that the browser has to say for itself, including it's version, name, operating system and maybe a few other stuff. I just check for the occurence of the string 'msie' in the userAgent property of the browser being rendered on, and if there be no such vile term in the user agent name, we get on with the trick show.
[/Edit]

No comments: