В каком направлении двигатели селектора читают, точно?

Я всегда верил (хотя теперь я сомневаюсь в достоверности этих убеждений), что:

div.name

Быстрее, чем:

.name

Однако недавно я читал, что большинство движков селектора CSS читаются справа налево, и в этом случае первый пример на самом деле не будет медленнее? Поскольку движок селектора просто найдет каждый элемент с классом имени, а затем должен определить, какие из них были div s?

Каким образом читатели CSS-селектора читают вообще? Слева направо или справа налево? И если они обычно читают справа налево, кто-то может предложить мне объяснение, почему (я не вижу, как имеет смысл читать справа налево в терминах механизма выбора)?

Ответ 1

Однако недавно я прочитал, что большинство движков селектора CSS читаются справа налево, и в этом случае первый пример не будет медленнее?

Какой способ для CSS-селекторных движков читается вообще? Слева направо или справа налево? И если они обычно читают справа налево, кто-то может предложить мне объяснение, почему (я не вижу, как имеет смысл читать справа налево в терминах механизма выбора)?

Честно говоря, почти невозможно определить, какой селектор будет медленнее в данном браузере, а тем более в браузерах. Производительность имеет тенденцию колебаться и быть непредсказуемой, особенно в таких микроскопических масштабах и с непредсказуемыми структурами документов. Даже если говорить о теоретической производительности, это в конечном счете зависит от реализации.

Сказав это, как показано в Борис Збарский, ответьте на этот другой вопрос и в Guffa ответьте на ваш, типичный браузер (в настоящее время это относится ко всем основным механизмам макета) принимает элемент и оценивает все селекторы-кандидаты, чтобы увидеть, какие из них соответствуют, вместо поиска набора элементов, соответствующих данному селектору. Это тонкая, но очень важная разница. Борис предлагает техническое объяснение, которое не только невероятно детализировано, но и авторитетно (поскольку он работает над Gecko, движком, используемым Firefox), поэтому я настоятельно рекомендую его прочитать.

Но я подумал, что я должен рассмотреть то, что, похоже, вызывает еще одно беспокойство в вашем вопросе:

Поскольку движок селектора просто найдет каждый элемент с классом имени, а затем должен определить, какие из них были div s?

Также как комментарий Патрика МакЭлхани:

Связанный вопрос объясняет, почему селектора читаются справа налево в целом, поэтому #foo ul.round.fancy li.current читается li.current, ul.round.fancy, #foo, но действительно ли он читается справа налево в каждом элементе (.current, li, .fancy, .round, ul, #foo)? Должно ли это быть?

Я никогда не реализовал CSS, и не видел, как другие браузеры его реализуют. Мы знаем из ответов, приведенных выше, что браузеры используют совпадение справа налево для просмотра комбинаторов внутри селекторов, таких как комбинаторы > в этом примере:

section > div.second > div.third

Если элемент не является div.third, тогда нет точки, проверяющей, является ли его родительский элемент div.second, родителем которого является section.

Однако я не считаю, что этот порядок справа налево сверлит весь путь до простого уровня селектора. Другими словами, я не считаю, что браузеры используют оценку справа налево для каждой части простой селекторной последовательности (также известной как составной селектор) в рамках оценки справа налево по ряду составных селекторов, разделенных комбинаторы.

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

div.name[data-foo="bar"]:nth-child(5):hover::after

Теперь нет гарантии, что браузер обязательно проверит эти условия для элемента в следующем порядке:

  • Является ли указатель на этот элемент?
  • Является ли этот элемент 5-м дочерним элементом его родителя?
  • Этот элемент имеет атрибут data-foo со значением bar?
  • Этот элемент имеет класс name?
  • Это элемент div?

И этот селектор, который функционально идентичен выше, за исключением того, что его простые селектора перемешались, обязательно оцениваются в следующем порядке:

div:hover[data-foo="bar"].name:nth-child(5)::after
  • Является ли этот элемент 5-м дочерним элементом его родителя?
  • Этот элемент имеет класс name?
  • Этот элемент имеет атрибут data-foo со значением bar?
  • Является ли указатель на этот элемент?
  • Это элемент div?

Просто нет причин, чтобы такой порядок применялся по соображениям производительности. Фактически, я бы подумал, что производительность будет повышаться, сначала выбирая определенные типы простых селекторов, независимо от того, где они находятся в последовательности. (Вы также заметите, что ::after не учитывается - потому что псевдоэлементы не являются простыми селекторами и даже не входят в соответствующее уравнение.)

Например, очень хорошо известно, что селекторы ID являются самыми быстрыми. Ну, Борис говорит об этом в последнем абзаце своего ответа на связанный вопрос:

Обратите внимание также, что существуют другие браузеры оптимизации, которые уже делают, чтобы избежать попыток сопоставления правил, которые определенно не совпадают. Например, если самый правый селектор имеет идентификатор и этот идентификатор не совпадает с идентификатором элемента, то не будет попытки сопоставить этот селектор с этим элементом вообще в Gecko: набор "селекторов с идентификаторами", которые предпринимаются происходит из поиска хэш-таблицы на идентификаторе элемента. Таким образом, это 70% правил, которые имеют довольно хорошие шансы на совпадение, которые по-прежнему не совпадают после рассмотрения только тега/класса/идентификатора самого правого селектора.

Другими словами, есть ли у вас селектор, который выглядит так:

div#foo.bar:first-child

Или это:

div.bar:first-child#foo

Gecko всегда будет проверять идентификатор и класс в первую очередь, независимо от того, где он находится в последовательности. Если у элемента нет идентификатора и класса, который соответствует селектору, он мгновенно отбрасывается. Если вы спросите меня, сделайте это быстро.

Это был просто Гекко в качестве примера. Это может различаться и между реализациями (например, Gecko и WebKit могут делать это иначе, чем Trident или даже Presto). Существуют стратегии и подходы, которые, как правило, согласованы поставщиками, конечно (вначале вряд ли будет разница в проверке идентификаторов), но небольшие детали могут отличаться.

Ответ 2

Механизм выбора не ищет элемент для применения правила, он ищет правила, применимые к определенному элементу. Поэтому имеет смысл читать селектора справа налево.

Селектор, подобный этому:

div span.text a.demo

заставит механизм селектора выполнить эти проверки, чтобы увидеть, относится ли селектор к элементу:

  • Это элемент a с классом demo?
  • Есть ли у него предка, который является элементом span с классом text?
  • Этот элемент имеет предка, который является элементом div?