![Дочерние и контекстные CSS селекторы. CSS: дочерние селекторы](https://i1.wp.com/idg.net.ua/blog/wp-content/uploads/of-type-pseudo-classes.png)
Дочерние и контекстные CSS селекторы. CSS: дочерние селекторы
Вы дошли до третьей части этой главы. Здесь вы узнаете, как использовать селекторы дочерних элементов в CSS, а также познакомитесь с некоторыми дополнительными псевдоклассами для более точной выборки.
Дочерние селекторы CSS
В предыдущей статье мы рассказывали о родственных связях между элементами HTML-документа, в том числе и о дочерних элементах. Давайте посмотрим на примере, как эти связи можно использовать в CSS.
Представьте, что вам нужно присвоить стиль только тем тегам
Которые являются дочерними по отношению к
, не затрагивая остальные(например,
Дочерние по отношению к
Как это сделать? Очень просто: создадим дочерний селектор:
Body > p { color: #333; }
Обратите внимание на символ > после body . С помощью него вы указываете, что стиль применяется исключительно для дочерних тегов
Родителя
. Если символ убрать, то стиль применится абсолютно ко всем тегамКоторые находятся внутри тега
, хотя они могут и не являться для него дочерними.Дополнительные псевдоклассы
В CSS3 существует ряд псевдоклассов для работы с дочерними элементами. Ниже приведено описание каждого из них, примеры использования, а также отличия между псевдоклассами вида «child» и «of-type».
- :first-child – с помощью этого псевдокласса можно применить стиль к указанному элементу веб-страницы, при условии, что он является первым
- :last-child – стиль применяется к указанному элементу веб-страницы при условии, что он является последним дочерним элементом своего родителя;
- :nth-child – позволяет выбрать четные (even) и нечетные (odd) дочерние элементы; также с помощью данного псевдокласса можно стилизовать чередующиеся дочерние элементы, используя выражения вида an+b и числа;
- :only-child – применяется к дочернему элементу при условии, что тот является единственным ребенком у родителя;
- :first-of-type – стиль применяется к первому элементу указанного типа (даже если этот элемент не является первым дочерним для своего родителя и над ним находятся другие дочерние элементы других типов);
- :last-of-type – работает точно так же, как и предыдущий псевдокласс, с той лишь разницей, что стиль применяется к последнему элементу указанного типа ;
- :nth-of-type – по принципу работы похож на:nth-child , но ориентируется на тип элемента;
- :only-of-type – применяется к дочернему элементу указанного типа при условии, что тот является у родителя единственным ребенком своего типа .
Пример использования:first-child, :last-child и:nth-child
Первый ребенок
Второй ребенок
Третий ребенок
Нечетный номер |
Четный номер |
Нечетный номер |
Четный номер |
Последний ребенок
/* CSS */ p:first-child { font-weight: bold; text-transform: uppercase; } p:last-child { font-style: italic; font-size: 0.8em; } p:nth-child(3) { color: red; } tr:nth-child(odd) { background-color: #A2DED0; } tr:nth-child(even) { background-color: #C8F7C5; } Скриншот: применение:first-child, :last-child и:nth-child
Мы написали CSS стиль для дочерних элементов простого HTML-документа, где тег
,
Ячейка 1.1 | Ячейка 1.2 |
Ячейка 2.1 | Ячейка 2.2 |
А теперь вопрос. Как вы поступите, если вам понадобится с помощью дочерних селекторов пройти от тега к ячейкам? Если вы решили написать вот так, то это неверно:
table > tr > td { свойство: значение; свойство: значение; ... }
А догадываетесь, почему неверно? Нет? Дело в том, что вы забыли еще про один элемент - , который даже если не указан явно, то все равно незримо присутствует в каждой HTML-таблице. Просто у него нет обязательных открывающих и закрывающих тегов, поэтому про него часто забывают. Кстати да, в HTML-учебнике мы его с вами не проходили, так как в реальности его явное указание редко когда нужно. Ну, думаю вы уже догадались, какая будет правильная запись, но я вам все же покажу.
table > tbody > tr > td { свойство: значение; свойство: значение; ... }
Кстати, не только таблицы имеют элементы с необязательными открывающими тегами, есть и еще такие элементы. Просто на практике «проблемы забывчивости» возникают чаще всего именно с таблицами, поэтому я и заострил ваше внимание на этом.
Домашнее задание.
- Установите на странице шрифт Arial с размером 0.9em и какой-нибудь фон.
- Напишите на странице несколько заголовков и параграфов, измените размер и цвет текста заголовков так, как посчитаете нужным.
- Сделайте так, чтобы ссылки непосредственно в параграфах страницы были синего цвета без подчеркивания. Но при этом, если ссылки дополнительно обрамляются любым тегом, например для курсива, то они должны отображаться уже с подчеркиванием и красного цвета. Еще раз заостряю ваше внимание на том, что не надо прописывать стили ссылок с каждым обрамляющим тегом в отдельности, сделайте универсально. Как? Подумайте.
- Создайте небольшое меню в правой части страницы и зафиксируйте его, для этого вам понадобится свойство и еще парочка сопутствующих ему свойств, там разберетесь. При этом, пусть меню не только остается на месте в пределах окна браузера, но и при скроллинге не наползает на содержимое страницы.
Думаю последний пункт урока будет для вас самым сложным, но не пытайтесь сразу полностью погрузиться в освоение позиционирования. Просто сделайте домашнее задание. Кстати, этот пункт не будет работать в Internet Explorer 6.0, так как старичек не понимает фиксированное позиционирование.
Дочерние элементы - это элементы, которые располагаются непосредственно внутри родительского элемента. Обратите внимание на слова: непосредственно внутри.
Давайте рассмотрим пример простого html-кода:
Абзац и в нём жирный элемент, и вот наклонный элемент.
Тут жирный и подчёркнутый и наклонный элементы.
В этом коде два абзаца. Внутри абзацев расположены строчные элементы , и . Во втором абзаце тег вложен в тег .
Теперь давайте к этому html-коду добавим CSS-стили с использованием дочерних селекторов.
Синтаксис дочерних селекторов:
Селектор 1 > Селектор 2 { Свойство: значение; }
Вот обновлённый код:
Абзац и в нём жирный элемент, и вот наклонный элемент.
Тут жирный и подчёркнутый и наклонный элементы.
Итак, вспомним: дочерний элемент - это элемент вложенный непосредственно в родительский. То есть, дочерний элемент - это потомок первого уровня. Давайте обратим внимание на тег , в первом абзаце он вложен в тег
А во втором он вложен в тег , хотя также является потомком тега
Поэтому во втором абзаце CSS правило для дочернего селектора p>i { color : blue ; } не сработает - наклонный текст второго абзаца не будет отображён синим цветом.
Рисунок 1. Работа примера №1.
Добраться к html-элементу второго абзаца можно используя CSS-правило: p>u>i { color : blue ; }.
Давайте при помощи такого правила зададим наклонному тексту второго абзаца жёлтый цвет.
Абзац и в нём жирный элемент, и вот наклонный элемент.
Тут жирный и подчёркнутый и наклонный элементы.
Этот стиль сработает и наклонный текст во втором абзаце будет отображён жёлтым цветом.
![](https://i0.wp.com/komotoz.ru/uroki/css/example/images/dochernie_selektory_2.png)
Более сложный пример
У нас есть html-код:
По умолчанию он интерпритируется так:
Задача: при помощи CSS превратить этот список в горизонтальное меню.
![](https://i2.wp.com/komotoz.ru/uroki/css/example/images/dochernie_selektory_3.png)
Вот решение этой задачи с использованием дочерних селекторов:
Для лучшего понимания этого примера читайте.
Я думаю, что многие знают о контекстных селекторах в CSS . Они используются чаще всего, однако, опытные верстальщики прекрасно знают, что иногда контекстные селекторы вносят определённые проблемы. Это проблема связана с тем, что в структуре элемента может быть много одинаковых элементов, вложенных друг в друга. И необходимо применить стиль не для всех вложенных элементов, а только к непосредственно дочернему элементу. Вот для этого и используются дочерные селекторы в CSS .
Чтобы проблема стала более понятной, давайте приведу небольшой пример. Пусть у нас есть такой HTML-код :
Первый абзац
Второй абзац
И наша задача сделать красным только "Второй абзац ". Если мы напишем с использованием контекстного селектора:
Container p {
color: red;
}
То у нас станут красным оба абзаца, что нам совсем не нужно. Данная задача очень просто решается с помощью дочерних селекторов в CSS :
Container > p {
color: red;
}
Всё, теперь красным у нас стал только "Второй абзац ". Поскольку именно данный абзац является непосредственно дочерним для .container . А "Первый абзац " является дочерним для внутреннего div , таким образом, под действие дочернего селектора он не попадает.
Вот так легко решаются такие задачи, однако, есть один огромный минус дочерних селекторов в CSS - они не работают в браузерах Internet Explorer . По этой причине, их использование крайне нежелательно. Но если Вы вдруг где-нибудь встретите, то теперь Вы будете знать, что означает данный тип селекторов и что он делает.
Сложные и тяжелые веб-приложения стали обычными в наши дни. Кроссбраузерные и простые в использовании библиотеки типа jQuery с их широким функционалом могут сильно помочь в манипулировании DOM на лету. Поэтому неудивительно, что многие разработчики использую подобные библиотеки чаще, чем работают с нативным DOM API, с которым было немало проблем . И хотя различия в браузерах по-прежнему остаются проблемой, DOM находится сейчас в лучшей форме, чем 5-6 лет назад , когда jQuery набирал популярность.
В этой статье я продемонстрирую возможности DOM по манипулированию HTML, сфокусировавшись на отношения родительских, дочерних и соседних элементов. В заключении я дам данные о поддержке этих возможностей в браузерах, но учитывайте, что библиотека типа jQuery по-прежнему остается хорошей опцией в силу наличия багов и непоследовательностей в реализации нативного функционала.
Подсчет дочерних узлов
Для демонстрации я буду использовать следующую разметку HTML, в течение статьи мы ее несколько раз изменим:
- Example one
- Example two
- Example three
- Example four
- Example five
- Example Six
Var myList = document.getElementById("myList"); console.log(myList.children.length); // 6 console.log(myList.childElementCount); // 6
Как видите, результаты одинаковые, хотя техники используются разные. В первом случае я использую свойство children . Это свойство только для чтения, оно возвращает коллекцию элементов HTML, находящихся внутри запрашиваемого элемента; для подсчета их количества я использую свойство length этой коллекции.
Во втором примере я использую метод childElementCount , который мне кажется более аккуратным и потенциально более поддерживаемым способом (подробнее обсудим это позже, я не думаю, что у вас возникнут проблемы с пониманием того, что он делает).
Я мог бы попытаться использовать childNodes.length (вместо children.length), но посмотрите на результат:
Var myList = document.getElementById("myList"); console.log(myList.childNodes.length); // 13
Он возвращает 13, потому что childNodes это коллекция всех узлов, включая пробелы - учитывайте это, если вам важна разница между дочерними узлами и дочерними узлами-элементами.
Проверка существования дочерних узлов
Для проверки наличия у элемента дочерних узлов я могу использовать метод hasChildNodes() . Метод возвращает логическое значение, сообщающие об их наличии или отсутствии:
Var myList = document.getElementById("myList"); console.log(myList.hasChildNodes()); // true
Я знаю, что в моем списке есть дочерние узлы, но я могу изменить HTML так, чтобы их не было; теперь разметка выглядит так:
И вот результат нового запуска hasChildNodes() :
Console.log(myList.hasChildNodes()); // true
Метод по прежнему возвращает true . Хотя список не содержит никаких элементов, в нем есть пробел, являющийся валидным типом узла. Данный метод учитывает все узлы, не только узлы-элементы. Чтобы hasChildNodes() вернул false нам надо еще раз изменить разметку:
И теперь в консоль выводится ожидаемый результат:
Console.log(myList.hasChildNodes()); // false
Конечно, если я знаю, что могу столкнуться с пробелом, то сначала я проверю существование дочерних узлов, затем с помощью свойства nodeType определяю, есть ли среди них узлы-элементы.
Добавление и удаление дочерних элементов
Есть техника, которые можно использовать для добавления и удаления элементов из DOM. Наиболее известная из них основана на сочетании методов createElement() и appendChild() .
Var myEl = document.createElement("div"); document.body.appendChild(myEl);
В данном случае я создаю
Но вместо вставки специально создаваемого элемента, я также могу использовать appendChild() и просто переместить существующий элемент. Предположим, у нас следующая разметка:
- Example one
- Example two
- Example three
- Example four
- Example five
- Example Six
Example text
Я могу изменить место расположения списка с помощью следующего кода:
Var myList = document.getElementById("myList"), container = document.getElementById("c"); container.appendChild(myList);
Итоговый DOM будет выглядеть следующим образом:
Example text
- Example one
- Example two
- Example three
- Example four
- Example five
- Example Six
Обратите внимание, что весь список был удален со своего места (над параграфом) и затем вставлен после него перед закрывающим body . И хотя обычно метод appendChild() используется для добавления элементов созданных с помощью createElement() , он также может использоваться для перемещения существующих элементов.
Я также могу полностью удалить дочерний элемент из DOM с помощью removeChild() . Вот как удаляется наш список из предыдущего примера:
Var myList = document.getElementById("myList"), container = document.getElementById("c"); container.removeChild(myList);
Теперь элемент удален. Метод removeChild() возвращает удаленный элемент и я могу его сохранить на случай, если он потребуется мне позже.
Var myOldChild = document.body.removeChild(myList); document.body.appendChild(myOldChild);
Таке существует метод ChildNode.remove() , относительно недавно добавленный в спецификацию:
Var myList = document.getElementById("myList"); myList.remove();
Этот метод не возвращает удаленный объект и не работает в IE (только в Edge). И оба метода удаляют текстовые узлы точно так же, как и узлы-элементы.
Замена дочерних элементов
Я могу заменить существующий дочерний элемент новым, независимо от того, существует ли этот новый элемент или я создал его с нуля. Вот разметка:
Example Text
Var myPar = document.getElementById("par"), myDiv = document.createElement("div"); myDiv.className = "example"; myDiv.appendChild(document.createTextNode("New element text")); document.body.replaceChild(myDiv, myPar);
Как видите, метод replaceChild() принимает два аргумента: новый элемент и заменяемый им старый элемент.
Я также могу использовать это метод для перемещения существующего элемента. Взгляните на следующий HTML:
Example text 1
Example text 2
Example text 3
Я могу заменить третий параграф первым параграфом с помощью следующего кода:
Var myPar1 = document.getElementById("par1"), myPar3 = document.getElementById("par3"); document.body.replaceChild(myPar1, myPar3);
Теперь сгенерированный DOM выглядит так:
Example text 2
Example text 1
Выборка конкретных дочерних элементов
Существует несколько разных способов выбора конкретного элемента. Как показано ранее, я могу начать с использования коллекции children или свойства childNodes . Но взглянем на другие варианты:
Свойства firstElementChild и lastElementChild делают именно то, чего от них можно ожидать по их названию: выбирают первый и последний дочерние элементы. Вернемся к нашей разметке:
- Example one
- Example two
- Example three
- Example four
- Example five
- Example Six
Я могу выбрать первый и последний элементы с помощью этих свойств:
Var myList = document.getElementById("myList"); console.log(myList.firstElementChild.innerHTML); // "Example one" console.log(myList.lastElementChild.innerHTML); // "Example six"
Я также могу использовать свойства previousElementSibling и nextElementSibling , если я хочу выбрать дочерние элементы, отличные от первого или последнего. Это делается сочетанием свойств firstElementChild и lastElementChild:
Var myList = document.getElementById("myList"); console.log(myList.firstElementChild.nextElementSibling.innerHTML); // "Example two" console.log(myList.lastElementChild.previousElementSibling.innerHTML); // "Example five"
Также есть сходные свойства firstChild , lastChild , previousSibling , и nextSibling , но они учитывают все типы узлов, а не только элементы. Как правило, свойства, учитывающие только узлы-элементы полезнее тех, которые выбирают все узлы.
Вставка контента в DOM
Я уже рассматривал способы вставки элементов в DOM. Давайте перейдем к похожей теме и взглянем на новые возможности по вставке контента.
Во-первых, есть простой метод insertBefore() , он во многом похож на replaceChild() , принимает два аргумента и при этом работает как с новыми элементами, так и с существующими. Вот разметка:
- Example one
- Example two
- Example three
- Example four
- Example five
- Example Six
Example Paragraph
Обратите внимание на параграф, я собираюсь сначала убрать его, а затем вставить перед списком, все одним махом:
Var myList = document.getElementById("myList"), container = document.getElementBy("c"), myPar = document.getElementById("par"); container.insertBefore(myPar, myList);
В полученном HTML параграф будет перед списком и это еще один способ перенести элемент.
Example Paragraph
- Example one
- Example two
- Example three
- Example four
- Example five
- Example Six
Как и replaceChild() , insertBefore() принимает два аргумента: добавляемый элемент и элемент, перед которым мы хотим его вставить.
Этот метод прост. Попробуем теперь более мощный способ вставки: метод insertAdjacentHTML() .
![](/assets/vsadu-icon.png)
чтобы вы всегда были в курсе самого интересного.