So, apparently it would be nice to
be able to do certain things with CSS.
Traditionally, these sorts of
things are done on the server side using a server side language (like PHP) that generates CSS every time
you hit the page. Wouldn't it be nice to do it on the client side? As the page loads and after, as the user
interacts with it?
I think we can break down the usefulness of the technique into three categories:
- Programmatic CSS
- Browser Compatibility
- Custom CSS Properties
With programmatic css, you can use loops to generate CSS that might have taken pages to type out. You
can have CSS constants. You can do all sorts of math and calculations for a property. You can also
modify the style of elements on the fly, without using javascript on each element. I'm sure there's other
cool stuff you can do that I didn't think of.
Browser compatibility is sort of self-obvious. Before, you used to have a style sheet for each browser
to allow for their quirks and weird CSS implementation. Now you can generate CSS that is browser specific
using JS. The class is built to allow you to extend it for any browser and any property. You can specify
a property to be limited, in which case it will only generate it for the browser you specify.
Ever wanted to invet your own CSS property? Now you can. See the examples for what I mean.
All of these examples are already implemented on this page, so you can take a look at the source code to
see the actual code used.
Let's start off with something simple.
Let's make this sentence bold.
Let's make this sentence big.
Let's make this sentence bold and big.
html
<span class="bold">Let's make this sentence bold.</span>
<span class="big">Let's make this sentence big.</span>
<span class="bb">Let's make this sentence bold and big.</span>
javascript
var css = new CSS();
css.add_rules({
'span.bold, span.bb':
{
'font-weight': 'bold'
},
'span.big, span.bb':
{
'font-size': 'x-large'
}
}).refresh();
So what's the big deal, right? I can do that with regular CSS. Yeah, but try
removing the classes entirely
from the page without touching each element and keeping the other classes intact. Now the sentences are back to normal.
html
<span class="a" onclick="css.remove_rules(['span.bold, span.bb', 'span.big, span.bb']).refresh();">removing the classes</span>
Now that we're getting somewhere, let's imagine for loops, constants, math, etc. all based on user
behavior, browser, layout choices, etc. All of it dynamic.
Let's take a look at a practical example. Take a look all the way at the bottom of the page. See those
badges? Aren't they pretty? They're actually just one image used as a background on anchors. You don't
want to hammer your server for a dozen images. It slows things down for the user and on the server ...
but if you have a lot of images that you combine into one to display partially using the background
property, that will require a lof of CSS. You need to define the offset for each anchor.
That sucks. Let's try this:
javascript
var url = '/_library/image/badge/credits.gif', badges = '', hover = '', rules = {};
for(var i = 0; i != 8; i++)
{
var badge = (badges ? ', ' : '') + 'A#badge_' + i;
badges += badge;
hover += badge + ':hover';
rules['A#badge_' + i] = {
'background': 'url(' + url + ') -' + (i * 80) + 'px',
'float': (i < 3 ? 'left' : 'right'),
'margin': (i < 3 ? '0px 8px 0px 0px' : '0px 0px 0px 8px')
};
}
rules[badges] = {
display: 'block',
opacity: 0.50,
width: '80px',
height: '28px'
};
rules[hover] = {
opacity: 1.00
};
css.add_rules(rules).refresh();
If we take a look at the code, we're not only incrementing the background property by 80 pixels on each
loop, but we're also setting the margin and the float property based on what iteration we're on, so
that everything displays properly. We also set the hover property so that the opacity changes when you
hover over the anchor ...
Speaking of which, let's talk about opacity. If you use the class without any changes, opacity will work
fine in Firefox, Safari, and Opera ... but it won't work in IE. IE doesn't have an opacity property. You
need to use a filter to get the same effect. So let's add this anywhere in the code before we use the class:
javascript
CSS.implement({
'trident_opacity': function(value, property)
{
return ['filter', 'alpha(opacity=' + (value * 100) + ')'];
}
});
Now that we added the code, any time that we specify the opacity property, our class will properly generate
the opacity css for all browsers.
The format we use here is the engine name + '_' + property name. The class will default to whatever you define.
Obviously, your function should spit back an array, with the property at the first index and the value at the second.
Now that we're done picking on IE, let's pick on Firefox and Safari. The good: they defined the CSS3 property
for making things with rounded corners. The bad: they did it by using proprietary extensions. This gets a bit more
complicated:
javascript
CSS.implement({
local:
{
limited: ['border-radius']
},
'gecko_border-radius': function(value, property)
{
return ['-moz-' + property, value];
},
'webkit_border-radius': function(value, property)
{
return ['-webkit-' + property, value];
}
});
All we're doing here is sticking their proprietary '-webkit-' or '-moz-' before the property. We no longer
have to worry about that garbage since the class will take care of it for us and we can use the property 'border-radius'.
Notice that we added 'border-radius' to the limited array in the class. That's to identify the 'border-radius' property
as limited to a select few browsers. IE and Opera don't have this property, so we don't want to define it for them.
Any property inserted into the limited array that doesn't have a function explicity defined for the browser being used
will not be inserted into our stylesheet, which is just the way we want it. Remember we didn't insert the 'opacity' property
into the limited array ... that's because it is defined for every browser, in some form.
You can actually end up building a compatibility library using this small class. To get started, take a look at:
Now that we're sick of compatibility, let's move on to inventing our own properties. I normally have my dpi set to 120
since I use a high resolution monitor. IE seems to scale text to a larger setting when you set a higher dpi. That's nice
and makes things readable, but my layout will look completely different on other browsers. So let's try this:
javascript
CSS.implement({
local:
{
limited: ['dpi']
},
'trident_dpi': function(value, property)
{
return ['font-size', ((96 / window.screen.deviceXDPI) * value).round() + '%'];
}
});
I can use the function by
css.add_rule('body', {dpi:100}).refresh() and now my text will look normal in IE.
There are more fun things you can do with the class, but this should get you started. External methods for the class are
destroy, refresh, add_prop, add_rule, add_rules, remove_prop, remove_rule, and remove_rules. You can figure out how to
use all of them pretty easily by looking at the code.
Just one for now. There is an option for the class called 'rules' that you can use like this:
javascript
var css = new CSS({
rules:
{
"BODY, INPUT, SELECT, TEXTAREA":
{
"dpi": 100
},
"SPAN.a IMG, DIV.window_lucid IMG, DIV.veil":
{
"opacity": 0.50
},
"A SPAN, DIV.translucent":
{
"opacity": 0.75
},
"SPAN.a IMG:hover, DIV.window_lucid IMG:hover":
{
"opacity": 1.00
},
"A SPAN":
{
"border-radius": '5%'
}
}
}).refresh();
Kind of makes everything look more like a stylesheet. Also, try to put everything as high in the page as
you can in a single block, before you define your static CSS. This way the browser doesn't have to
refresh the content multiple times. Use
refresh() as few times as possible.
Need to contact me for some reason? Have a suggestion? Something is broken? My name is Marat Denenberg
and you can reach me at maratd@gmail.com