Parent css: Is there a CSS parent selector?

Содержание

Почему у нас нет селектора по родителю — Веб-стандарты

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

Вкратце: производительность.

Как работает CSSСкопировать ссылку

В связи с моей работой я делаю много тестов производительности. Для определения «узких мест» мы используем массу приложений. Например, Google Page Speed, который дает рекомендации по улучшению производительности JavaScript и рендеринга. Прежде чем я перейду к рассмотрению этих рекомендаций, нам нужно разобраться как браузеры работают с CSS

Стиль элемента применяется в момент его созданияСкопировать ссылку

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

Взгляните на этот документ:

<body>
    <div>
        <div>
            <p>Lorem Ipsum</p>
        </div>
        <div>
            <p>Lorem Ipsum</p>
            <p>Lorem Ipsum</p>
            <p>Lorem Ipsum <span>Test</span></p>
        </div>
    </div>
</body>

Браузер начинает сначала и видит элемент <body>. В этот момент времени считается, что этот узел не имеет дочерних узлов. Ничего более не рассматривается. Браузер определяет соответствующий ему обобщенный набор стилей и применяет его к элементу. Какой шрифт, цвет, интерлиньяж? После того, как это все будет выяснено, элемент отображается на экране.

Далее браузер видит элемент <div> со значением атрибута ID content. И снова в этот момент времени браузер считает его пустым. Он не рассматривает другие элементы. Как только браузер рассчитает стиль, элемент отображается на экране. Затем браузер определяет нужно ли перерисовать <body> — стал ли элемент шире или выше? Я подозреваю, что там есть масса других проверок, но изменение ширины и высоты — самый распространенный способ повлиять на отображение родительского узла.

Процесс продолжается, пока браузер не достигнет корневого узла документа.

Вот как выглядит визуализация процессов перерисовки в Firefox:

CSS-селекторы анализируются справа налевоСкопировать ссылку

Чтобы определить, применяется ли CSS-правило к определенному элементу, браузер рассматривает селектор справа налево.

Если у вас есть селектор body div#content p { color: #003366; }, то, когда каждый элемент появляется на странице, браузер проверяет, является ли он параграфом. Если да, он начинает подниматься вверх по DOM и ищет <div> со значением атрибута ID равным content. Если он его находит, то продолжает подниматься по DOM пока не найдет <body>.

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

ПравилаСкопировать ссылку

Возвращаясь к Page Speed, давайте рассмотрим несколько его рекомендаций:

  • Избегайте селектора по потомку: .content .sidebar;
  • Избегайте селектора по дочернему элементу: .content > .sidebar и селектора по следующему элементу: .content + .sidebar.

Конечно, селекторы по ID — самые быстрые. Проверить применимость селектора #content к рассматриваемому элементу можно очень быстро. Есть у него этот ID или нет? Селекторы по классу практически такие же быстрые, так как нет никаких связанных элементов, которые надо проверять.

Селекторы по потомкам, такие как .content .sidebar — более ресурсоемкие, так как, чтобы определить надо ли применять правило к .sidebar, браузер должен найти .content. Cелектор по дочернему элементу, например, .content > .sidebar, лучше селектора по потомку, так как браузер должен проверить только один элемент вместо множества. (К сожалению, селекторы + и > не поддерживаются IE6. Так что если его поддержка актуальна для вас, то про них придется забыть — прим. переводчика).

Селектор по тегу и универсальный селекторСкопировать ссылку

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

Рассмотрим следующий пример:

#content * { color: #039; }

Так как в селекторе присутствует ID, то можно подумать, что этот селектор обрабатывается очень быстро. Проблема в том, что браузер обрабатывает селектор справа налево и сперва проверяется универсальный селектор. Для того чтобы браузер мог определить, должен ли цвет текста элемента быть тёмно-синим, надо проверить каждый предок элемента, пока не будет найден предок с атрибутом ID равным content или не будет достигнут корень документа.

И это должно быть сделано для каждого элемента на странице.

Теперь, когда мы понимаем, как элемент обрабатывается, как определяется применимость правил к элементу, давайте рассмотрим пример.

Почему IE долго не поддерживал

:last-childСкопировать ссылку

Все жаловались: у всех браузеров, кроме IE, есть поддержка :last-child (она появилась только в IE9!) Некоторые могли подумать насколько же сложнее сделать :last-child, если уже реализован :first-child?

Давайте представим, что мы — браузер и мы парсим документ-пример, который я приводил ранее.

. module > p:first-child { color: red; } /* Первое правило */
.module > p:last-child { color: blue; } /* Второе правило */

Когда мы рассматриваем внутренности первого <div>, мы видим, что там есть параграф. Браузер видит что-то вроде этого:

<div>
    <p>Lorem Ipsum</p>

Нужно ли применить первое правило к параграфу? Да, это параграф; да, это первый дочерний узел; и, да, это непосредственный потомок элемента с классом

