Detecting CSS selectors support + my JSConf EU talk

I’ll start with a little backstory, if you want to jump straight to the meat, skip the next 4 paragraphs.

In the past few months, my CSS research has been getting some attention and I’ve been starting to become somewhat well-known in the CSS industry. A little known fact about me is that JavaScript has always been one of my loves, almost as much as CSS (even more than it in the past). Ironically, the first time I was asked to speak in a big conference, it was about JavaScript, even though I ended up choosing to speak about CSS3 instead.

Lately, I’ve started wanting to get more into the JavaScript industry as well. I’m quite reluctant to submit speaking proposals myself (every conference or meetup I’ve given a talk so far has asked me to speak, not the other way around) and most JavaScript conferences expect you to submit a proposal yourself. I also couldn’t think of a good topic, something I was passionate about and hasn’t already been extensively covered.

This changed a few weeks ago. While I was writing my <progress> polyfill, it dawned on me: Polyfills is something that’s JS-related and I’m passionate about! I love studying them, writing them, talking about them. I quickly searched if there were any talks about polyfill writing already and I couldn’t find any. So, I decided to submit a proposal to JSConf EU, even though the call for speakers had passed 10 days ago. When I read @cramforce’s tweet that they had decided on most of the speakers, I spent a few days stressed as hell, checking my inbox every few minutes and hoping that my gut feeling that I would get accepted was right.

And it was! 3 days ago I received an email from JSConf EU that my proposal was accepted!! I can’t even begin to describe how happy and excited I am about it. And nervous too: What if they know everything I’m going to say? What if they hate my talk? What if the JavaScript industry is really as sexist as some people claim and they dismiss me because of my gender? I decided to put my fears aside and start working on my slides, as I couldn’t wait until later (even though I have multiple deadlines creeping up on me right now…).

A big part of writing polyfills is feature detection. Before trying to implement a feature with JavaScript, you first have to check if it’s already supported. So, a substantial portion of my talk will be about that. How to detect if APIs, HTML elements, CSS properties/values/selectors etc are supported. There are already established solutions and techniques about most of these, except CSS selectors. Modernizr doesn’t detect any, and judging from my Google search nobody has written about any techniques for doing so in a generic fashion.

A really simple way to detect CSS selectors support is using document.querySelector() in a try...catch statement. If the selector is not supported, an error will be thrown. However, that’s not really reliable, as the Selectors API is not supported in IE < 8. So, I thought of another idea: What if I turn the hassle of reading out a stylesheet via the DOM methods (browsers drop stuff they don’t understand) into a feature detection method?

The basic idea is creating a new <style> element with an empty rule and the selector we want to test support for, and then read out the stylesheet through the DOM methods to see if a rule actually exists. I’ve so far tested it in Firefox, Opera and Chrome and it seems to work. I haven’t tested it in IE yet, as I currently have too many apps running to turn on the vm, so it might need a few fixes to work there (or I might be unlucky and the idea might not work at all).

You can test it out yourself in this fiddle, just check the console: http://fiddle.jshell.net/leaverou/Pmn8m/show/light/

Apologies if this has already been documented elsewhere, I really couldn’t find anything.

