Продвинутый HTML и CSS. Часть 2 — Тренажёр HTML Academy
Продвигаемся дальше по джунглям вёрстки. Прошлая часть была почти полностью про HTML, а в этой наоборот — один только CSS. Разберемся с ворохом селекторов, разложим с их помощью пасьянс, подробно поговорим о каскадировании и наследовании и изучим как работают фоны.
Здесь будет много испытаний и даже парочка практических заданий, которые «Вызовы». Испытания, кстати, сложные. Особенно в частях про селекторы. В общем, есть чем заняться и в этот раз действительно интересно.
Порядок будет иной — я склею две части про «Селекторы» в одну и, наверное, приступим к ним в последнюю очередь, начнем с лёгкого и постепенно дойдем до сложностей.
Фоны
Начнем с самого лёгкого в плане теории и, наверное, самого долгого в плане испытаний. Мы будем разбираться с группой свойств background и парочкой хитростей в работе с ними.
Не буду ничего придумывать — пойдем в порядке указанном в тренажёре:
background-color
— задаёт цвет фона, полностью заливает фон указанным цветом, про способы указания цвета мы разбирались в свойствеcolor
в одной из прошлых частей;background-image
— задаёт фоновое изображение с помощью значенияurl("img.png")
, на первый взгляд тривиальное свойство, но на самом деле скрывает в себе много возможностей, о чем нам обещают рассказать в более поздних частях тренажёра;background-repeat
— задаёт повторяемость фонового изображения, по умолчанию стоит значениеrepeat
— фон повторяется и горизонтально и вертикально, занимая всё пространство, другие значениеrepeat-x
иrepeat-y
— повторять только по горизонтали или вертикали соответственно, иno-repeat
— не повторять, то бишь будет одно изображение;background-position
— управляет положением фонового изображения, принимает два значенияx y
— гдеx
позиция по горизонтали, аy
по вертикали, за значения принимаются ключевые слова —top
,left
,center
и так далее, проценты%
и пикселиpx
, причем значения можно комбинировать —-20px 50%
;background-attachment
— управляет прикреплением фонового изображения, значение по умолчаниюscroll
прокручивает изображение вместе содержимым блока, а с помощью значенияfixed
можно зафиксировать изображение относительно экрана.
У всего этого безобразия есть универсальный вариант записи с помощью сокращённого свойства background
. Как и в других подобных свойствах, например, в font
— в свойстве background
важен порядок компонентов.
Порядок именно тот, что указан выше — сверху вниз от -color
до -attachment
, при этом если какой-то из компонентов не указан — берётся значение по умолчанию:
background: #e74c3c; /* просто цвет, остальное по умолчанию */ background: url("img.png") no-repeat; /* картинка без повторений */ background: url("img.png") 10px 20px; /* с повтором, но смещена */
По поводу форматов — им уделено несколько уроков, но на мой взгляд оно того не стоило. Мы уже разбирали форматы изображений в одной из прошлых частей и в этот раз добавился только PNG-8.
Хитрости
Перейдем к хитростям — для моего дилетантского ума было сюрпризом использование спрайтов для иконок. Насколько я понял, это не очень распространённый способ, но весьма интересный.
Спрайт — это одно изображение в котором отрисовано несколько, например, иконок. Однако, впервые я со спрайтами столкнулся давно в RPGM — простеньком игровом движке, там именно через спрайты задаётся анимация — несколько кадров отрисовано в одном изображении. Как интересно, в вебе тоже надо попробовать.
Вернемся к иконкам — они расположены в виде такой таблички, и достаточно просто каждую можно найти по координатам:
Таким образом снижается нагрузка на сервер и даже потенциально уменьшается вес страницы, так как одна картинка — один запрос на сервер.
Вторая хитрость не указана в теории, зато написана в последнем испытании. Связана она с сокращенным свойство background
— оказывается, в него можно вписывать сразу несколько фонов через запятую:
background: url("img1.png") no-repeat 0 0, url("img2.png") repeat-x 50% 50%, url("img3.png");
Таким образом, даже к одному блоку можно прикрутить какой-нибудь красивый параллакс-эффект. Судя по всему, к этому мы вернёмся по позже, в блоках продвинутого уровня, но сейчас в испытании это было крайне полезно. Кстати, испытания.
Испытания
Их два и оба они просто до безобразия ужасны. И даже не в сложности дело — в них нужно подбирать позицию изображений на глаз. То есть всё сверстать дело двух минут, а потом минут 20 ты сидишь и меняешь background-position у нескольких изображений, чтобы попасть в исходник.
Первое испытание «Котокомикс» еще ладно, но тоже заметьте позицию 41px, блин, по горизонтали для селектора .block8
Я специально заскринил результат — если кому надо списывайте, так как это действительно не вопрос мастерства, а вопрос подбора циферок.
Второе испытание «CAT Academy» это жесть в кубе.
Я сначала задавал все в процентах, что убавило мне количество нервных клеток. Потом решил пойти от фона — звёздочки и надпись, в них попал почти сразу. А дальше минут 10-15 выравнивание котов. Feel free списывать координаты.
Короче, испытание не то чтобы плохое, но ленивое. Зато запоминающиеся, у меня даже в статистике по выдаче моего блога в Google эти испытания есть.
Наследование и каскадирование
Перейдем к сложным для понимания вещам. Сразу скажу — эта часть была между двух частей про Селекторы, которые далее. Поэтому, возможно, здесь будут селекторы которые мы еще раз повторим в следующей части.
Про наследование и каскадирование свойств в CSS мы уже немножечко знаем из части «Знакомства», теперь разберёмся подробно.
Наследование
HTML документ имеет иерархическую структуру — каждый элемент кроме первого, корневого <html>
, имеет одного родителя и располагается внутри него. В тренажёре это назвали Иерархическое дерево:
В <html>
вложены <head>
и <body>
, в которые, в свою очередь, вложены их дочерние элементы и так далее, «глубже» по структуре.
Наследование в CSS — это способность родителя-элемента передавать свои значения свойств всем его дочерним элементам.
Если задать <body>
свойство color: red;
, то текст станет красного цвета во всем документе, если только какому-то конкретному дочернему элементу не задан конкретный цвет.
Однако, наследуются далеко не все свойства. Например, наследуются практически все свойства относящиеся к параметрам текста — color
, font
, text-align
, line-height
и другие. Также, например, visabilty
— скрывая родительский элемент, скроются и все дочерние.
Перечислять все наследуемые и не наследуемые свойства смысла нет, чаще всего наследственность работает логично, например, фон background
не наследуется по понятным причинам. Полный список можно и периодически нужно сверять со спецификацией.
Однако в CSS есть способ заставить элемент унаследовать какое-либо свойство родителя, например, тот же background
, с помощью значения inherit
:
background: inherit;
Каскадирование и специфичность
Каскадность в CSS означает, что к одному элементу в итоге применяется набор CSS-правил из разных источников. По разным причинам — стили браузера по умолчанию, наследуемость, несколько разных селекторов могут назначать одному и тому же элементу несколько свойств.
Отсюда выходит, что к одному элементу может применяться несколько раз одно и то же свойство, но с разными значениями. Какое значение в итоге будет?
В CSS существуют три основных фактора, определяющих порядок применения свойства к элементу — порядок исходного кода, специфичность селектора, важность. Причем, следующий отменяет предыдущий, то есть специфичность важнее порядка кода.
Сразу разберёмся с порядком кода, это самое простое:
p {color: red;} p {color: green;}
В данном случае текст будет зелёный green
, так как указан позже и таким образом переопределил значение свойства color
.
Переходим к сложному — специфичность. Возьмем объяснение из тренажёра, оно хорошо описывает термин:
Чем меньшее количество элементов потенциально может выбрать селектор, тем он специфичнее.
То есть, селектор p {}
применятся ко всем <p>
в документе — он менее специфичен, чем селектор .text {}
, который применяется только к элементам с классом text
. В свою очередь селектор .text {}
менее специфичен чем селектор p.text {}
, который применяется только к <p calss="text">
.
Селектор по id #text {}
еще более специфичен чем селекторы по классу, так как может применятся только к одному элементу на странице. И как следствие, селектор p.text {}
менее специфичен чем #text {}
.
Еще большим приоритетом обладают стили в атрибуте style=""
, так как применяются к одному конкретному элементу в вёрстке.
Наибольший или «усиленный» приоритет, даже больший чем стиль в атрибуте, имеет важное свойство, которое определяется ключевым словом !important
в конце этого свойства. Применять его стоит только в критичных ситуациях.
Чтобы как-то структурировать показатели специфичности умные разработчики придумали своего рода индексацию. Селекторы разделены на четыре группы — A, B, C и D — каждая группа это порядок специфичности, от самого специфичного A до наименее специфичного D.
- Группа A отвечает за встроенные стили, то есть определенные в атрибуте
style=""
, A=1 если стиль встроен в вёрстку, и A=0 если нет; - Группа B отвечает за количество идентификаторов #id в селекторе;
- Группа C отвечает за количество классов
.class
, псевдоклассов:hover
и атрибутных[attr]
селекторов (про них скоро узнаем); - Группа D отвечает за количество селекторов по тегу
a
и псевдоэлементов::after
.
Я рехнусь описывать все здесь в текстовом виде, поэтому в табличке на картинке:
При этом надо понимать, что это не «баллы» специфичности. Например, селектор с идентификатором #id
и классом .class
имеет индекс специфичности 0.1.1.0, его часто можно увидеть без точек — 0110, что как раз может сбивать с толку.
Очевидно, что 0.1.1.0 специфичнее чем, например, 0.0.1.1. Однако, если представить себе, что у вас монструозный селектор из более десяти классов 0.0.10.0, то он все равно менее специфичен чем селектор из одного идентификатора 0.1.0.0, хотя если писать без точек будет 00100 и 0100.
Поэтому я рекомендую и сам в дальнейшем если буду прибегать к этой индексации, писать с точками или как-то иначе отделять группы.
Учитывая наследуемость, каскадирование и специфичность, в современной разработке часто используется приём перекрёстного наследования. Он заключается в том, что бы задать базовый стиль для какого-то типа элементов, например для кнопок .button
, а затем определить вспомогательные стили для разных нужд, например .button-open
, .button-clear
и т. д.
Таким образом у всех кнопок на сайте будет определен базовый стиль, а специфичным кнопкам будет добавляться класс, переопределяющий или добавляющий свойства.
Испытание и практика
Разбираемся со взломом! В испытании напортачил кот-конкурент, указав стили в разметке, а наша задача переопределить их применяя полученные знания.
В вызове задачка примерно такая же, но по сложнее — здесь взломан профиль и нужно использовать псевдоклассы из части про селекторы.
Селекторы
Перейдём к самому интересному! В тренажёрах им уделено две части — в первой основные селекторы и базовые псевдоклассы, а во второй уже почти полный набор комплексных селекторов, псевдоклассов и даже парочка псевдоэлементов сюда попали заодно.
Все уроки это знакомство с новым селектором, я же напишу в стиле, что был у меня в части про поля форм в прошлой статье. Иначе, если размусоливать каждый селектор, то статья выпадет из экрана и убежит.
Типы селекторов
Селектор по тегу a {}
— содержит стили для всех элементов с указанным тегом, например, все изображения img {}
, все параграфы p {}
и так далее.
Одно правило может содержать несколько селекторов, в этом случае они записываются через запятую — a, p {}
содержит стили общие для ссылок и параграфов. Это относится ко всем селекторам, не только по тегу.
Селектор по классу .class {}
— содержит стили для всех элементов с указанным классом в соответствующем атрибуте class=""
. Основной инструмент при работе со стилями.
Если нужно обратиться к определённым элементам, например <p>, с определённым классом, то в их селектор записывается тег с классом без пробелов — p.class {}
.
Если у нужного элемента несколько классов и нам нужно задать стиль конкретному набору элементов с этими классами, то в селектор записываются эти классы без пробелов — .first.second {}
. Работает, на самом деле, не только с классами, но и с другими селекторами.
Селектор по идентификатору #id {}
— содержит стили для конкретного элемента с указанным атрибутом id=""
. Стилизация через селекторы по ID считается плохой практикой, хотя иногда может спасти ситуацию.
Селектор по атрибуту input[type="text"]
— содержит стили для указанных элементов с указанным атрибутом и значением. В самом начале прохождения тренажёров я подметил, что мы можем меняем стиль через lang=""
, как оказалось все еще интереснее — можно выбирать по сути любой атрибут и стилизовать через него элемент.
Контекстный или вложенный селектор p a.link {}
— содержит стили для указанных элементов, внутри указанного родительского элемента. В нашем примере p a.link {}
содержит стили всех ссылок <a>
с классом link
внутри параграфов <p>
. Вложенность задаётся именно через пробел между селекторами. Это одна из самых распространённых конструкций.
Дочерний селектор ul > li {}
— содержит стиль указанного дочернего элемента, то есть только ближайшего потомка, указанного родительского элемента. Отличие от контекстного селектора в том, что он влияет на всех потомков, то есть контекстный селектор ul li {}
будет влиять как на свои пункты списка <li>
, так и на вложенные внутрь него списки, как бы рекурсивно. Дочерний же селектор влияет только на ближайших потомков.
Соседний селектор .first + .second {}
— содержит стиль второго селектора, в нашем случае .second
, применимые только если этот элемент находится сразу после элемента с первым селектором, в нашем случае .first
. Супер странная конструкция, могу примерно представить где её применять, но с натяжкой.
Селектор последующих элементов .first ~ .second {}
— похож на соседний селектор, но второй элемент может находится не сразу за первым, а главное после него, то есть между ними могут быть одноуровневые элементы с другими селекторами.
Это всё касаемо селекторов без псевдоклассов в тренажёрах. Беглый гуглинг показал, что их больше, включая комплексные селекторы по атрибутам. Но сейчас не будем на этом заострять внимание, я уверен мы еще вернёмся к ним.
Псевдоклассы
Псевдоклассы дополняют селектор, указывая на его состояние или положение. Они работают со всеми селекторами и конструкциями указанными выше, то есть и контекстные, и дочерние, и объединённые, и так далее конструкции можно дополнять псевдоклассами.
Первый дочерний элемент :first-child
— позволяет задать стиль первого указанного дочернего элемента. Тут важно понять логику — псевдокласс задаётся дочернему элементу, а не родителю. Селектор родителя не столь важен.
Последний дочерний элемент :last-child
— аналогично, задаёт стиль последнему указанному дочернему элементу. Логика такая же как и с первым.
Определённый дочерний элемент :nth-child(выражение)
— позволяет задать стиль указанному или даже группе указанных дочерних элементов. В одном этом псевдоклассе таиться огромное количество возможностей. Долго не мог понять что за «нтх», потом как-то само дошло, что читается «N-th», то бишь «Энное», и это помогло понять логику этого псевдокласса. N — number, мы задаем в скобочках.
В скобочках мы задаем либо просто число, например li:nth-child(3)
— это третий дочерний элемент списка, либо ключевое слово или целое выражение. Например, :nth-child(2n)
выберет все чётные элементы, что можно записать через ключевое слово :nth-child(even)
. А нечётные можно задать с помощью :nth-child(2n+1)
или :nth-child(odd)
.
Вычисления в скобочках работают по формуле an+b
, где n
— это счетчик итерации начинающийся с нуля, а a
и b
— целые числа, своего рода модификаторы. В случае с нечётными числами можно посчитать, почему именно формула 2n+1
выделяет нечётные элементы:
Аналогично можно выбрать, например, каждый 4-й элемент или элементы с определённым шагом. Это самая сложная часть, правда. Дальше все :nth-
псевдоклассы работают аналогично.
Определённый дочерний элемент с конца :nth-last-child(выражение)
— отличие от псевдокласса :nth-child
в том, что счет ведётся не с начала, а с конца списка дочерних элементов.
Единственный дочерний элемент :only-child
— сработает только если элемент единственный внутри родительского элемента.
Первый дочерний элемент по типу :first-of-type
— отличие от первого дочернего элемента :first-child
в том, что он ищет первый встречаемый по типу элемент. В то время как :first-child
обращается к самому первому элементу не проверяя его тип и если селекторы не совпадают, например указан li:first-child
, а первый дочерний элемент <div>
, то стили не применяются.
Последний дочерний элемент по типу :last-of-type
— аналогично и для последнего элемента, псевдокласс ищет не самый последний дочерний элемент, а последний по указанному селектору.
Определённый дочерний элемент по типу :nth-of-type()
— работает также как и :nth-child()
, но ищет только элементы указанного типа, игнорируя остальные.
Определённый дочерний элемент по типу с конца :nth-last-of-type()
— ищет указанный элемент по указанному типу с конца списка дочерних элементов.
Единственный дочерний элемент по типу :only-of-type
— работает так же как и :only-child
, но учитывает типа элемента. То есть если внутри родителя есть только один <p>
и неважно сколько других элементов, то p:only-of-type
сработает. Более дипломатичный вариант.
Пустой элемент :empty
— выбирает только полностью пустые элементы, то есть в которых нет ни одного символа, даже переноса строки.
Можно сказать разобрались с дочерними элементами и их набором псевдоклассов. Далее один специфичный псевдокласс и более знакомые мне псевдоклассы состояния.
Отрицающий селектор :not(селектор)
— с помощью этого псевдокласса можно выбрать элемент не имеющий указанный в скобочках селектор. Например, li:not(:last-child) {}
— выберет все пункты списка кроме последнего.
В скобочки можно написать только единичный селектор или псевдокласс, а также нельзя использовать двойное отрицание :not(:not())
. Но можно использовать его последовательно, например:
li:not(:first-child):not(:last-child) {} // не первый и не последний
Далее знакомые многим псевдоклассы, без которых не обходиться стилизаций ни одной ссылки даже на самом простом сайте.
При наведение курсора :hover
— позволяет задать стиль элементу при наведении на него курсора мыши. Крайне полезный псевдокласс, с помощью которого можно оживить интерфейс даже без участия JavaScript.
Не посещённая ссылка :link
— специальный псевдокласс для ссылок, позволяет настроить стиль ссылки по которой пользователь еще не переходил.
Посещённая ссылка :visited
— напротив, позволяет настроить уже посещённую ссылку.
Активная ссылка :active
— задаёт стиль ссылки в момент нажатия или при выборе её с помощью клавиши tab.
Выделенный элемент :focus
— позволяет настроить стиль выделенного элемента, например, выделяются поля формы <input>
если кликнуть на них мышью.
На этом с псевдоклассами всё! Изучение псевдоклассов сложный, но интересный опыт, на них в основном построены испытания, но про них чуть позже, а пока псевдоэлементы.
Псевдоэлементы
В этой части тренажёра упоминаются не все, а только четыре — два самых основных и ещё два на затравку.
Псевдоэлемент «до» ::before
— добавляет «псевдотег» внутрь указанного элемента до всего внутреннего содержимого элемента. Работает этот элемент как обычный <span>
, при этом он не отображается в разметке, а существует «виртуально» в памяти браузера.
Содержимое псевдоэлемента задается свойством content
, причем, чтобы псевдоэлемент появился достаточно даже пустой строки content: "";
. В остальном элемент работает как строчный и принимает любые не специфичные CSS-свойства.
Псевдоэлемент «после» ::after
— аналогично добавляет псевдотег внутрь элемента, но уже после всего внутреннего содержимого. Работает абсолютно также как и псевдоэлемент ::before
.
Эти двое, наверное, самый распространённые и лично мне по началу, когда я ни черта не разбирался в вёрстке, ломали мозг. Смотришь — на сайте контент есть, а в F12 ничего нет.
Далее два псевдоэлемента, которые работают как модификаторы уже существующего текстового контента.
Первая строка текста ::first-line
— позволяет задать стиль первой строки текста указанного элемента. Строка определяется до первого переноса. В правилах можно использовать только свойства относящиеся к текстовому оформлению.
Первый символ текста ::first-letter
— позволяет задать стиль первого символа, таким образом можно сделать «буквицу». Тоже принимает только текстовые свойства.
Испытания и Практика
Пожалуй, самые сложные испытания были именно в этих частях. Первая часть «Селекторы. Знакомство» проходила с темой биатлона. Здесь одно Испытание и один Вызов.
В Вызове нужно подобрать к CSS-правилам нужные селекторы. Основная сложность в том, что полоски целей две и они по разному стилизованы, первая базово закрыта, а вторая наоборот открыта (или наоборот, уже точно не помню), а стили уже модифицируют отдельные мишени.
При этом контейнеры мишеней — флексы, и если что-то поначалу неправильно ввести оно может даже не сразу это показать.
В испытании у нас внезапно таблица. В этот раз наоборот, CSS закрыт и нам надо подставить нужным ячейкам правильные теги и классы и поменять местами кое-где строки.
Далее часть «Селекторы. Погружение», где всё строится на игральных картах. Первое испытание, на мой взгляд, оказалось самым сложным. Нам впервые дают самостоятельно разбираться с псевдоклассами. Менять разметку нельзя, базовые стили тоже, всё только через уже существующие элементы.
Нужно выделить 9-ки, то бишь четвёртый элемент, и раскрасить определённые масти, определённые позиции карт по номеру и строчке. Здесь прям полные комбинации нескольких псевдоклассов. Очень крутая задачка.
Следующее испытание на псевдоэлементы, не такое сложное, просто детально разбираемся как они работают. Немножечко знакомимся с transform
.
И заключительное испытание — это опять раскладка. Она уже сложнее чем первая, больше логических задач. Здесь прям максимум знаний про :nth-
вычисления надо применить.
Предыдущая хардкорная раскладка подготовила, поэтому эта показалась может не легче, но не сложнее. Но это всё еще очень круто, обожаю когда сложность в понимании, а не в попадании или угадывании.
Неужели осилил? Это самый большой блок по частям — восемь штук, с большим количеством уроков, испытаний и даже практикой. Я его делал еще второпях в рамках интенсива. Следующий блок будет про построение сеток, он меньше.
Спасибо за внимание! Очень рад всем кто это прочитал и еще больше рад если кому-то это было интересно или как-то помогло.
Ссылки на предыдущие статьи по HTML Academy:
Знакомство с Веб-разработкой
Знакомство с HTML и CSS
Знакомство с JavaScript
Знакомство с PHP
Таблицы и подробно о формах
Наследование, каскады и селекторы <- Вы здесь
Блочная модель, поток и сетка на float
Гибкие флексбоксы display: flex
Удобные сетки на гридах display: grid
Пропуск блока «Погружение»
Позиционирование и двумерные трансформации
Теневое искусство и линейные градиенты
CSS-фильтры и Кекстаграм
Мастерские
Продвинутые Мастерские
...
Остальные статьи можно посмотреть у меня на главной странице блога.
Также мои соц. сетки, которые я продолжаю вести:
Мой Twitter
Мой Telegram
Мой Паблик ВК
Заходите куда удобно вам и подписывайтесь! Еще раз спасибо за внимание!