How to Generate All Possible CSS 2 Selector Combinations?


Tags: javascript,jquery,dom,css-selectors

Problem :

What would be the best way to generate all possible CSS 2 Selector Combinations for a DOM element in context with the current state of the document?

Eg: For the following MarkUp

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <title></title>
</head>
<body>
  <div id="content">
    <ul>
      <li>a</li>
      <li>b</li>
      <li class="last-li">c</li> <!--ARGUMENT ELEMENT-->
    </ul>
  </div>
</body>
</html>

A JS/jQuery Method such that the <!--ARGUMENT ELEMENT--> in the above markup is given as an argument to the method it returns all possible CSS 2 Selector combinations for the Argument in form of an array. An optional parameter can be added to the method which will define the maximum depth of CSS 2 Selector Nesting. If this parameter is set to false it should return all possible combinations (Will be a performance hogger if set to false)

Example Output:

[
 ".last-li",
 "li.last-li",
 "ul .last-li",
 "ul li.last-li",
 "div ul .last-li",
 "div ul li.last-li",
 "#content ul .last-li",
 "#content ul li.last-li",
 "div#content ul .last-li",
 "div#content ul li.last-li",
 "body div ul li.last-li",
 "body div ul .last-li",
 "body #content ul li.last-li",
 "body div#content ul .last-li",
 "body div#content ul li.last-li",
]

Any pointers in this regard will be extremely helpful.



Solution :

First, let's stick to a narrow class of selectors involving tag names, class names and IDs, nothing fancy like E > F or E + F. Let's also disallow combinations of class names (.class1.class2.class3), otherwise a single element with 10 class names would generate 4 million selectors alone.

Each of our full selectors consists of simple selectors separated by spaces. Every simple selector is a combination of tag{0,1}id{0,1}class{0,n} - i.e. each element has exactly one tag, at most one ID, and it can have an arbitrary number of class names. That gives us the upper limit of 2 * 2 * (n + 1) simple selectors for a single element.

Given a reference to a DOM element grab it's tag name, ID and class names. Calculate all possible simple selectors as described above. Lets call this set A1. Move one step up the hierarchy to it's parent, calculate all simple selectors for that parent element - that'll be the set A2. Continue until you reach the html element - the set Am. Now you'll have a list, consisting of m items, each item is a set of simple selectors.

Now pick some of these sets and find their cartesian product. Say, m = 5. How many sets can you pick? The set A1 is always present, but others are optional. For each one of them you either pick it or not. That's like binary numbers:

0000 // 0, A1
0001 // 1, A1 x A2
0010 // 2, A1 x A3
0011 // 3, A1 x A2 x A3
0100 // 4, A1 x A4
...

That means you'll have 2^(m-1) cartesian products. You can now convert them to strings. The last step is to remove duplicates, consider this example:

<div>
  <div>
    <span></span>
  </div>
</div>

Our calculations will yield this list:

  span
  div span // inner div
  div span // outer div
  div div span

Those two divs yield duplicate selectors. Remove those and the job is done. All the steps are very simple algorithmically. I'm sure you can figure them out, but if you get stuck somewhere or need further clarification feel free to ask me in the comments.


UPDATE

So, I decided to play with it a bit more and wrote the program, here's the list of selectors your example generates: http://pastie.org/1616164


    CSS Howto..

    How to apply css to id generated dynamically?

    How can `animation-play-state` in CSS be initially set to `paused` and changed to `running`?

    How to scale an element when it loads using only CSS?

    DropDown Menu won't show for more than a second..not working.

    How to change css style of input and some text in one function?

    How to use conditional CSS In CakePHP 2.x?

    How to use PHP to get the value of a star rating widget?

    How to include css and jquery in joomla component

    how to give space between tr tag and divide by | in html

    How to generate image sprites in ember-cli using compass?

    How make input element take up available space next to a fixed width element?

    How to pause or delay animation using JavaScript/jquery

    How to include css in mulitple jsps?

    How to get a css property of an externally styled element with javascript

    How to add more items to the list without going to the next row? [closed]

    How to change CSS color values in real-time off a javascript slider?

    How do I insert tag using :after css property?

    How can i use css frameworks without cluttering html with too many classes [closed]

    How does Slick js carousel behave width custom css?

    how to remove small space that only appear in iphone browser

    How to make alerts overlap?

    How do I get a td background color to stretch from left to right, not just the table width, in html & css?

    How to use an existing source map to compile scss to css?

    How do I get IE to display CSS DIV tables properly?

    How do I use CSS and JS files within a HTML file being sent with Express?

    How to Fix: iOS drops CSS files when javascript changes DOM

    Refactor link to show/hide a table row

    How apply CSS to browse button

    Bootstrap: How to get part of my banner image to hide under my sidebar?

    How can I style a list of name-value pairs to appear like an HTML table?