Главная » Статьи » Как работает в CSS display: contents;

Как работает в CSS display: contents;

Как работает в CSS display: contents;

От автора: как я часто говорю, каждый элемент в дереве документа представляет собой прямоугольный блок. В общем, этот «прямоугольный блок» состоит из двух разделов. Во-первых, у нас есть сам блок, который состоит из областей границы, отступов и полей. Во-вторых, у нас есть содержимое блока; область содержимого. Для работы с ней нам пригодится display contents CSS.

С помощью свойства CSS display мы можем управлять тем, как этот блок и его дочерние элементы выводятся на странице. Мы можем вставить блок в ряд с элементами одного уровня с помощью inline. Мы можем сделать так, чтобы блок вел себя, как таблица, с помощью table. Мы можем даже поместить блок в совершенно другую позицию по оси z с помощью absolute.

Для свойства display есть только два значения, которые определяют, будет ли элемент, определенный в разметке, в принципе генерировать блок. Значение none приведет к тому, что на странице не будет отображаться ни сам блок, ни его содержимое. С другой стороны, новое значение contents приведет к тому, что содержимое блока будет выведено как обычно, но окружающий его блок будет полностью опущен.

Что произойдет, если вы используете display: contents?

Самый простой способ понять, что происходит при использовании display: contents — представить, что из разметки были удалены теги открытия и закрытия элемента. В спецификации говорится: Для целей создания блоков и определения макета элемент должен обрабатываться так, как если бы он был заменен в дереве элементов его содержимым.

Возьмем, например, следующую разметку:

<div class="outer"> I’m some content <div class="inner">I’m some inner content</div>
</div>

И следующие стили:

.outer { border: 2px solid lightcoral; background-color: lightpink; padding: 20px;
} .inner { background-color: #ffdb3a; padding: 20px;
}

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

Однако, если мы добавим display: contents для элемента .outer, вот как он будет отображаться:

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

I’m some content
<div class="inner">I’m some inner content</div>

Как насчет…?

Это правило CSS прямого действия, и для него существует очень немного специфических случаев, о которых стоит упомянуть. Мы должны помнить, что правило display: contents влияет только на визуальное отображение блока на странице; оно не влияет на разметку внутри документа.

Как насчет атрибутов элемента?

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

Мы по-прежнему можем выбирать элемент по его идентификатору, например, сделав ссылку на него, используя aria-labelledby.

<div id="label" style="display: contents;">Label here!</div>
<button aria-labelledby="label"><button>

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

<div id="target" style="display: contents;">Target Content</div> <script> window.location.hash = "target"; // => Ничего не происходит
</script>

Как насчет событий JavaScript?

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

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

<div class="outer">I’m some content</div> <script> document.querySelector(".outer").addEventListener("click", function(event) { console.log(this); // => <div class="outer"></div> });
</script>

Как насчет псевдоэлементов?

Псевдоэлементы элемента с display: contents считаются частью его дочерних элементов, поэтому отображаются как обычно.

<style> .outer { display: contents; } .outer::before { content: "Before" } .outer::after { content: "After" }
</style> <div class="outer">I’m some content</div>

Приведенная выше разметка дает следующий результат:

Как насчет элементов формы, изображений и других замещаемых элементов? Замещаемые элементы и элементы формы ведут себя по другому, когда к ним применяется display: contents.

Замещаемые элементы

Замещаемыми элементами являются такие элементы, как изображения, их отображение и «блоки» контролируются внешним ресурсом. Попытка удалить блок для таких элементов не имеет смысла, потому что не совсем понятно, что такое «блок». Для этих элементов display: contents работает так же, как и display: none. Весь блок и содержимое элемента вообще не выводятся на странице.

Элементы формы

Многие элементы формы не состоят из одного «блока». Они выглядят так с точки зрения нас, авторов веб-страниц. Но под капотом они состоят из нескольких меньших элементов. Подобно замещаемым элементам, нет смысла убирать блок, потому что у нас есть несколько блоков. Поэтому для элементов формы, таких как select, input и textarea, display: contents работает так же, как и display: none.

Как насчет кнопок и ссылок?

Элементы <button> и <a> не имеют никакого особого поведения при указании display: contents. Однако мы отдельно рассмотрим, как это правило влияет на них, потому что это может быть не очевидным.

Кнопки

Кнопки не являются одним из элементов формы, которые состоят из нескольких блоков. Поэтому display: contents просто удаляет окружающий блок, оставляя содержимое отображаемой кнопки. Если кнопка используется в форме, нажатие на нее по-прежнему приведет к отправке данных формы, и, как мы уже рассматривали, прослушиватели событий для кнопки будут функционировать нормально.

Ссылки

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

Чем полезно display: contents?

Раньше нам приходилось компоновать HTML таким образом, чтобы это работало как семантически, так и в целях определения стилей с помощью CSS. Это приводило к случаям, когда у нас было либо слишком много ненужных элементов для обертывания, либо слишком мало элементов, чтобы обеспечить прямое указание стилей одноуровневых элементов. Последнее стало особенно актуальным с введением CSS Grid Layout, который по крайней мере на данный момент работает с потомками элементов первого уровня.

Возьмем, к примеру, следующий макет:

У нас есть две «карточки», расположенные рядом друг с другом, каждая из них содержит заголовок, параграф и подвал. Мы хотим, чтобы каждый из разделов внутри каждой карточки был одинаковой высоты, независимо от его содержимого (например, заголовок первой карточки содержит только 1 строку, тогда как третья карточка содержит 3-строчный заголовок, но высота заголовка первой карточки должна соответствовать заголовку второй).

Мы могли бы создать такой макета с помощью CSS Grid, но нам нужно, чтобы все элементы в каждой «карточке» были дочерними элементами одного уровня. Таким образом, нам, возможно, придется скомпоновать HTML следующим образом:

<div class="grid"> <h2>This is a heading</h2> <p>...</p> <p>Footer stuff</p> <h2>This is a really really really super duper loooong heading</h2> <p>...</p> <p>Footer stuff</p>
</div>

И мы могли бы применить следующие стили —

.grid { display: grid; grid-auto-flow: column; grid-template-rows: auto 1fr auto; grid-template-columns: repeat(2, 1fr); grid-column-gap: 20px;
}

Хотя это не является полностью некорректным способом структурирования документа, вероятно, имеет смысл сгруппировать каждую «карточку» внутри элемента article. Вот здесь нам пригодится display: contents. Мы можем совместить лучшее из обоих способов — задать разметку разметки так, чтобы это имело смысл семантически, но наш CSS будет действовать таким образом, который подходит для макета.

<div class="grid"> <article style="display: contents;"> <h2>This is a heading</h2> <p>...</p> <p>Footer stuff</p> </article> <article style="display: contents;"> <h2>This is a really really really super duper loooong heading</h2> <p>...</p> <p>Footer stuff</p> </article>
</div>

Задав то же код CSS, что был приведен выше, мы можем достичь желаемого макета.

Использование display: contents

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

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

article { display: grid; grid-template-rows: 200px 1fr auto; /* т.е. указать фиксированную высоту заголовка */
} @supports (display: contents) { article { display: contents; }
}

Источник: https://bitsofco.de/

Редакция: Команда webformyself.