Чому браузери відповідають селекторам CSS справа наліво?


560

Селектори CSS узгоджуються двигунами браузера справа наліво. Тож вони спочатку знаходять дітей, а потім перевіряють батьків, чи вони відповідають решті частин правила.

  1. Чому це?
  2. Це тільки тому, що говорить специфікація?
  3. Чи впливає це на можливий макет, якщо його оцінювали зліва направо?

Для мене найпростішим способом це було б використання селекторів з найменшою кількістю елементів. Отже, ідентифікатори спочатку (як вони повинні повернути лише 1 елемент). Тоді можуть бути класи або елемент, який має найменшу кількість вузлів - наприклад, на сторінці може бути лише один проміжок, тому переходьте безпосередньо до цього вузла з будь-яким правилом, яке посилається на проміжок.

Ось декілька посилань, що підтверджують мої претензії

  1. http://code.google.com/speed/page-speed/docs/rendering.html
  2. https://developer.mozilla.org/uk/Writing_Efficient_CSS

Здається, що це робиться таким чином, щоб уникнути необхідності дивитися на всіх батьків батьків (яких може бути багато), а не на всіх батьків дитини, які повинні бути одним. Навіть якщо DOM є глибоким, він би дивився лише на один вузол на рівні, а не на кілька у відповідність RTL. Чи легше / швидше оцінити CSS-селектори LTR або RTL?


5
3. Ні - як би ви не читали, селектор завжди відповідає однаковому набору елементів.
Šime Vidas

39
Оскільки це варто, браузер не може вважати, що ваші посвідчення особи унікальні. Ви можете вставити один і той же id = "foo" по всій DOM, і #fooселектор повинен відповідати всім цим вузлам. jQuery має можливість сказати, що $ ("# foo") завжди поверне лише один елемент, оскільки вони визначають власний API зі своїми правилами. Але веб-переглядачам потрібно впроваджувати CSS, і CSS каже, щоб узгодити все в документі з заданим ідентифікатором.
Борис Збарський,

4
@Quentin У "невідповідних" (до HTML) ідентифікаторах документів можуть бути не унікальні, і в цих документах CSS вимагає зіставлення всіх елементів із цим ідентифікатором. Сам CSS не пред'являє жодних нормативних вимог до унікальних ідентифікаторів; текст, який ви цитуєте, є інформативним.
Борис Збарський

5
@Boris Zbarsky Що робить jQuery, залежить від кодового шляху в jQuery. У деяких випадках jQuery використовує API NodeSelector (нативне querySelectorAll). В інших випадках використовується Sizzle. Sizzle не відповідає декільком ідентифікаторам, але QSA (AYK). Проведений шлях залежить від селектора, контексту та браузера та його версії. jQuery's Query API використовує те, що я назвав "Спочатку, подвійний підхід". Я написав статтю про це, але це вниз. Хоча ви можете знайти тут: fourbelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett

5
Я навіть не впевнений, що хтось, але ви двоє, можете зрозуміти, що відбувається з цією довгою розмовою. У нас є чат для цих розширених дискусій. Все, що ви насправді хочете тримати навколо, слід ставити на запитання чи відповідь, особливо якщо це уточнююча інформація. Переповнення стека не впорядковує обговорення в коментарях.
Джордж Стокер

Відповіді:


824

Майте на увазі, що коли веб-переглядач відповідає селектору, він має один елемент (той, для якого намагається визначити стиль), і всі ваші правила та їх вибір, і він повинен знайти, які правила відповідають елементу. Це відрізняється від звичайної речі jQuery, скажімо, у вас є лише один селектор і вам потрібно знайти всі елементи, які відповідають цьому селектору.

Якщо у вас був лише один селектор і лише один елемент для порівняння з цим селектором, то зліва направо в деяких випадках має більше сенсу. Але це, очевидно, не ситуація із браузером. Веб-переглядач намагається надати Gmail або будь-який інший, і він має той <span>, який намагається стилювати, і 10 000+ правил, які Gmail вводить у свій таблицю стилів (я не збільшую це число).

Зокрема, у тому випадку, коли браузер переглядає більшість вибраних елементів, які, на його думку , не відповідають заданому елементу. Таким чином, проблема стає вирішенням того, що селектор не відповідає максимально швидко; якщо це вимагає трохи додаткової роботи у випадках, які відповідають, ви все одно виграєте завдяки всій роботі, яку ви збережете у випадках, які не відповідають.

Якщо ви почнете з того, щоб просто зіставити крайню праву частину селектора з вашим елементом, то ймовірність, що він не збігається, і ви закінчите. Якщо це відповідає, вам доведеться виконати більше роботи, але тільки пропорційну глибині дерева, що в більшості випадків не так вже й велике.

