Кратко
СкопированоCSS-счётчики — это мощный инструмент для нумерации любых элементов страницы, не только списков.
Основные свойства для работы со счётчиками:
counter
— создаёт счётчик;- reset counter
— увеличивает числовое значение счётчика;- increment counter
— устанавливает точное значение счётчика.- set
Помимо свойств, с помощью которых можно создавать счётчики и управлять ими, есть две основные функции для подставления значения счётчика: counter
и counters
.
counter-reset
СкопированоПеред тем как воспользоваться значением счётчика, этот счётчик нужно создать. Счётчики создаются при помощи свойства counter
.
ul { counter-reset: example 0;}
ul { counter-reset: example 0; }
В качестве значения сначала указывается имя счётчика, а затем его начальное целочисленное значение.
Имена счётчиков чувствительны к регистру. Например, значения example
и EXAMPLE
— это два разных, не связанных между собой счётчика.
Нельзя использовать ключевые слова: none
, initial
, inherit
, unset
, default
в качестве названий счётчиков.
Также счётчики подчиняются принципу каскада, например:
h1 { counter-reset: counter1 1;}h1 { counter-reset: counter2 99;}
h1 { counter-reset: counter1 1; } h1 { counter-reset: counter2 99; }
В таком случае применится только последнее CSS-правило для элемента <h1>
.
Чтобы свойство counter
сработало для обоих счётчиков из примера выше необходимо указать их вместе через пробел:
h1 { counter-reset: counter1 1 counter2 99;}
h1 { counter-reset: counter1 1 counter2 99; }
Или вот такой пример:
h1 { counter-reset: counter 1 counter 2;}
h1 { counter-reset: counter 1 counter 2; }
В значении свойства counter
указаны два одинаковых счётчика, в таком случае будет учитываться только последний.
counter-increment
СкопированоЧтобы значение счётчика начало увеличиваться на определённое значение от элемента к элементу необходимо указать свойство counter
.
li { counter-increment: example 2;}
li { counter-increment: example 2; }
Теперь каждый элемент <li>
в документе будет увеличивать значение счётчика example
на 2.
Рассмотрим пример:
<ul> <li class="first">Значение счётчика example равно 1</li> <li class="second">Значение счётчика example равно 10</li> <li class="third">Значение счётчика example равно 15</li></ul>
<ul> <li class="first">Значение счётчика example равно 1</li> <li class="second">Значение счётчика example равно 10</li> <li class="third">Значение счётчика example равно 15</li> </ul>
На элементе <ul>
создадим счётчик example
с начальным значением 0:
ul { counter-reset: example 0;}
ul { counter-reset: example 0; }
Укажем разные значения увеличения счётчика для каждого элемента <li>
:
.first { counter-increment: example 1;}.second { counter-increment: example 9;}.third { counter-increment: example 5;}
.first { counter-increment: example 1; } .second { counter-increment: example 9; } .third { counter-increment: example 5; }
Как это работает будет более подробно расписано дальше. Но в конечном итоге пример выше выглядит так:
li { /* Значение счётчика example будет равно 15 на этом элементе */ counter-increment: example 1 example 9 example 5;}
li { /* Значение счётчика example будет равно 15 на этом элементе */ counter-increment: example 1 example 9 example 5; }
counter-set
СкопированоВам неожиданно понадобилось изменить порядок нумерации элементов в списке, чтобы после 1 пункта шёл сразу 9? counter
отлично подойдёт для этой задачи:
Рассмотрим пример:
<ul> <li class="first">Значение счётчика example равно 1</li> <li class="second">Значение счётчика example равно 9</li> <li class="third">Значение счётчика example равно 15</li></ul>
<ul> <li class="first">Значение счётчика example равно 1</li> <li class="second">Значение счётчика example равно 9</li> <li class="third">Значение счётчика example равно 15</li> </ul>
На элементе <ul>
создадим счётчик example
с начальным значением 0:
ul { counter-reset: example 0;}
ul { counter-reset: example 0; }
Укажем разные значения увеличения счётчика для каждого элемента <li>
, а для элемента с классом second
добавим ещё свойство counter
:
.first { counter-increment: example 1;}.second { counter-increment: example 3; counter-set: example 9;}.third { counter-increment: example 6;}
.first { counter-increment: example 1; } .second { counter-increment: example 3; counter-set: example 9; } .third { counter-increment: example 6; }
Может показаться, что второй элемент с классом second
должен иметь значение 12, а не 9, потому что на него применяются сразу два свойства counter
и counter
, однако при вычислении значения используется определённый порядок.
Сначала выполняется свойство counter
и значение счётчика увеличивается на 3, но затем сразу же переписывается свойством counter
устанавливая точное значение 9.
li { /* Значение счётчика example будет равно 9 на этом элементе */ counter-set: example 1 example 6 example 9;}
li { /* Значение счётчика example будет равно 9 на этом элементе */ counter-set: example 1 example 6 example 9; }
counter()
и counters()
СкопированоСвойства counter
и counter
не отображают фактическое значение счётчиков. Эти свойства только управляют ими.
Но когда речь заходит о том, чтобы отобразить значения того или иного счётчика на помощь приходят функции counter
и counters
.
Разница между ними будет описана подробнее дальше.
Возьмём один из примеров который уже был представлен выше:
<ul> <li class="first">Значение счётчика example равно 1</li> <li class="second">Значение счётчика example равно 10</li> <li class="third">Значение счётчика example равно 15</li></ul>
<ul> <li class="first">Значение счётчика example равно 1</li> <li class="second">Значение счётчика example равно 10</li> <li class="third">Значение счётчика example равно 15</li> </ul>
На элементе <ul>
создадим счётчик example
с начальным значением 0:
ul { counter-reset: example 0;}
ul { counter-reset: example 0; }
Укажем разные значения увеличения счётчика для каждого элемента <li>
:
.first { counter-increment: example 1;}.second { counter-increment: example 9;}.third { counter-increment: example 5;}
.first { counter-increment: example 1; } .second { counter-increment: example 9; } .third { counter-increment: example 5; }
Теперь, чтобы значения счётчика начали отображаться в документе воспользуемся функцией counter
.
Подставляем значение счётчика example
на каждый псевдоэлемент :
элемента <li>
:
li::marker { content: counter(example);}
li::marker { content: counter(example); }
В обоих функциях есть необязательный аргумент, который указывает стиль счётчика, например, вместо использования десятичной системы счисления — данное значение устанавливается по умолчанию, можно указать строчную римскую нумерацию, то есть тип lower
:
li::marker { content: counter(example, lower-roman);}
li::marker { content: counter(example, lower-roman); }
О других стилях счётчика можно посмотреть в доке по свойству list
.
Основные термины
Скопировано- Набор счётчиков — это коллекция неповторяющихся между собой счётчиков, которые имеет элемент. Эта коллекция пополняется наследуемыми счётчиками от другого элемента и счётчиками которые элемент создал сам.
Изначально каждый элемент или псевдоэлемент в документе имеет пустой набор счётчиков.
Каждый счётчик, который попадает в эту коллекцию, имеет внутри себя следующие данные:
- имя — идентификатор, который указывается при создании счётчика;
- создатель — элемент, который его создал;
- значение — целочисленное значение счётчика.
Представить это можно так:
- Явный счётчик — это счётчик который создали вы.
- Неявный счётчик — это счётчик который автоматически создаётся браузером.
- Последний счётчик — это самый последний счётчик с указанным именем из набора счётчиков.
Порядок вычисления значения
СкопированоЗначения счётчика из набора на каждом отдельном элементе могут быть разными. Значение счётчика на текущем элементе вычисляется поэтапно:
- Сначала наследуется набор счётчиков.
- Если на элементе указано свойство
counter
— создаётся новый счётчик.- reset - Если на элементе указано свойство
counter
— значение счётчика увеличивается.- increment - Если на элементе указано свойство
counter
— устанавливается точное значение счётчика.- set
И только теперь, после всех вычислений, значением счётчика можно воспользоваться через функции counter
и counters
.
Создание и наследование счётчиков
СкопированоНаследование
СкопированоЭлемент наследует свой начальный набор счётчиков от своего родителя и предыдущего одноуровневого элемента.
Затем он берёт значения этих счётчиков из набора счётчиков предыдущего элемента в порядке дерева. Предыдущий элемент может быть:
- родителем;
- одноуровневым элементом;
- дочерним элементом предыдущего одноуровневого элемента.
Создадим счётчик на элементе <ul>
и проследим как счётчик будет наследоваться элементами, которые находятся внутри.
<ul> <li class="first">Первый элемент <p class="paragraph">Параграф</p> </li> <li class="second">Второй элемент</li></ul>
<ul> <li class="first">Первый элемент <p class="paragraph">Параграф</p> </li> <li class="second">Второй элемент</li> </ul>
На элементе <ul>
создаём счётчик c именем new
и начальным значением 0:
ul { counter-reset: new 0;}
ul { counter-reset: new 0; }
Теперь в наборе у элемента <ul>
есть один, созданный им же счётчик с именем new
.
Укажем, чтобы на элементах <li>
с классами first
и second
, и на элементе <p>
с классом paragraph
значение счётчика new
увеличивалось на 1:
.first,.second,.paragraph { counter-increment: new 1;}
.first, .second, .paragraph { counter-increment: new 1; }
Первый дочерний элемент с классом first
наследует свой начальный набор счётчиков от родительского элемента <ul>
.
Также <ul>
является предшествующим элементом в порядке дерева, поэтому элемент с классом first
наследует также значение счётчика new
— 0, а затем сразу же увеличивает его на 1.
Теперь значение счётчика new
на элементе <li>
с классом first
равно 1.
Тоже самое происходит с элементом у которого класс paragraph
. Он наследует счётчик new
от своего родительского элемента с классом first
, а также его значение — 1, а затем сразу же увеличивает его на 1.
Теперь значение счётчика new
на элементе <p>
с классом paragraph
равно 2.
Однако элемент с классом second
немного отличается. Он наследует счётчик new
от своего предыдущего одноуровневого элемента <li>
с классом first
, но вместо того чтобы наследовать значение 1, он наследует значение 2 от элемента с классом paragraph
, предыдущего элемента в порядке дерева, а затем сразу же увеличивает его на 1.
Теперь значение счётчика new
на элементе <li>
с классом second
равно 3.
Подставим значение счётчика new
в псевдоэлемент :
для элемента <p>
, и в псевдоэлемент :
для элементов с классами first
и second
чтобы пронумеровать их:
li::marker,p::before { content: counter(new);}
li::marker, p::before { content: counter(new); }
Создание счётчиков
СкопированоПосле наследования набора счётчиков от предыдущего одноуровневого и родительского элементов происходит создание счётчиков.
Создавать новые счётчики может не только свойство counter
.
Если вы применили свойство counter
или counter
, или воспользовались функцией counter
или counters
и указали имя несуществующего счётчика:
li { /* Сначала создаст счётчик с именем new, а затем увеличит его значение на 2 */ counter-increment: new 2;}li { /* Сначала создаст счётчик с именем new, а затем установит его значение на 2 */ counter-set: new 2;}li::marker { /* Сначала создаст счётчик с именем new, а затем подставит его значение */ content: counter(new);}li::marker { /* Аналогично */ content: counters(new, '.');}
li { /* Сначала создаст счётчик с именем new, а затем увеличит его значение на 2 */ counter-increment: new 2; } li { /* Сначала создаст счётчик с именем new, а затем установит его значение на 2 */ counter-set: new 2; } li::marker { /* Сначала создаст счётчик с именем new, а затем подставит его значение */ content: counter(new); } li::marker { /* Аналогично */ content: counters(new, '.'); }
В таком случае на элементе сначала создастся новый счётчик с именем new
и начальным значением 0. После создания счётчика свойства counter
и counter
, и функции counter
и counters
начнут действовать как обычно.
Если элемент уже содержит унаследованный счётчик с именем n
, который был создан одноуровневым предыдущим элементом, и вы создаёте на элементе счётчик с точно таким же именем n
, то этот счётчик заменит унаследованный.
Рассмотрим пример. Создадим элемент <div>
с 3 вложенными элементами <p>
и проследим как счётчик будет наследоваться элементами.
<div> <p class="first">Первый абзац</p> <p class="second">Второй абзац</p> <p class="third">Третий абзац</p></div>
<div> <p class="first">Первый абзац</p> <p class="second">Второй абзац</p> <p class="third">Третий абзац</p> </div>
На элементе <div>
создаём счётчик c именем new
и начальным значением 0:
div { counter-reset: new 0;}
div { counter-reset: new 0; }
Теперь у элемента <div>
в наборе имеется один, созданный им же счётчик с именем new
.
Создаём ещё один счётчик, но уже на элементе <p>
. Назовём его paragraph
:
p { counter-reset: paragraph 1;}
p { counter-reset: paragraph 1; }
Первый дочерний элемент с классом first
наследует свой начальный набор счётчиков от родительского элемента <div>
и также создаёт собственный счётчик с именем paragraph
.
Теперь у элемента <p>
с классом first
в наборе два счётчика: один унаследованный от родительского элемента <div>
, второй собственно созданный счётчик.
Так как <div>
является предшествующим элементом в порядке дерева, элемент с классом first
наследует также значение счётчика new
— 0.
Следующий элемент <p>
с классом second
унаследует точно такой же набор счётчиков от предыдущего одноуровневого элемента <p>
с классом first
, но как только это произойдёт он перезапишет счётчик paragraph
элемента <p>
c классом first
на собственно созданный счётчик.
Последний элемент <p>
с классом third
сделает абсолютно тоже самое.
Увеличим элементу с классом second
значение счётчика paragraph
:
.second { counter-increment: paragraph 1;}
.second { counter-increment: paragraph 1; }
Подставим значение счётчика paragraph
в псевдоэлемент :
элементов с классами first
, second
и third
чтобы пронумеровать их:
p::before { content: counter(paragraph);}
p::before { content: counter(paragraph); }
Так как счётчик paragraph
увеличивался при помощи свойства counter
только на элементе с классом second
, значение счётчика на следующих элементах <p>
будет 1.
Вложенные счётчики и область применения
СкопированоПредставим ещё одну ситуацию. Если элемент уже содержит унаследованный счётчик с именем n
, который был создан родительским элементом (любым, необязательно родительским элементом текущего элемента), и вы создаёте на элементе счётчик с точно таким же именем n
, в таком случае этот счётчик вложится в уже существующий счётчик.
Представить это можно так:
Рассмотрим пример. Создадим многоуровневый список создав счётчик на элементе <ul>
и проследим как счётчик будет наследоваться элементами, которые находятся внутри.
<ul class="first-list"> <li class="one">Первый элемент первого списка <ul class="second-list"> <li class="one-one">Первый элемент второго списка</li> <li class="one-two">Второй элемент второго списка</li> </ul> </li> <li class="two">Второй элемент первого списка</li></ul>
<ul class="first-list"> <li class="one">Первый элемент первого списка <ul class="second-list"> <li class="one-one">Первый элемент второго списка</li> <li class="one-two">Второй элемент второго списка</li> </ul> </li> <li class="two">Второй элемент первого списка</li> </ul>
На элементе <ul>
создаём счётчик c именем new
и начальным значением 0:
ul { counter-reset: new 0;}
ul { counter-reset: new 0; }
Теперь у элемента <ul>
с классом first
в наборе имеется один счётчик созданный им же.
Укажем, чтобы на элементе <li>
значение счётчика new
увеличивалось на 1:
li { counter-increment: new 1;}
li { counter-increment: new 1; }
Первый дочерний элемент с классом one
наследует свой начальный набор счётчиков от родительского элемента <ul>
c классом first
.
Также <ul>
с классом first
является предшествующим элементом в порядке дерева, поэтому элемент с классом one
наследует также значение счётчика new
— 0, а затем сразу же увеличивает его на 1.
Теперь значение счётчика new
на элементе <li>
с классом one
равно 1.
В элементе <li>
с классом one
появляется ещё один элемент <ul>
с классом second
.
Элемент <ul>
с классом second
наследует счётчик new
от своего родительского элемента с классом one
, после этого он создаёт собственный счётчик с именем new
, но так как счётчик с таким же именем, созданный родительским элементом <ul>
с классом first
, уже есть в наборе, то этот счётчик не будет удалён, а вложится в уже существующий.
У элемента <ul>
с классом second
получается примерно следующий набор счётчиков:
На этом моменте область действия счётчика new
элемента <ul>
с классом first
заканчивается. И если применить свойства counter
или counter
чтобы повлиять на значение счётчика new
, меняться будет только последний счётчик с именем new
в наборе.
Первый дочерний элемент с классом one
, элемента <ul>
с классом second
, наследует свой начальный набор счётчиков от родительского элемента.
Также <ul>
c классом second
является предшествующим элементом в порядке дерева, поэтому элемент с классом one
наследует также значение счётчика new
— 0, а затем сразу же увеличивает его на 1. Но увеличивает не счётчик созданный элементом <ul>
с классом first
, а счётчик, который создал его родительский элемент <ul>
с классом second
.
Элемент <li>
с классом one
наследует счётчик new
от своего предшествующего одноуровневого элемента с классом one
, а также его значение — 1, а затем сразу же увеличивает его на 1. Но опять же, увеличивает не счётчик созданный элементом <ul>
с классом first
, а счётчик, который создал его родительский элемент <ul>
с классом second
.
Самый последний элемент <li>
с классом two
наследует набор счётчиков от предыдущего одноуровневого элемента — элемента <li>
с классом one
.
Так как предыдущий элемент в порядке дерева — это элемент <ul>
с классом second
, элемент с классом two
наследует значение счётчика new
, но только не того счётчика new
, который создал элемент <ul>
с классом second
, а счётчика который создал элемент <ul>
с классом first
и затем сразу же увеличивает его на 1.
Теперь значение счётчика new
на элементе <li>
с классом two
равно 2.
Разница между counter()
и counters()
СкопированоВ итоговой демке выше нумерация (значение счётчиков) была выведена при помощи функции counters
.
Функция counters
выводит значения всех счётчиков с указанным именем в наборе:
Вторым аргументом функции counters
важно указать разделитель в виде строки. Эта строка будет разделять значения всех счётчиков с указанным именем.
Функция counter
выводит значение только последнего счётчика с указанным именем.
Выведем ту же самую демку, но используем функцию counter
:
Свойства display
и content
СкопированоСвойства counter
, counter
и counter
не будут действовать на элементах которые не создают блок. Например на элементах с display
или псевдоэлементах с content
.
Рассмотрим пример:
<div> <p class="display">Абзац с display: none</p> <p class="content">Абзац с content: none</p> <p class="first">Нормальный абзац</p> <p class="second">Последний нормальный абзац</p></div>
<div> <p class="display">Абзац с display: none</p> <p class="content">Абзац с content: none</p> <p class="first">Нормальный абзац</p> <p class="second">Последний нормальный абзац</p> </div>
На элементе <div>
создаём счётчик c именем new
и начальным значением 0:
div { counter-reset: new 0;}
div { counter-reset: new 0; }
Укажем, чтобы на элементах <p>
с классами first
и second
значение счётчика new
увеличивалось на 1:
p.first,p.second { counter-increment: new 1;}
p.first, p.second { counter-increment: new 1; }
Далее укажем основные свойства для элементов с классами display
и content
, а также укажем увеличение значения счётчика new
на 1:
p.display { counter-increment: new 1; display: none;}p.content::before { counter-increment: new 1; content: none;}
p.display { counter-increment: new 1; display: none; } p.content::before { counter-increment: new 1; content: none; }
Пронумеруем элементы с классами first
и second
через функцию counter
:
p.first::before,p.second::before { content: counter(new);}
p.first::before, p.second::before { content: counter(new); }
Как видно элемент <p>
с классом display
и псевдоэлемент :
элемента с классом content
не увеличили значение счётчика new
.
Неявный счётчик элементов списка
СкопированоВ дополнение к любым явно определённым в CSS счётчикам, любые элементы с display
автоматически имеют в своём наборе специальный неявный счётчик с именем list
.
Например, элементы <li>
по умолчанию имеют свойство display
со значением list
.
Счётчиком list
можно управлять так же как и обычным явным счётчиком.
Если в значении свойства counter
явно не указывается значение на которое счётчик list
будет увеличиваться от элемента к элементу, то он автоматически увеличивается браузером на 1 для каждого элемента со свойством display
.
Именно поэтому, когда вы объявляете список <ol>
элементы <li>
в списке автоматически нумеруются.
Даже если вы попытаетесь указать свойство counter
со значением none
на элементе <li>
неявный счётчик list
всё равно продолжит нумерацию. Эта защита сделана специально.
Однако если вы укажете другое значение, например:
li { counter-increment: list-item 2;}
li { counter-increment: list-item 2; }
автоматическое увеличение счётчика list
будет переопределено и теперь его значение будет увеличиваться на 2.
Вы также можете указать значение 0, тогда счётчик list
вообще перестанет увеличиваться.
Рассмотрим пример:
<ol> <li>Первый элемент</li> <li>Второй элемент</li> <li>Третий элемент</li></ol>
<ol> <li>Первый элемент</li> <li>Второй элемент</li> <li>Третий элемент</li> </ol>
Укажем, чтобы на элементе <li>
значение неявного счётчика list
увеличивалось на 2:
li { counter-increment: list-item 2;}
li { counter-increment: list-item 2; }
На самом деле это очень удобно, когда уже есть готовый неявный счётчик. Если вам нужно создать многоуровневый нумерованный список, это можно сделать в два счёта:
<ol> <li>Первый элемент первого списка <ol> <li>Первый элемент второго списка</li> <li>Второй элемент второго списка</li> </ol> </li> <li>Второй элемент первого списка</li></ol>
<ol> <li>Первый элемент первого списка <ol> <li>Первый элемент второго списка</li> <li>Второй элемент второго списка</li> </ol> </li> <li>Второй элемент первого списка</li> </ol>
Выведем нумерацию при помощи функции counters
:
li::marker { content: counters(list-item, '.');}
li::marker { content: counters(list-item, '.'); }
Всё! Больше ничего делать не нужно.
Особенность списка <ol>
СкопированоЛюбые вносимые в HTML изменения нумерации списка также будут учитываться счётчиком.
Например, если вы укажете в списке <ol>
атрибут start
и значение с которого следует начинать нумерацию, или атрибут reversed
, чтобы пронумеровать список в обратном порядке, то данные атрибуты будут учитываться при нумерации списка. Также это касается элемента <li>
и его атрибута value
:
<ol> <li>Первый элемент первого списка</li> <li value="5">Второй элемент первого списка, value = 5 <ol start="3"> <li>Первый элемент второго списка. Список нумеруется с 3</li> <li>Второй элемент второго списка <ol reversed> <li>Первый элемент третьего списка. Список отображается в обратном порядке</li> <li>Второй элемент третьего списка</li> <li>Третий элемент третьего списка</li> <li>Четвёртый элемент третьего списка</li> </ol> </li> <li>Третий элемент второго списка</li> </ol> </li> <li>Третий элемент второго списка</li></ol>
<ol> <li>Первый элемент первого списка</li> <li value="5">Второй элемент первого списка, value = 5 <ol start="3"> <li>Первый элемент второго списка. Список нумеруется с 3</li> <li>Второй элемент второго списка <ol reversed> <li>Первый элемент третьего списка. Список отображается в обратном порядке</li> <li>Второй элемент третьего списка</li> <li>Третий элемент третьего списка</li> <li>Четвёртый элемент третьего списка</li> </ol> </li> <li>Третий элемент второго списка</li> </ol> </li> <li>Третий элемент второго списка</li> </ol>
Выведем нумерацию при помощи функции counters
:
li::marker { content: counters(list-item, '.');}
li::marker { content: counters(list-item, '.'); }
Полная поддержка подобного поведения, пока что, реализована только в Mozilla Firefox.
На практике
Скопированосоветует Скопировано
🛠 Вы вдохновились выступлением Никиты Дубко и решили написать курсовую оформленную через CSS? Счётчики помогут вам сделать автоматическую нумерацию страниц, рисунков, вашего содержания и списка литературы, а также глав и подглав вашего научного труда.
Или у вас есть интересное дизайнерское решение вроде изображённого в начале статьи?
Также вы можете посмотреть две переведённые статьи на тему счётчиков: