Grow your CSS skills. Land your dream job.

jQuery CSS Abstraction

Published by Chris Coyier

It should be said, first and foremost, that you should try to keep your styling and your JavaScript away from each other. If you want to change the style of an element with JavaScript, add (or remove) a class name from the element. Then use that class name as your hook in the CSS to affect the styling.

However, there are times when you need to apply styling within JavaScript. jQuery has a function just for it:

$("#thing").css("margin-right", 0);

It can even be kind of appealing, because jQuery handles some cross-browser weirdness for you. For example, opacity is one of those things that requires a variety of CSS properties to be cross-browser compatible. But with jQuery, you don't need to apply all of them. Just setting the opacity with the CSS function works. jQuery knows what kind of browser environment it is in and will apply the right property.

// just works
// also alternate CSS syntax
$("#thing").css({
   opacity: 0.5
});

So you might extend that thinking into assuming that jQuery will help you out with other things that require multiple CSS properties to get the same cross browser effect, like the quintessential example of border-radius. We need -moz-border-radius for Mozilla browsers, -webkit-border-radius for WebKit browsers, and border-radius for Opera (and the future). But jQuery doesn't help us here.

// not gonna help in current Firefox or Safari/Chrome
$("#thing").css({
   "border-radius": "20px"
});

To get cross-browser compatible CSS through jQuery, we'll still have to list all three:

$("#thing").css({
   "border-radius": "20px",
   "-moz-border-radius": "20px",
   "-webkit-border-radius": "20px"
});

So what's up with that? David Walsh thinks it would be library bloat to have this abstraction within jQuery. Ben Alman thinks jQuery should handle fully supported CSS properties (the border-radius spec isn't officially final). Screwlewse thinks that the only reason opacity is supported that way is because it's required for animation (core functions like fadeToggle).

I'm not sure what I think 100%. On one hand it sure would be nice to have that handled magically for me. On the other I can understand the bloat and not-final-spec arguments.

What do you think?

If you have found yourself needing to apply rounded corners through jQuery, you might be smart to abstract it away into a little plugin for yourself.

$.fn.roundThis = function(radius) {
    return this.each(function(e) {
        $(this).css({
           "border-radius": radius,
           "-moz-border-radius": radius,
           "-webkit-border-radius": radius
        });  
    }); 
};

$(function() {

    // usage
    $("#thing-that-will-become-rounded").roundThis("20px");

});

This still won't handle IE though, but there are great plugins that do.


In other news: Congratulations to Brent Traut for winning the free jQuery Conference ticket from my little contest. There were ten people who entered who donated a total of $115 to open source projects. Pseudo-random number generation picked the winner. If you were still thinking of coming, you should! And if you were still thinking about donating to open source, you should!

Comments

  1. Carlos Letelier
    Permalink to comment#

    As you said, it would be nice to have that problem solved by JQuery, but i think that i’ll go with Ben Alman’s thought: “JQuery should handle fully supported CSS”

  2. I agree with the thought that jQuery should keep the extra non-standards stuff out of the library. Especially when I think we’ll soon see CSS3 tools like rounded corners supported in all browsers without using the proprietary CSS attributes.

  3. Nice post; I like the various perspectives on making CSS more robust in jQuery.

    One comment about your plugin: Do you really need a .each() in there? I thought .css would iterate over a set by itself. Just curious if that was working around another issue.

    Thanks!

    • The .each() is because it’s a plugin, which may pass multiple selectors, like:

      $(“#thing, #thing2).roundThis();

      I’m pretty sure that’s why the .each() is needed, but if someone smarter than me wants to elaborate, feel free.

    • close, its in case you pass a collection

      like $(‘a’).roundThis();

    • V1
      Permalink to comment#

      the “this” inside your plugin already referrers to you jQuery element stack. The .css also loops over elements else, you couldn’t do $(“a”).css();

      So you have constructed an unneeded loop. Removing the .each should leave the functionality intact.

  4. Or you can use jQuery UI CSS framework.

    • Permalink to comment#

      Not at all.
      The JQuery UI still uses the same moz and webkit css properties and willnot work in IE

  5. I would image that this rely’s on the sizzle.js engine?

  6. It should be noted that changing CSS properties through JavaScript is a very expensive operation in terms of performance. It’s much faster to just remove/add classes to the elements in question, and have the CSS declared in your stylesheet.

    Having said that, there are cases (albeit very few) where dynamically setting CSS (instead of classes) is needed, for example when creating a tool like css3please.com. However, I don’t think jQuery should abstract proprietary CSS properties like you proposed. If you really want to do this, it’s easy enough to write a plugin, as you demonstrated.

    • Don’t forget that you can’t animate CSS classes, so you have to directly change the CSS.

    • Permalink to comment#

      I think the more abstraction jQuery piles on the more difficult it comes to troubleshoot. Consider the issue you were having the other day with the delegate property and jQuery’s hover event(which is not an actual JavaScript event but an abstraction that only works in some instances): Had you been a newbie jQuery programmer with a shallow understanding of JavaScript and browser events, the distinction between what’s being abstracted and what’s really going on underneath the hood might not have been so obvious. I can imagine someone spending an inordinate amount of time trying to solve a problem whose answer would be more obvious if the hover event had not been introduced as a replacement for mouseenter and mouseleave.

    • Permalink to comment#

      I didn’t mean this as a reply to you, Matthias, but as response to the article in general and Chris in particular.

  7. Everytime i use such things, i’m feeling bad because not all user / customer have javascript enabled. Isn’t it?

    • Permalink to comment#

      Most do. I can’t think of a browser that ships with Javascript disabled, so the user would have to consciously disable it or have it disabled as a company/organization-wide mandate.

      Besides that, there’s nothing wrong with a bit of graceful degradation. Javascript won’t round off the corners because it’s not enabled? Customer sees square corners. This is the subject of countless articles at the moment, and I for one agree. Square, rounded… at the end of the day the site will be quite usable for the 2% with Javascript disabled.

  8. Neat approach but doing this with JS is heavy. I restored sanity to my stylesheets with Compass/Sass.

    http://www.slideshare.net/pengwynn/css-metaframeworks-king-of-all-media/100

    Compass can be used with any framework because it just spits out CSS.

  9. James
    Permalink to comment#

    Just to add – Chrome now accepts the border-radius CSS property along with the -webkit-border-radius property.

    Great tutorial Chris

    • which version of Chrome support it?
      I’m wondering if I define both of them, which one wins?

  10. Sooner or later we wont need proprietary selectors anymore. Weee!!! /o/

  11. Back in the old days of the last century I recall that it was relatively common to talk about a JS notation for CSS. I may still have a book with that (I’m a bit of a horder).

    When we say “keep your CSS and JS separate” we are using a short hand for “keep your presentation and functionality separate”… under common conditions, those to are equivalent.

    But.

    There is no reason why couldn’t express your presentation using JavaScript notation. In fact, writing all your “progressive enhancement” presentation in JavaScript, leveraging jQuery. Would be entirely legit (and possibly even desirable). Just make sure that you keep you keep your presentation separate from the behaviour.

    I realize I’m being a little controversial here but I think that the approach has merit. Especially if you write plugins to handle things like rounded corners (roll the CSS with with IE work around into one call) and other common treatments. I would suggest this even in the mythical world where CSS 3 is fully implemented everywhere.

  12. I’ve added a snippet in TextMate to help me the ridiculous amount of code needed to produce edge css3 coolness.

    i just type

    rounded ⇥

    and it puts in

    -moz-border-radius: 3px;
    -webkit-border-radius: 3px;
    -khtml-border-radius: 3px;
    border-radius: 3px;

    • Justin
      Permalink to comment#

      Nice. You can make the snippet more flexible using mirrored placeholders:

      -moz-border-radius: ${1:3px};
      -webkit-border-radius: $1;
      -khtml-border-radius: $1;
      border-radius: $1;

  13. David Hucklesby
    Permalink to comment#

    It makes sense to me to include properties like opacity to a library, as it’s needed for dynamic effects (“fade”). But would you want to change border-radius dynamically?

  14. Unfortunately, waiting for something to become a standard before jQuery implements it is a little pointless because it takes a long time for stuff to get passed the proposed recommendation stage.

    For example, CSS2.1 only reached candidate recommendation status on September 8, 2009 (maybe it was upgraded, then downgraded at some point, so that might explain why). So there’s nothing wrong with jQuery including something like border-radius, even if it’s not “officially” a standard — because it might not be “official” for many years to come.

  15. Hesky
    Permalink to comment#

    that’s a great way to pass by the css validation thing. thanks

  16. Permalink to comment#

    Thanks for the link to great plugins tht do that the fix on round borders in IE is now solved for me thanks Chris for the link and Dave Methvin for the plugin.
    I prefer to use the plugin until cross browser issues are solved so that my visitors dont miss out on the fun

  17. Dyllon
    Permalink to comment#

    For rounded corners to work in IE, would it be worth making images in photoshop and setting them at the top and bottom of divs?

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".