З іншого боку, якщо ви почнете з узгодження крайньої лівої частини селектора ... з чим це ви співпадаєте? Ви повинні почати ходити по DOM, шукаючи вузли, які можуть відповідати йому. Виявлення того, що немає нічого, що відповідає самій лівій частині, може зайняти деякий час.

Тож браузери відповідають праворуч; це дає очевидну відправну точку і дозволяє дуже швидко позбутися більшості кандидатів. Деякі дані ви можете побачити на веб- сторінці http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 (хоча позначення є заплутаним), але останній результат - це зокрема для Gmail два роки тому для 70% пар (правило, елемент) ви могли вирішити, що правило не відповідає, лише вивчивши частини тега / класу / id найправішого селектора для правила. Відповідне число тестового набору Mozilla для завантаження сторінок становило 72%. Тож справді варто спробувати позбутися тих 2/3 усіх правил якнайшвидше, а потім лише турбуватися про відповідність решти 1/3.

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


5
Як невеликий бонус, має сенс читати його RTL, ніж LTR, навіть англійською мовою. Приклад: stackoverflow.com/questions/3851635/css-combinator-precedence / ...
BoltClock

6
Зауважте, що відповідність RTL застосовується лише для комбінаторів . Він не проходить до рівня простого селектора. Тобто браузер бере найменший правий селектор або послідовність простих селекторів і намагається його атомно співставити. Потім, якщо є збіг, він слід за комбінатором вліво до наступного селектора сполук і перевіряє елемент у цьому положенні тощо. Немає доказів того, що браузер читає кожну частину складеного селектора RTL; насправді останній абзац показує саме інакше (ідентифікатори перевірки завжди приходять першими).
BoltClock

5
Насправді, до моменту, коли ви підбираєте селекторів, принаймні у Gecko, ім'я тегів та простір імен виходять на перше місце. Ідентифікатор (а також ім'я тегів та імен класів) розглядається на етапі попередньої фільтрації, який виключає більшість правил, не намагаючись відповідати селекторам.
Борис Збарський

Це може допомогти залежно від оптимізації, яку робить UA, але це не допоможе кроку попередньої фільтрації в Gecko, який я описав вище. Існує другий крок фільтрації, який працює над ідентифікаторами та класами, який використовується лише для комбінаторів нащадків, але це може допомогти.
Борис Збарський

@Benito Ciaro: Не кажучи вже про проблеми специфіки.
BoltClock

33

Синтаксичний аналіз праворуч наліво, який також називають розбором знизу вгору, є фактично ефективним для браузера.

Розглянемо наступне:

#menu ul li a { color: #00f; }

Спочатку браузер перевіряє наявність a, потім li, потім ulі потім #menu.

Це тому, що, коли браузер сканує сторінку, йому просто потрібно переглянути поточний елемент / вузол та всі попередні вузли / елементи, які він просканував.

Слід зазначити, що браузер починає обробляти момент, коли він отримує повний тег / вузол і не потрібно чекати всієї сторінки, за винятком випадків, коли він знаходить сценарій, і в цьому випадку він тимчасово призупиняє і завершує виконання сценарію, а потім йде вперед.

Якщо це буде навпаки, це буде неефективним, оскільки браузер знайшов елемент, який він сканував при першій перевірці, але потім був змушений продовжувати перегляд документа для всіх додаткових селекторів. Для цього браузеру потрібно мати весь html та може знадобитися сканувати всю сторінку, перш ніж він почне css-живопис.

Це суперечить тому, як більшість libs розбирають dom. Там побудовано dom, і йому не потрібно сканувати всю сторінку, просто знайдіть перший елемент, а потім перейдіть на узгодження інших всередині нього.


20

Він дозволяє здійснювати каскадування від більш конкретного до менш конкретного. Це також дозволяє коротке замикання в застосуванні. Якщо більш конкретне правило застосовується у всіх аспектах, до яких застосовується батьківське правило, усі батьківські правила ігноруються. Якщо у батьків є інші біти, вони застосовуються.

Якщо ви пішли навпаки, ви б відформатували відповідно до батьків, а потім перезаписали кожен раз, коли у дитини щось інше. Зрештою, це набагато більше роботи, ніж ігнорування елементів у правилах, про які вже піклуються.


11
Це окреме питання. Ви робите каскад, сортуючи правила за специфікою, а потім порівнюючи їх у порядку конкретності. Але питання тут полягає в тому, чому за певним правилом ви конкретно співпадаєте з його селекторами.
Борис Збарський
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.