CSS 카운터 함수(counter())로 목차형 순번 붙이기

CSS의 고급 활용 기법입니다.

초보자에게는 다소 어려운 내용일 수 있습니다.

CSS의 기초적인 부분과 가상 요소에 대해서 알려면 먼저 다음 내용을 학습하시기 바랍니다.

> HTML+CSS 기초 강의 - 27. CSS 선택자 기초 4 - 가상 요소

> CSS 선택자 고급 - 가상 요소(Pseudo Element)로 선택하기

> CSS 가상 요소를 활용해 삼각형 화살표 만들기

HTML 태그 중에는 <OL> 태그(Ordered List)가 있어서 목록 앞에 번호 순서를 붙일 수 있는 기능을 제공합니다.

다만, 순번을 붙이는 방식이나 태그가 제한되어 있기 때문에 용도 또한 제한적으로만 사용할 수 있습니다.

일반 태그에도 넘버링을 붙일 수 있는 기능이 있습니다.

CSS 카운터라고 하고, 태그 종류에 제한 없이 다양한 순번을 붙일 수 있습니다.

CSS 카운터에도 몇 가지 제약 사항이 있습니다.

<OL> 태그와는 달리 숫자 순번만 붙일 수 있고, 순번의 표현을 CSS 가상 요소로 직접 작성을 해야 합니다.

대신 다채로운 숫자 순번 표현이 가능합니다.

CSS 카운터를 간단한 예를 들어 알아보겠습니다.

다음과 같이 여러 개의 문단이 있는 HTML에서 문단 앞에 순번을 붙여보겠습니다.

CSS 카운터 기초 이해

<div id="counter1">
  <p>문단1</p>
  <p>문단2</p>
  <p>문단3</p>
</div>

CSS는 다음과 같이 작성합니다. 순번 표시를 위해 CSS 클래스 2개만 정의한 것이지만 안에는 많은 내용을 담고 있습니다. "counter"로 시작하는 클래스 속성은 CSS카운터를 위한 전용 속성입니다. CSS 카운터 사용을 위해 사용한 CSS 속성은 "counter-reset", "counter-increment", "counter()" 3개가 전부입니다.

"var-count"는 사용자 정의 변수명입니다.

div#counter1{
	counter-reset: var-count;
}
div#counter1 > p::before{
	counter-increment: var-count;
	content: counter(var-count) ": ";
}
CSS 카운터 적용 결과

CSS 카운터는 순번이 표시되는 태그들의 나열과 함께 한 개의 부모 요소를 필요로 합니다.

반드시 한 개의 부모 요소로 감싼 요소들이 필요합니다.

부모 요소의 CSS 속성에는 "counter-reset" 속성으로 순번 증가를 위해 사용하는 사용자 정의 변수명과 순번의 시작 값을 정의합니다. "counter-reset: var-count 13;"으로 정의한 부모 요소의 CSS 카운터 초기화 속성은 "var-count" 변수명을 하위 요소들의 순번 증가를 위해 사용하고 시작 숫자는 13이라는 뜻입니다.

순번을 표시하는 하위 요소들은 CSS 선택자로 선택한 요소들에 모두 부여됩니다. 같은 태그가 아니어도 되며, 같은 태그여도 CSS 선택자로 선택한 태그들에만 순번이 붙게 됩니다.

예를 들어 다음과 같이 첫 번째 요소는 순번에서 제외하는 CSS 선택자를 사용해 CSS 카운터를 적용하는 요소를 적용하면 두 번째 요소부터 시작 순번이 붙으면서 순번이 표시됩니다.

div#counter1{
	counter-reset: var-count;
}
div#counter1 > p:not(:firdst-child)::before{
	counter-increment: var-count;
	content: counter(var-count) ": ";
}

CSS 카운터 속성 4종 기초

먼저 CSS 카운터에 사용하는 전용 속성 4개를 알아야 합니다.

사용 방법이 고정되어 있기 때문에 사용 자체는 어렵지 않습니다.

속성이 어떤 게 있고 어떻게 사용한다 정도만 알면 됩니다. 4가지 속성은 다음과 같은 역할을 합니다.

