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

가상(Pseudo) 클래스는 클래스, 또는 태그명 선택자 뒤에 콜론(:) 을 붙여서 표시하는 특별한 요소의 상태, 또는 요소의 순서를 기준으로 자식 요소를 선택하는 방법을 기술한 것입니다.

선택자:가상클래스{

    속성: 값;

}

으로 정의하며 예를 들어 다음과 같은 방식으로 가상 클래스를 기술해 표현합니다.

p:first-child {}
a:hover {}
article:first-child {}
article p:nth-of-type(2n) {}

가상 클래스는 상태, 또는 선택 방법을 기술하는 정해진 키워드가 있으며, 정해진 방식으로 사용해야 합니다.

또한 가상 클래스 선택자를 잘못 사용하면 원하는 요소가 선택되지 않을 수 있습니다.

예를 들어 다음과 같은 HTML 엘리먼트 구조를 가상 클래스를 사용해 선택해보겠습니다.

<h1>문서 타이틀</h1>
<article class="first-paragraph">
  <h1>섹션 타이틀 1</h1>
  <p>문단 1</p>
  <p>문단 2</p>
  <p>문단 3</p>
</article>
<article>
  <h1>섹션 타이틀 2</h1>
  <p>문단 4</p>
  <p>문단 5</p>
</article>

:first-child, :last-child

부모 요소의 전체 자식 요소들 중 첫 번째, 또는 마지막 요소를 선택하는 가상 클래스입니다.

자식 요소들의 순서를 기준으로 첫 번째, 또는 마지막 요소를 선택합니다.

다음과 같은 HTML 코드에서

<article>
  <h1 class="item">섹션 타이틀</h1>
  <p class="item">문단 1</p>
  <p class="item">문단 2</p>
  <p class="item">문단 3</p>
</article>

article h1:first-child{}

는 "섹션 타이틀" 을 선택합니다. 그러나

article p:first-child{}

는 아무런 요소를 선택하지 않습니다. 언뜻 "article"의 자식 요소들 중 첫번째 나오는 "p"를 선택해서 "문단 1" 이 선택될 것 같지만, "p:first-child" 는 순서상 첫번째 요소(first-child)이면서 (그리고) p 여야 합니다. 동시 충족해야 하며, 따라서 조건에 만족하는 요소는 존재하지 않습니다. 

선택하고자 하는 요소가 "문단 1" 요소였다면 "p:first-of-type" 와 같이 태그 타입을 먼저 선택한 후 순서 필터링을 하는 가상 클래스를 사용해야 합니다. "list-of-type"은 밑에서 배웁니다.

태그 대신 클래스를 기준으로 선택을 하면

article .item:first-child{}

는 순서상 첫 번째 요소이면서 "item" 클래스를 가진 요소를 선택하므로 "섹션 타이틀" 이 선택됩니다.

태그로 자식 요소를 선택할 때는 순서 가상 클래스가 동시에 만족해야 하는 조건임을 명심해야 합니다.

:nth-child(n), :nth-last-child(n)

선택한 부모 요소의 자식 요소들 중 n번째 오는 자식 요소들을 선택합니다.

자식 요소들의 나오는 순서를 기준으로 선택을 하며, n에는 간단한 연산식을 적용해 선택하는 요소를 필터링할 수 있습니다. n은 서수 값으로 1에서 시작합니다. 연산식을 사용할 수 있으므로 변수를 사용할 수 있으며, "2n+1"과 같이 변수를 조합해 수식을 사용할 수 있습니다. 이때 수식에 사용한 변수에 대입하는 값은 0에서 시작하는 정수입니다.

최종적으로 적용되는 값과 변수를 사용할 때 변수에 적용하는 값이 다른 점에 주의해야 합니다. 최종 값은 서수이므로 1에서 시작하지만, 수식으로 변수를 사용할 경우 변수에 대입되는 시작 값은 0에서 부터입니다.

nth-child와 nth-last-child는 순서가 반대입니다. nth-child는 처음 요소부터, nth-last-child는 마지막 요소부터 선택을 합니다.

예를 들어

article .item:nth-child(1){}

은 "섹션 타이틀" 을 선택합니다.

article .item:nth-child(2n+1){}

은 첫 번째(2*0+1)와 세 번째(2*1+1) 요소를 선택합니다.

"문단 1" 과 "문단 3" 이 선택됩니다.

:first-of-type, :last-of-type

부모 요소의 자식 요소들 중에 특정 선택자 태그인 첫 번째, 또는 마지막 요소를 선택하는 가상 클래스입니다. 

first-child/last-child와 다른 점은 자식 요소들 중 선택자 태그와 일치하는 요소만 먼저 필터링 한 후, 필터링 된 자식 요소들 중에서 첫 번째, 또는 마지막 요소를 선택합니다.

article p:first-of-type{}

으로 선택하면 "문단 1"이 선택됩니다.

태그를 선택자로 사용해서 자식 요소들 중에서 특정 태그만 필터링하는 가상 클래스지만, 선택자로 클래스를 사용할 수도 있습니다. 단, 태그를 사용할 때와는 다른 방식으로 선택이 되므로 클래스를 선택자로 사용할 때는 주의해야 합니다.

article .item:first-of-type{}

이렇게 선택을 하면 "섹션 타이틀"과 "문단 1" 2개가 선택됩니다. 클래스 선택자로 선택하면 자식 요소들 중 각 태그별로 첫 번째 나오는 요소를 모두 선택합니다.

:nth-of-type(n), :nth-last-of-type(n)

선택한 요소의 자식 요소들 중 n번째 오는 특정 태그인 자식 요소들을 선택합니다.

n은 1부터 시작하는 서수입니다.

:nth-child(n)와 다른 점은 가상 클래스로 지정한 특정 태그만을 대상으로 요소를 선택합니다. 예를 들어 "p:nth-of-type(n)"으로 선택하면 자식 요소들 중 "<p>" 태그들 만을 대상으로 n번째 요소(들)을 선택합니다.

다음과 같은 HTML 내용에서

<article>
  <h1 class="item">섹션 타이틀</h1>
  <p class="item">문단 1</p>
  <p class="item">문단 2</p>
  <p class="item">문단 3</p>
  <p class="item">문단 4</p>
</article>

"article p:nth-of-type(2n+1)" 로 선택을 하면 "문단 1", "문단 3" 이 선택됩니다.

n에는 nth-child와 동일하게 간단한 수식을 사용할 수 있습니다. 선택 수식에 사용한 n은 0부터 시작합니다.

태그가 아닌 클래스를 기준으로 "article .item:nth-of-type(2n+1)" 로 선택하면 "섹션 타이틀", "문단 1", "문단 2" 가 선택됩니다.

nth-of-type과 nth-last-of-type은 선택 방향이 반대입니다. nth-of-type은 첫 요소부터, nth-last-of-type은 마지막 요소부터 선택을 합니다.

"p:nth-last-of-type(2n+1)"로 선택하면 "문단 2", "문단 4" 가 선택됩니다.

:not(x)

x는 태그, 또는 클래스 이름을 사용할 수 있습니다.

부모 요소의 자식 요소들 중 "x" 인 태그, 또는 클래스가 아닌 요소를 모두 선택합니다. 필요 없는 요소(들)을 제외하고 나머지 모든 자식 요소들을 선택해야 할 때 사용할 수 있습니다.

다음과 같은 HTML 내용에서

<article>
  <h1 class="item">섹션 타이틀</h1>
  <p>문단 1</p>
  <p class="item">문단 2</p>
  <p class="item">문단 3</p>
  <p class="item">문단 4</p>
</article>

"article p:not(.item)" 으로 선택을 하면 "문단 1"만 선택됩니다.

article의 자식 요소들 중 "<p>" 태그를 가진 요소들 중에서 ".item" 클래스가 없는 요소만을 선택합니다.

링크 요소(마우스 클릭에 반응하는 요소)의 다양한 상태를 표현하는데 사용하는 가상 클래스입니다.

태그, 또는 클래스가 적용된 요소에 마우스 동작에 따른 반응 인터페이스를 구현할 때 사용합니다.

최초에는 링크(<a>), 또는 폼 요소(버튼 요소 등)에 대한 구현체였으나, 특별히 다른 태그나 클래스에 대한 제한이 없었기 때문에, 모든 태그에 대해 마우스 액션에 반응하는 기능을 구현할 수 있습니다.

마우스 클릭이 되는 하이퍼 링크(<a>), 또는 폼 요소와 일반 태그 요소가 다른 점은 별도로 가상 클래스를 정의하지 않아도 내부적으로 기본 정의된 가상 클래스들이 존재합니다.

가상 클래스 설명
:link 방문하지 않은 링크 a:link{
    color: #ff0000;
}
:hover 마우스 커서가 요소 위에 위치할 때 a:hover{
    color: #00ff00;
}
:visited 방문한 링크 a:visited{
    color: #0000ff;
}
:active 링크 선택시 a:active{
    font-weight: bold;
}

링크 요소가 아닌 "<p>" 태그에 마우스 호버 가상 클래스를 적용하면 

article p:hover{

    background-color: red;

}

문단에 마우스 호버가 되면 다음과 같이 배경색이 적용됩니다.

 

 

:focus, :focus-within

클릭하거나 탭했을 때, 또는 포커스를 받을 수 있는 요소(입력 필드 등)를 탭 키를 사용해 이동해 선택한 경우, 요소에 포커스가 가면서 선택자 속성이 적용됩니다.

현재 포커스가 위치한 요소를 확실히 알 수 있도록 속성을 부여할 수 있습니다. 예를 들어 다음과 같이 폼의 입력 요소에 포커스가 위치하면 빨강색 테두리 속성을 부여해 구분이 되도록 할 수 있습니다.

form input:focus{

    border: 3px solid red;

}

다음과 같은 HTML 폼 내용이 있다면

<form name="myform">
  <p>로그인</p>
  <input type="text" name="userid" size="20" maxlength="20" placeholder="아이디"/>
  <input type="password" name="userpw" size="20" maxlength="20" placeholder="패스워드"/>
</form>

입력 포커스가 위치하면 다음과 같이 CSS가 적용됩니다.

focus-within 가상 클래스는 포커스가 위치한 요소를 포함하고 있는 요소에 속성을 부여하고 싶을 경우 :focus-within 선택자를 사용할 수 있습니다.

예를 들어 앞의 HTML 양식 태그에

form:focus-within{

    color: blue;

}

과 같이 폼에 가상 클래스를 적용하면, 폼 안의 입력 필드에 포커스가 위치하면 폼 안에 표시되는 텍스트 컬러가 파랑색으로 변경됩니다.

:focus-within 가상 클래스는 활용하기에 따라서 다음과 같이 입력 포커스가 위치하면 안내 메시지를 표시하는 것과 같이 사용자 액션에 반응하는 UI를 만드는데 사용할 수 있습니다.

<form name="myform">
  <p>로그인</p>
  <span class="msg">아이디와 패스워드는 8자리 이상 입력해야 합니다.</span>
  <input type="text" name="userid" size="20" maxlength="20" placeholder="아이디"/>
  <input type="password" name="userpw" size="20" maxlength="20" placeholder="패스워드"/>
</form>
form span.msg{
  display: none;
}

form:focus-within span.msg{
  color: red;
  display: block;
}

:checked

체크 박스, 또는 라디오 박스를 위한 가상 클래스입니다.

다른 요소에는 사용할 수 없으며, 사용해도 태그 속성이 존재하지 않아 요소가 선택자로 선택될 수 없기 때문에 가상 클래스가 적용되지 않습니다.

"checked" 태그 속성이 켜지거나 꺼진 상태에 따라 가상 클래스가 적용되거나 미적용됩니다.

다음과 같은 라디오박스와 체크박스 선택 양식이 있으면

<div>
  <input type="radio" id="sexmale" name="sex" value="M"/><label for="sexmale">남</label>
  <input type="radio" id="sexfemale" name="sex" value="F"/><label for="sexfemale">여</label>
</div>
<div>
  <input type="checkbox" id="checkjava" name="language"/><label for="checkjava">JAVA</label>
  <input type="checkbox" id="checkcsharp" name="language"/><label for="checkcsharp">C#</label>
  <input type="checkbox" id="checkpython" name="language"/><label for="checkpython">PYTHON</label>
</div>

:checked 가상 클래스로 체크된 항목에 다양한 효과를 적용할 수 있습니다.

input:checked + label { //체크된 입력 필드의 라벨 텍스트 색상을 빨강색으로 변경
    color: red;
}
input[type="radio"]:checked { //체크된 라디오박스 크기 키움
    width: 20px;
    height: 20px;
}

input[type="checkbox"]:checked { //체크된 체크박스 크기 키움
    width: 20px;
    height: 20px;
}

가상 클래스의 범위 제한

가상 클래스는 굉장히 편리한 선택 방법이지만, 선택하는 범위를 명확하게 제한하지 않으면, 예상했던 것과는 전혀 다른 요소들이 선택됩니다. 또, 지나치게 많은 요소를 한꺼번에 선택하면 렌더링 속도와 화면 갱신에 영향을 주게 되므로 주의해서 사용해야 합니다.

특별한 경우가 아니면 "p:hover" 와 같은 가상 클래스를 HTML 페이지 전역으로 정의해 사용하는 것은 추천하지 않습니다.