Dynamic CSS pseudo class styles with jQuery
On Glogster we recently made a new little Flash widget to allow users to “skin” their profile pages – allowing them to change the colour of text, backgrounds and so on to make their part of Glogster more personal.
It works by saving all the definitions (i.e. div.p = #ff0000, etc) to the database and then generating a custom CSS file which is loaded on the profile page to override the default style. We also needed a “preview” mode whereby the changes made in the Flash widget would be immediately visible on the page to allow the user to see what the page would look like before saving the skin.
No problem, I thought, when I first saw the brief. We can use jQuery to easily change styles dynamically. Indeed we could, but not pseudo classes for things such as :hover on links. I did a bit of searching and I couldn’t find a way of doing it using jQuery, so I made one.
I decided on an object oriented approach as I needed a way to keep track of what style properties the Flash widget has sent and update definitions accordingly. Using an OO approach allowed me to model components of a stylesheet individually thus allowing client code to construct a new stylesheet by use of these components. For example if the Flash widget sent a “color” definition for the CSS selector “div#main p”, an then sent another definition, let’s say “font-size” for the same selector, then I would need some way to group these definitions together. So I made three classes:
Contains a property and value pair for example “color” and “#ff0000″.
Contains StyleSheetElement objects for a given CSS selctor, for example “div#main p”.
Contains StyleSheetTag objects. Orders the StyleSheetTag objects and creates CSS output.
On to an example:
// Create a new StyleSheet instance var sheet = new StyleSheet(); // Create a CSS element to set colour to green var elementBasic = new StyleSheetElement("color", "#00ff00"); // Attach the CSS element to the CSS description for "a" var tagA = sheet.addElementToTag("a", elementBasic); // Set the order of the tag for "a" tagA.order = 2; // Create a CSS element to set colour to red var elementHover = new StyleSheetElement("color", "#ff0000"); // Attach the CSS element to the CSS description for "a:hover" var tagHover = sheet.addElementToTag("a:hover", elementHover); // Set the order of the tag for "a:hover", will appear beore "a" tagHover.order = 1; // Create a stylesheet for the new elements and add it to the page (replaces it if one already exists) addInlineStyleSheet(sheet);
The first step is to create a SyleSheet object that will contain the style definitions and create the CSS.
The second step is to create a StyleSheetElement for the property you want to set and the value you want to set it to.
Next, you need to add that element to the StyleSheet object along with a CSS selector. If the StyleSheet does not already contain a tag for the given CSS descriptor then it will first of all create one, then it will see if the tag already contains an element for the same property as the element being added. If it does then the new element replaces the old one, otherwise the new element is just added to the tag.
You may notice that StyleSheet::addElementToTag() returns the tag that the element was added to. This is useful if you want to set the order of the tags. Sometimes you will have tags that need to appear higher up in the resulting CSS, set the StyleSheetTag.order property to achieve this, the smaller the number the nearer the top it will appear.
The final stage is to generate the CSS and add/replace it in the page. To do this, just call addInlineStyleSheet() and pass it the StyleSheet object you’ve just made.
What about Internet Explorer?
Of course, IE managed to throw a spanner into the works by not supporting changing inline stylesheets. Well, it does support them in that it’s possible to change the actual HTML, but it has no effect on the rendering of the page. Great. So I found a workaround, if the browser is a flavour of IE, then the addInlineStyleSheet() function compiles the StyleSheet object not to CSS but to a list of tags and CSS rules which are then applied in turn by calling document.styleSheets.inlinestyle.addRule().
Shout me a coffee
If this article has been useful to you and you want to say “thanks”, then why not shout me a coffee by donating through PayPal?