Как создать все возможные комбинации селектора CSS 2?

Каким будет наилучший способ генерации всех возможных комбинаций селектора CSS 2 для элемента DOM в контексте текущего состояния document?

Например:   Для следующего 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>

Метод JS/jQuery, так что <!--ARGUMENT ELEMENT--> в вышеупомянутой разметке задается как аргумент метода, он возвращает все возможные комбинации выбора CSS 2 для аргумента в форме массива. Необязательный параметр может быть добавлен к методу, который определит максимальную глубину вложения CSS2 Selector Nesting. Если этот параметр установлен в false, он должен возвращать все возможные комбинации (Будет ли hogger производительности, если установлено значение false)

Результат:

[
 ".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",
]

Любые указатели в этом отношении будут чрезвычайно полезны.

Ответ 1

Во-первых, давайте придерживаться узкого класса селекторов с именами тегов, именами классов и идентификаторами, ничего необычного, как E > F или E + F. Пусть также запрещают комбинации имен классов (.class1.class2.class3), иначе один элемент с 10 именами классов будет генерировать только 4 миллиона селекторов.

Каждый из наших полных селекторов состоит из простых селекторов, разделенных пробелами. Каждый простой селектор представляет собой комбинацию tag{0,1}id{0,1}class{0,n} - то есть каждый элемент имеет ровно один тег, не более одного идентификатора, и может иметь произвольное число имен классов. Это дает нам верхний предел 2 * 2 * (n + 1) простых селекторов для одного элемента.

Учитывая ссылку на элемент DOM, возьмите его имя тега, идентификатор и имена классов. Вычислите все возможные простые селекторы, как описано выше. Позволяет вызвать этот набор A1. Переместите один шаг вверх по иерархии в родительский, вычислите все простые селектора для этого родительского элемента - это будет набор A2. Продолжайте, пока не достигнете элемента html - набора Am. Теперь у вас будет список, состоящий из m элементов, каждый элемент представляет собой набор простых селекторов.

Теперь выберите некоторые из этих множеств и найдите их декартово произведение. Скажем, m = 5. Сколько наборов вы можете выбрать? Множество A1 всегда присутствует, но другие необязательны. Для каждого из них вы либо выбираете его, либо нет. Это похоже на двоичные числа:

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
...

Это означает, что у вас будут 2 ^ (m-1) декартовы продукты. Теперь вы можете преобразовать их в строки. Последний шаг - удалить дубликаты, рассмотрим следующий пример:

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

Наши вычисления приведут к этому списку:

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

Эти два div дают двойные селектора. Удалите их, и работа будет выполнена. Все этапы очень просты алгоритмически. Я уверен, что вы можете понять их, но если вы где-то застряли или нуждаетесь в дальнейшем разъяснении, не стесняйтесь спрашивать меня в комментариях.


UPDATE

Итак, я решил поиграть с ним немного больше и написал программу, вот список селекторов, которые ваш пример генерирует: http://pastie.org/1616164