Edit: James Long worked on fixing my example’s issues with IE

  • Anonymous

    Hmm…interesting. I can see this being useful as a test to see what the common selectors are that document.querySelector(All) works with.

  • http://twitter.com/rwaldron rick waldron

    That’s pretty cool – would it make sense to take the faster document.querySelector path wherever possible?

    • http://leaverou.me Lea Verou

      Totally, the code in the fiddle is just to show the idea, not to be used as is.

  • Pingback: xhtml css templates – Detecting CSS selectors support + my JSConf EU talk | Lea Verou | XHTML CSS - Style sheet and html programming tutorial and guides

  • http://twitter.com/thecssninja Ryan Seddon

    Awesome, nice idea. Modernizr does something similar for font face detection. It regexp’s the style to see if the src property exists.

  • http://andrewdupont.net Andrew Dupont

    “Apologies if this has already been documented elsewhere, I really couldn’t find anything.”

    I don’t think it has been documented elsewhere, and even if it has, you need not apologize — after all, people don’t apologize for writing redundant articles about closures or prototypal inheritance.

    Prototype’s selector engine (now deprecated in favor of Sizzle) was able to use querySelectorAll, DOM3 XPath queries, or old-school DOM traversal (in order of preference) to turn a selector into a collection of elements. We used the same trick — try querySelectorAll and catch syntax errors — in order to test whether we could use the Selectors API. A simple capability check wouldn’t have worked because we supported oddball pseudoclasses (like nth-of-type) that weren’t supported even in some browsers that supported the Selectors API.

    (I must say that’s the biggest downside of treating CSS like a querying language. DOM3 XPath is much more complicated, but at least browsers implemented _all_ of it, so if it existed in the XPath spec you could be sure that it’d work in a browser.)

  • http://andrewdupont.net Andrew Dupont

    Hey, check it out: the “insertRule” method on stylesheet objects works the same way.

    var stylesheet = document.styleSheets[0];
    stylesheet.insertRule(“:unsupported {}”, 0);

    That’ll throw a syntax error (at least in FF and Safari) if the selector is invalid.

    No help for IE (the same versions that don’t support qSA also don’t support this API for manipulating CSS rules), but with this approach you could re-use one stylesheet instead of creating a new one each time.

    • http://leaverou.me Lea Verou

      Good find, although I’m hoping the above can or has the potential to work with IE too.

      • Michal Čaplygin

        As far as I can recall one excellent article ( http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript ), IE uses stylesheet.addRule(selector, rule, index) instead of stylesheet.insertRule(rule, index).

  • http://keithclark.co.uk Keith Clark

    Have you seen Diego Perini’s work on CSS support detection?

    http://javascript.nwbox.com/CSSSupport/

    • http://leaverou.me Lea Verou

      No, I haven’t. It looks a bit similar, although doesn’t work correctly (it says Chrome doesn’t support :nth-child)

      • http://mathiasbynens.be/ Mathias Bynens

        Seems to work fine here. Which Chrome version are you testing it in?

      • Diego Perini

        Lea,
        my CSSSupport project does test many more aspects of CSS browser support: properties, selectors, media-queries and at-rules and it has been long used in Modernizr.

        Your assertion about it incorrectly detecting “nth-child()” in Chrome seems wrong.

        The reason is that the test is specifically checking “:nth-child(n)” support which is not supported by Chrome but it is supported in Firefox and other browsers. So the testing I do seems correct. Check it out ! I will be happy to add some patch if it proves wrong.

        • http://leaverou.me Lea Verou

          Yes, you’re right. I assumed it was generally checking :nth-child(), as the failing tests are the only ones that test for :nth-something support. 

          Very interesting library, I should definitely look more into it!

        • Diego Perini

          I should probably add a test for standard :nth-child() and friends in CSSSupport using common parameters instead of just (n).

          I hadn’t noticed this bug during my tests but your tests helped finding a probable bug in Chrome. I suspect something changed between the various release of Chrome.

  • http://twitter.com/suprMax Max

    What do you think about creating a new and filling it with not empty rules, but some basic rules like “color: red” and then reading styles from the element that should be selected. Sounds simpler and if you get red color it means that element was selected by browser..

    • http://leaverou.me Lea Verou

      That would work too, but it’s harder to make test cases. The good thing with that is that it enables you to test conformity too, not just if the browser thinks they support it.

      • Gabriel Petrioli

        Exactly.. I was thinking the same thing..
        A classic example is the multiple classes.. IE6 thought it was supported but it failed miserably, applying the latest one it encountered although the .aclass.anotherclass had higher specificity..

  • http://twitter.com/suprMax Max

    A little bit of browser testing for you :)

    IE7: http://plasmon.rghost.ru/15554951.image (error)
    IE8: http://plasmon.rghost.ru/15554971.image (error)
    Opera: http://rghost.ru/15555001/image.png (duplicated console log, probably because of jsfiddle)

    If you need any more testing – shoot me messages through twitter, I’ve got dedicated laptop just for browser testing :D

    • http://leaverou.me Lea Verou

      Thank you! I’ll fix it and contact you again, I know what causes this.

  • http://www.facebook.com/profile.php?id=755920516 James Long

    Hi Lea.

    I’ve been an admirer of your work for a little while and I think I can help here. I’ve got versions of IE6 – 8 on my version of XP so I forked your fiddle and started playing. I’ve managed to get this working in IE6 – 8, Firefox, Safari, Opera and Chrome. I don’t have IE9 so I’m afraid I can’t test in that, but hopefully my code will hold up.
    Anyway: the fiddle http://jsfiddle.net/Skateside/5dKc7/5/

    Keep up the good work,
    Skateside

    • http://leaverou.me Lea Verou

      That’s great work James! Updating the original post now, thanks!

    • sasbranding

      Thanks both Lea and James. This problem was frustrating me and both you provided the perfect solution. Keep being awesome.

  • Pingback: Progressive enhancement – CSS selectors detection and best practices. « ziad rahhal's notes

  • http://paulirish.com Paul Irish

    Diego Perini has a library for detection of CSS Support including selectors. 
    Looks like his solution is a bit larger than yours. https://github.com/dperini/css-support

  • Pingback: Using JavaScript to detect CSS Support | Skateside Online

  • http://www.hugwebdesign.com Daniel Hug

    In your fiddle, IE8 won’t output true or false on any of the tests and instead gives the error:
    Unable to get property ‘rules’ of undefined or null reference

    Can’t find out why. :(

    • http://www.hugwebdesign.com Daniel Hug

      Got it to work with line 6 being:

      ret = !!( style.sheet ? (style.sheet.rules || style.sheet.cssRules)[0] : style.styleSheet.rules.length );

      Works in IE8+ now.