Кратко
Скопировано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? Счётчики помогут вам сделать автоматическую нумерацию страниц, рисунков, вашего содержания и списка литературы, а также глав и подглав вашего научного труда.
Или у вас есть интересное дизайнерское решение вроде изображённого в начале статьи?
Также вы можете посмотреть две переведённые статьи на тему счётчиков: