Home > Javascript > Dynamic CSS pseudo class styles with jQuery

Dynamic CSS pseudo class styles with jQuery

November 10th, 2009 Nick Leave a comment Go to comments

The problem

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.

The solution

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:

StyleSheetElement
Contains a property and value pair for example “color” and “#ff0000″.

StyleSheetTag
Contains StyleSheetElement objects for a given CSS selctor, for example “div#main p”.

StyleSheet
Contains StyleSheetTag objects.   Orders the StyleSheetTag objects and creates CSS output.

The implementation

On to an example:
http://www.4pmp.com/examples/javascript-dynamic-pseudo-classes/test.html

    // 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().

Download

StyleSheet.001.js

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?


Thanks!

  1. clive
    November 22nd, 2009 at 17:38 | #1

    hi – I am passing a parameter to fn which is fine
    $(create_sheet(“li”));

    I would like to add $(create_sheet(“a”));
    so both apply – is this possible?

    code below….

    function create_sheet(whichtag) {
    var sheet = new StyleSheet();

    // Create a CSS element to set colour to red
    var elementBasic = new StyleSheetElement(“color”, “#000000″);

    // Attach the CSS element to the CSS description for “a”
    var tagA = sheet.addElementToTag(whichtag, elementBasic);

    // Set the order of the tag for “a” whichtag
    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(whichtag + “: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);
    }

  2. November 22nd, 2009 at 21:31 | #2

    If you move the instantiation of the StyleSheet object outside the function then it will be in the global namespace. This will allow you to consecutively call the function with different tags, updating the same StyleSheet object and therefore not overriding the previous style each time you call the function.

    Something like:

    var sheet = new StyleSheet();

    function create_sheet(whichtag) {

    // Create a CSS element to set colour to red
    var elementBasic = new StyleSheetElement(“color”, “#000000″);

    // Attach the CSS element to the CSS description for “a”
    var tagA = sheet.addElementToTag(whichtag, elementBasic);

    // Rest of your code carries on….

    }

  3. December 28th, 2009 at 15:56 | #3

    Hello,

    There an error in your StyleSheet.001.js. download file and demo also…
    // IE
    var rules = sh.compileToRules();

    replace above line with below
    // IE
    var rules = styleSheet.compileToRules();

  4. January 2nd, 2010 at 21:51 | #4

    Thanks for that, the source has been updated :-)

  1. June 4th, 2012 at 00:03 | #1