CSS Selectors as Superpowers

You thought CSS was weak? Think again.

After years of complaints about Cascading Style Sheets, many stemming from their deliberately declarative nature, it’s time to recognize their power. For developers coming from imperative programming styles, it might seem hard to lose the ability to specify more complex logical flow. That loss, though, is discipline leading toward the ability to create vastly more flexible systems, a first step toward the pattern matching model common to functional programming.

Way back when I was writing about styling XML in browsers, I didn’t even have to stop to think about how difficult it would be to repurpose CSS selectors for XML documents. Since they weren’t tightly bound to assumptions about HTML beyond the existence of elements and attributes, they just worked.

The tool that most vividly demonstrated the real power of selectors, though, was jQuery. I may have annoyed some people by referring to jQuery as “that framework that lets you use CSS selectors instead of DOM tree walking” for a while, but Remy Sharp makes clear the power of that:

The ease in which jQuery could be learnt was the appeal to me. All the DOM navigation was done using CSS expression using some insane black box magic that John Resig had come up [with] – saving my limited brain power, and once I had those DOM nodes, I could do what I wanted to them (usually some combinations of showing, hiding, fading and so on).

Over time, as Sharp notes, Web browsers learned from jQuery, building this basic lesson deeper into their tools and making it work more efficiently:

In those 7 years, quite a bit has happened. Probably one of the most important steps forward was the introduction of querySelectorAll.

Being able to give a native function of a browser a CSS expression, and it doing the work to navigate the DOM is a huge (literally) part of jQuery.

CSS selectors simplified the learning process, but they also simplified the resulting code. Figuring out which node an expression had selected became much much simpler. Developers could apply the same selectors they were using for code to a stylesheet and quickly resolve any questions, and could readily apply the same stylesheet to multiple documents if there were further questions.

Why does this work so well, when CSS selectors offer only a few options and a little logic?

Declarative approaches work extremely well for describing queries through a document. Markup (whether HTML or XML) provides clear hooks for those queries through their element and attribute structures. The queries developers used to do by hunting and pecking through the DOM are still available, but most of the time, declarations will get you there a lot faster, or in identical time.

Browsers can optimize their CSS selector processing, running it in much faster native code. I was very surprised to find that completely switching out stylesheets was faster than making a few style changes through JavaScript directly a few years ago. Tree walking in JavaScript means a lot of back and forth between the document object and the interpreted code.

Performance and ease of learning aren’t all you get, though. You also get a style of programming that offers vastly more flexibility, because selectors are a form of pattern matching. Pattern matching approaches don’t require that all the patterns match. It is perfectly acceptable to the pattern matching engine for some patterns to match nothing, and for some content to go unmatched.

That makes it easy to build stylesheets that apply to a wide variety of differently structured documents, or to write code that only applies event handlers or other processing to elements if and when it finds a match. It’s a simple message often explained to beginners at the very start of learning CSS, but it reverberates throughout CSS practice. Stylesheets can be shared, reused, and applied in layers through the cascade that determines which declaration applies where.

Adding to that flexibility, CSS can work with any markup vocabulary. There are some selectors that are tightly bound to HTML expectations of classes and IDs, but they are mostly syntax sugar. Selecting the same content in another vocabulary might require a few more characters and a bit more processing, but it is always possible and generally easy.

Selectors give developers the freedom to create and use new vocabularies in the browser. Used in style sheets, they let developers tell browsers how to present content the browser doesn’t intrinsically understand. Used in JavaScript they let developers tell browsers what behavior to give new elements and attributes mixed into HTML. The result: polyfills, the development style that horrified lots of people last week, but which I still suspect is the future of web development.

Pattern matching has promise for markup processing beyond the browser. Historically that’s mostly been done with XPath and XSLT or with other language combinations rather than CSS selectors, but CSS selectors are spreading even there, spreading on the vectors of JavaScript on the server and testing tools.

My long-term hope is that the success of CSS selectors will bring developers to look for other ways to apply pattern-matching to their markup, acting as a gateway to more declarative approaches that will add some much needed flexibility to code and document structures. All these quiet years later, CSS selectors seem to be our best bet for changing how we write code for the Web.


Sign up for the O'Reilly Programming Newsletter to get weekly insight from industry insiders.
topic: Web Platform
  • http://twitter.com/alexp700 alexp700

    Yeah, but they’re still Hard. Much like XSLT or reverse polish notation, people’s brains don’t like working with a non trivial structure for much more than very simple tweaking tasks (e.g. showing and hiding, which they are perfectly adequate for). The way they are applied is non-obvious (precedence from the order the style sheets are written and then ordered), and their notation quickly looks very ugly/hard to follow with sub pattern matches. At least they’ve become a little more consistent as browsers have improved – though animations (the most useful feature) still seem to be a grey area, and controlling them is too hard.

    And they are very effective at showing and hiding!

    • http://www.facebook.com/joshisgross Joshua Gross

      I think it’s pretty unfair to compare selectors to XSLT or RPN. CSS is a lot more intuitive than either of those.

      • Iggy

        I disagree, RPN is far more intuitive for stack based computation then XSLT or CSS on any level. Switching to RPN is no different then switching to a new keyboard layout. Everyone will complain about it unless they have the motivation to do it differently.

    • SayWhatAgain

      CSS selectors are not hard for the mildly intelligent. Far easier than XSLT, so much that I wouldn’t even compare them. They’re similar tools for very different purposes, and there are better tools than XSLT for what it does (namely just about any programming language that you’re currently using). None of your arguments for its difficulty make much sense to me; it seems like you just don’t understand how CSS is meant to be used in a workflow. Your XSLT argument is a straw man.

  • http://twitter.com/limscoder Dave Thompson

    The problem with CSS is the cascading part. Reasoning about which selector will be applied relative to others that match the same element is difficult, especially when there are thousands of selectors across dozens of css files. Css compilers like less and sass help, as do the Chrome development tools, but it’s still very tricky, especially if you are using a gui library that includes it’s own css like Bootstrap, Ext, or Dojo.

  • http://www.wonderwhy-er.com wonderwhy-er

    There is a hidden dangers in CSS selectors and cascade. They may enforce a non flexibility trough context dependency. Cascade by its definition is context dependency. Which goes against some of principles of SOLID.
    I heard large firms with larger sites and large dev teams completely ruling out use of cascade and some other “dangers” of CSS.
    Like they never use tag names in CSS selectors. Aka “div.selected” is a “no” usually as it introduces dependency to div tag, and someone somewhere refactoring may break something when he refactors HMTL structure.

    So yeah, lot of HTML/CSS/JS code there I see is almost not reusable cuz its riddled with context dependencies. Not that its not possible to do it in a way that’s better but that rules out a lot of features of CSS as they are context dependencies by definition.

  • Alex Besogonov

    I absolutely _hate_ CSS. It’s NOT powerful and it’s cementing all these #$@IY(* fixed webpage designs. Also, CSS selectors are not reusable and require quite a lot of context knowledge.

  • Max.

    The fact that the debugging is difficult, and the fact that there is very little verification (any xml/html document is supposed to work with any css) makes it hard to use, regardless that it is functional. As much as I find xslt/xpath nice on small things, but on large systems, it is just not working.