module.

Нужно ли применить к этому параграфу второе правило? На данный момент это последний элемент. Но мы могли ещё не загрузить все элементы и не можем быть уверены, что он останется последним.

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

Как на самом деле это делает браузер?Скопировать ссылку

Я не мог сказать с абсолютной уверенностью, как браузеры парсят :last-child, так что я создал несколько тестов:

  • Статическая HTML-страница;
  • Рендеринг с паузами в одну секунду.

Первый пример весьма скучен. В любом браузере, включая IE9, всё отображается корректно. Внутри <div> первый элемент красный, а последний синий. Но посмотрите на второй пример, и вы увидите интересные отличия в поведении браузеров.

Второй пример приостанавливается перед добавлением каждого параграфа в <div>.

В Firefox первый параграф изначально отображается синим. Когда загружается второй параграф, первый становится красным и второй — синим. Когда загружается третий параграф, второй отображается цветом по умолчанию и третий отображается синим. Firefox рассматривает каждый элемент, который был загружен в качестве последнего элемента, пока не будет загружен ещё один.

В Safari, Chrome и Opera мы увидим другой подход. Первый параграф красный. Второй отображается чёрным. Последний параграф отображается чёрным, пока браузер не получит закрывающий тег </div>. В этот момент последний параграф становится синим. Эти браузеры не рассматривают элемент как последний, пока не будет закрыт родительский.

В Internet Explorer 9 Beta я нашел интересный баг. В то время, как статическая страница отображается корректно, версия с паузами отрабатывает с любопытным побочным эффектом. Первый параграф синий, второй параграф синий и затем — третий. Когда закрывающий тег </div> загружен, предпоследний параграф меняет цвет на чёрный. IE9 пытается обрабатывать селектор как WebKit и Opera, но… м-м… не выходит. Надо бы отправить багрепорт в Microsoft.

Почему у нас нет селектора по родителю?Скопировать ссылку

Уже дано достаточно пояснений, чтобы можно было вернуться к оригинальному вопросу. Проблема не в том, что у нас не может быть селектора по родителю.

Проблема в том, что мы столкнемся с проблемами быстродействия, когда дело дойдет до определения того, какие CSS-правила применимы к данному элементу. Если Google Page Speed не рекомендует использование универсальных селекторов, то можно гарантировать, что селектор по родителю будет первым в списке ресурсоемких селекторов, намного опережая все проблемы с производительностью, которые могут быть вызваны использованием универсального селектора.

Давайте посмотрим почему. Первым делом давайте приведём пример синтаксиса для селектора по родителю.

div.module:has(span) { color: green; }

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

Посмотрите на часть нашего документа:

<div>
    <p>Lorem Ipsum</p>
    <p>Lorem Ipsum</p>
    <p>Lorem Ipsum <span>Test</span></p>
</div>

Исходя из того, что мы видим, .module будет отображён без использования правила, применяемого селектором по родителю. Когда будет загружен первый элемент <p>, нужно повторно оценить применимость селектора по родителю к <div>. Нужно сделать это снова для следующего параграфа. И снова, для следующего. Наконец, когда <span> загружен, селектор по родителю будет применен к родительскому <div>, и элемент нужно будет повторно перерисовать.

И что теперь? Теперь, если изменится любое наследуемое CSS-свойство, каждый потомок должен будет повторно анализироваться и перерисовываться. Ох…

Почему проблему можно решить с помощью JavaScript?Скопировать ссылку

Это только кажется, что JavaScript решает проблему. В общем случае JavaScript-заплатки (заплатки — polyfills — части кода, обеспечивающие функциональность, которую должен обеспечивать браузер — прим. переводчика). Или регрессивное усовершенствование (или как там вы, молодежь, это сейчас называете) запускаются только один раз, после полной загрузки DOM.

Для того чтобы действительно имитировать поведение CSS, любой скрипт, решающий эту проблему, должен запускаться после отображения каждого элемента на странице, чтобы определить, нужно ли применить нашу «заплатку». Помните CSS-expressions в Internet Explorer? Именно по этой причине они вызывали такие проблемы с производительностью.

Не невозможноСкопировать ссылку

Появится ли когда-нибудь селектор по родителю? Возможно. То, что я описал, не невозможно. На самом деле — наоборот. Это значит только то, что нам придётся иметь дело с ухудшением производительности из-за использования этого селектора.

Идентификатор Css с классом — родительский селектор

спросил

Изменено 2 года, 8 месяцев назад

Просмотрено 454 раза

Html:

Родительский:

div

Дочерний:

nav role=»navigation» aria-label=»Main Menu»

Я пытаюсь скрыть навигацию по сайту. Как правильно выбрать недвижимость здесь. Я пробовал:

#sticky-header-sticky-wrapper.sticky-wrapper is-sticky #site-navigation {display:none;}

  • css
  • css-селекторы