CSS 카운터 속성 설명
counter-reset 카운터 변수를 정의하고, 카운터를 초기화 합니다.
셀제 카운터 순번이 붙는 요소들의 부모 요소에 속성을 선언해야 합니다.
변수명 뒤에 시작 값을 추가로 지정할 수 있습니다.
별도의 시작 값을 지정하지 않으면 변수 값은 0에서 시작합니다.
counter-reset: 변수명 [시작값];
counter-reset: var-count;
counter-reset: num-counter 25;
counter-increment 카운터 변수의 증가 단위를 설정합니다. 별도의 증가 단위 표시 없이 변수명만 표시하면 1씩 증가합니다. counter-increment: 변수명 [증가단위];
counter-increment: var-count;
counter-increment: var-count 5;
counter() 가상 요소로 각 항목 앞에 표시할 순번 변수 값을 계산합니다. 문자열과 조합해서 순번의 표현 방식을 다양하게 작성할 수 있습니다.
파라메터는 1개이며 부모 요소에 정의한 변수명을 받습니다.
content: ["추가문자열"] counter(변수명) ["추가문자열"]
content: counter(var-count) ": ";
counters() 트리형으로 중첩된 순번 표시를 지원합니다.
트리형 다단 목차 표시와 같은 중첩된 구조의 순번 표시를 하는데 사용합니다.
파라메터는 2개이며 부모 요소에 정의한 변수명과 중첩 단계별 순번을 구분하는 구분자를 파라메터로 받습니다.
반드시 2개의 파라메터가 모두 있어야 동작합니다.
content: ["추가문자열"] counter(변수명, "구분자") ["추가문자열"]
content: counter(var-count, ".") " ";

CSS 카운터 속성 중 "counter()"는 증가 단위 속성인 "counter-increment"으로 지정한 값만큼씩 자동 증가하는 순번을 부여하는 일종의 함수 기능을 한다고 이해하면 됩니다.

"counter()"와 "counters()"는 같은 함수이며, 복수형 "counters()"는 트리형 중첩 순번을 표시할 때만 예외적으로 사용합니다. 뒤에서 사용 방법에 대해서 다시 배웁니다.

두 함수는 단수와 복수형으로 구분하고 "counters()" 함수는 파라미터가 2개 필요하며 반드시 2개를 모두 명시해야 합니다. 그렇지 않으면 동작하지 않습니다.

"counters()"의 첫 번째 파라미터는 부모 요소의 "counter-reset" 속성에서 정의한 변수명이 오고, 두 번째 파라메터는 표시되는 순번의 각 레벨을 구분하는 구분자 문자열이 옵니다. 구분자가 없을 경우에는 ""와 같이 빈 문자열이라도 표시를 해서 파라미터를 명시해야 합니다.

CSS 카운터는 CSS 선택자로 선택한 요소에만 순번을 붙일 수 있습니다.

다음과 같은 HTML에 앞서의 CSS 카운터를 적용하면 <p> 태그에만 선택적으로 순번을 붙일 수도 있습니다.

<div class="counter1">
  <div>블록1</div>
  <p>문단1</p>
  <h2>제목1</h2>
  <p>문단2</p>
  <h2>제목2</h2>
  <p>문단3</p>
  <h2>제목3</h2>
</div>

"counter-reset" 속성으로 CSS 카운터 변수를 정의할 때 시작 값을 표시하지 않으면 0으로 시작합니다. 주의해야 합니다.

그리고 첫 번째 표시 순번 번호는 "counter-increment"로 정한 증가 단위를 더한 숫자가 됩니다.

즉, "counter-reset"의 시작 값이 0이라면 0 + counter-increment 값이 첫 번째 표시 순번이 됩니다.

앞의 HTML 예를 다음과 같이 시작 번호와 증가 단위를 지정해서 CSS 카운터를 표시하면 값이 어떻게 증가하는지 쉽게 알 수 있습니다. 첫 번째 순번 값은 20이 되고, 두 번째는 27, 마지막은 34가 됩니다.

div#counter1{
	counter-reset: var-count 13;
}
div#counter1 > p::before{
	counter-increment: var-count 7;
	content: counter(var-count) ": ";
}

CSS 카운터 순번과 가상 클래스

CSS 가상 요소로 순번을 붙일 때는 HTML 요소의 특징을 활용하면 다양한 조건으로 순번을 붙일 수도 있습니다.

<div class="remark">
  <p><a class="bookmark" href="#remark1"></a></p>
  <p><a href="contactus.html">문의하기</a></p>
  <p><a class="bookmark" href="#remark2"></a></p>
  <p><a href="#remark3">주석별도</a></p>
</div>

웹 페이지의 다른 위치에 있는 주석에 대한 설명으로 이동할 수 있도록 하이퍼링크를 달아놓은 링크 표시입니다.

CSS 선택자와 CSS 카운터를 이용해서 주석 링크를 보기 좋게 완성해 보겠습니다.

먼저 위의 HTML은 다음과 같이 보입니다. 첫 번째와 세 번째 하이퍼링크에는 링크를 표시하기 위한 텍스트 내용이 없는 것에 주의해야 합니다. 이 비어있는 하이퍼링크 텍스트를 CSS 카운터를 사용해 자동으로 순번을 매겨서 내용을 표시합니다.

다음과 같이 CSS를 작성합니다.

비어있던 주석 링크에 텍스트 내용이 표시되면서 자동으로 순번까지 붙여서 표시를 해줍니다.

.remark {
	counter-reset: rem-counter;
}
a[href] {
	counter-increment: rem-counter;
}
a[href].bookmark::after {
	content: "[주석" counter(rem-counter) "]";
}

앞의 CSS 카운터 구현은 주의해야 할 부분이 하나 있습니다. CSS 클래스 중 두 번째 클래스는 순번을 결정하는데 중요한 역할을 합니다. 두 번째 클래스가 없고 순번 증가 속성("counter-increment")을 세 번째 클래스로 병합하면 다음과 같이 순번 표시가 변경됩니다.

".bookmark" 클래스가 있는 하이퍼링크만 순번 증가가 적용되면서 두 번째 "문의하기" 하이퍼링크가 순번 증가에서 제외되기 때문에 순번이 바뀌게 되는 것입니다.

.remark {
	counter-reset: rem-counter;
}
a[href].bookmark::after {
	counter-increment: rem-counter;
	content: "[주석" counter(rem-counter) "]";
}

작성한 주석 문 순번 붙이기를 조금 더 세련되게 만들면 다음과 같이 만들 수 있습니다.

".bookmark" 클래스를 부여한 하이퍼링크에 순번을 붙이는 대신 하이퍼링크 텍스트 내용이 없는 것만 골라서 순번을 붙일 수 있습니다.

":empty"는 텍스트 내용이 없는 요소만을 선택하는 가상 클래스로 ".bookmark" 클래스는 더 이상 필요하지 않습니다.

.remark {
	counter-reset: rem-counter;
}
a[href]:empty::after {
	counter-increment: rem-counter;
	content: "[주석" counter(rem-counter) "]";
}

가상 클래스에 대한 이해가 부족하면 다음 강의를 참고하면 됩니다.

> HTML+CSS 기초 강의 - 28. CSS 선택자 기초 5 - 가상 클래스

> HTML+CSS 기초 강의 - 25. CSS 선택자 기초 2 - 클래스와 가상 클래스의 조합

> CSS 선택자 고급 - 가상 클래스(Pseudo Class)로 선택하기

CSS 카운터 순번 중첩

CSS 카운터는 변수 값을 중첩해서 상속하는 특징이 있습니다.

설명보다는 다음 예를 보면 어떤 모습인지 쉽게 알 수 있습니다.

중첩 CSS 카운터 구현은 앞서 만들었던 CSS 카운터와 같지만 자동 순번 값을 증가시키는 함수가 "counters()"라는 점만 다릅니다.

기본적으로 중첩형 목록은 리스트 태그(<UL>, <OL>)를 사용하는 것이 더 구현이 쉽고 간결하지만, 태그 종류에 관계없이 트리형으로 중첩된 CSS 카운터를 표현할 수 있다는 것을 보여주기 위해서 블록 태그(<DIV>)만으로 구현을 했습니다.

<div  id="counter">
    <div class="parent">
        <div>아이템</div>                <!-- 1 -->
        <div>아이템                      <!-- 2 -->
            <div class="parent">
                <div>아이템</div>            <!-- 1 -->
                <div>아이템</div>            <!-- 2 -->
                <div>아이템                  <!-- 3 -->
                    <div class="parent">
                        <div>아이템</div>         <!-- 1 -->
                        <div>아이템</div>         <!-- 2 -->
                    </div>
                    <div class="parent">
                        <div>아이템</div>         <!-- 1 -->
                        <div>아이템</div>         <!-- 2 -->
                        <div>아이템</div>         <!-- 3 -->
                    </div>
                </div>
                <div>아이템</div>            <!-- 4 -->
            </div>
        </div>
        <div>아이템</div>                <!-- 3 -->
        <div>아이템</div>                <!-- 4 -->
    </div>
</div>

CSS는 다음과 같습니다. 블록 태그로만 중첩 목록을 구현했기 때문에 CSS 카운터의 부모 요소를 구분할 수 있도록 부모 요소에는 ".parent" 클래스를 따로 부여했습니다.

그리고 순번이 추가되는 자식 요소들에서는 부모 요소를 배제해야 하기 때문에 ":not(.parent)" 필터링 가상 클래스로 제외를 시켰습니다.

앞서 설명한 대로 순번을 계산하는 함수는 "counters()"로 복수형 함수명을 사용합니다. 그리고 두 번째 파라미터로 구분자를 넣어서 순번 레벨별로 구분이 되도록 해주었습니다.

#counter div.parent{
    counter-reset: var-count;
    list-style-type: none;
    margin: 10px 0 10px 40px;
}
#counter div:not(.parent)::before{
    counter-increment: var-count;
    content: counters(var-count,".") " ";
}

중첩 CSS 카운터의 최대 장점은 단일 레벨 CSS 카운터와 유사한 짧은 CSS 만으로 트리형 목차 리스트를 만들 수 있다는 점입니다. 자바스크립트로 구현을 하려면 여러 개의 중첩 루프를 돌면서 로컬 변수 여러 개를 관리해서 만들어야 하는 중첩 순번을 간단하게 구현할 수 있습니다.