1

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

.класс1.класс2 { ... } .

Вы можете сделать что-то вроде следующего для своего варианта использования:

 #sticky-header-sticky-wrapper.sticky-wrapper.is-sticky #site-navigation {
  дисплей: нет;
} 
 <дел>
  
  
НЕ NAV

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

В этом случае это будет работать так же:

 #сайт-навигация {
  дисплей: нет;
}
 
 #sticky-header-sticky-wrapper. sticky-wrapper.is-sticky #site-navigation {
  дисплей:нет;
}
 

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

Я бы рекомендовал просто использовать селектор классов, например:

 .main-navigation: {
  дисплей:нет;
}
 

Таким образом, селектор легче читается и менее специфичен для CSS, что означает, что вы можете очень легко переопределить его с помощью другого селектора класса.

Идентификатор уникален, вы можете сделать это так:

 div.sticky-wrapper.is-sticky #site-navigation{
  дисплей:нет;
}
 

или

 div#sticky-header-sticky-wrapper.sticky-wrapper.is-sticky #site-navigation {
     дисплей:нет;
}
 

Но лучший и самый простой способ —

 #site-navigation{
    дисплей:нет;
}
 

добавить «.» перед (липким) классом.

Зарегистрируйтесь или войдите в систему

Зарегистрируйтесь с помощью Google

Зарегистрироваться через Facebook

Зарегистрируйтесь, используя электронную почту и пароль

Опубликовать как гость

Электронная почта

Требуется, но не отображается

Опубликовать как гость

Электронная почта

Требуется, но не отображается

как работает селектор CSS

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

Это также относится к тому, как мы выбираем элементы — от родителя к дочернему, без возможности выбора родителей, от до .

В спецификации CSS Selectors 4 CSS представляет новый селектор под названием :has() , что, наконец, позволяет нам выбрать родителей. Это означает, что мы сможем настроить таргетинг на элемент CSS, у которого есть конкретных дочерних элемента. Это уже поддерживается в Safari , а также в Chrome 105 . Полная таблица поддержки показана ниже:

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

Как родительские селекторы работают в CSS

В CSS, если мы хотим что-то выбрать, мы используем селекторы, которые спускают DOM. Например, выбор тега p в теге div выглядит следующим образом:

 div p {
    красный цвет;
}
 

До сих пор мы не могли выбрать тега div , внутри которых было тега p , и это означало, что нам пришлось прибегнуть к Javascript. Основная причина, по которой это не было реализовано в CSS, заключается в том, что это довольно дорогая операция. CSS относительно быстро анализируется, но выбор родительских тегов требует значительно большего объема обработки.

Используя селектор :has , мы теперь можем выбрать элемента div , у которых есть p дочерних элемента, или любую обычную комбинацию селекторов. Например, выбор элемента div с дочерним элементом p теперь выглядит так:

 /* Цвет элемента div: красный; */
div: имеет (р) {
    красный цвет;
}
 

Это сделает любой div с дочерним элементом p красным.

Комбинирование родительского выбора с другими селекторами

Как и любой другой селектор CSS, мы можем комбинировать его для определенных обстоятельств. Например, если вы хотите выбрать только div теги, которые имеют прямые span дочерние элементы:

 div:has(> span) {
    красный цвет;
}
 

Как следует из словаря :has , он не ограничивается только родительским выбором. Например, ниже мы можем выбрать span , который :has родственный div :

 span:has(+ div) {
    красный цвет;
}
 

Или даже, выбрав элемент, у которого есть дочерний элемент , а не , с помощью селектора :not() . Например, следующее выберет любой div, который делает не иметь p дочерний элемент:

 div:not(:has(p)) {
    красный цвет;
}
 

Выбор элементов, которые содержат только текст в CSS

Одна очень распространенная проблема в CSS заключается в том, что тег :empty не выбирает элементы, содержащие какой-либо текст, поэтому иногда элемент может содержать один пробел, и :empty будет не применять. Селектор :has дает нам возможность выбирать элементы, которые содержат только текстовые узлы и не содержат других дочерних элементов.

Хотя это не идеальное решение для простых :пустых элементов с пробелами (поскольку это выберет любой элемент только с текстом и без дополнительных элементов DOM HTML) — это дает нам возможность выбирать элементы DOM только с текстовыми узлами. , что раньше было невозможно. Мы можем добиться этого с помощью следующего кода:

 div:not(:has(*)) {
    фон: зеленый;
}
 

Заключение

С добавлением поддержки селектора :has() в Chrome 105 родительский выбор быстро становится реальностью, которую мы скоро сможем использовать в реальных проектах. На данный момент с поддержкой Safari легко протестировать и посмотреть, как он будет работать в будущем. Это дает дополнительное преимущество, позволяя сократить использование решений Javascript для выбора родительских элементов, что довольно часто встречается во многих приложениях и продуктах.

Оставить комментарий

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *