본문 바로가기
HTML, CSS

:has() 선택자

by 타로 스토리 2023. 5. 31.
반응형

":has() 의사 클래스는 '부모 선택자'로 불리며, 단순히 요소의 조상을 스타일링하는 것 이상의 범위를 가지고 있습니다. Safari 15.4+ 및 Chromium 105+에서 사용 가능하며, Firefox에서는 플래그로 사용됩니다.

 

 

가상 클래스로서, :has()의 기본적인 기능은 해당 요소인 '대상' 요소를 스타일링하는 것입니다. 이는 다른 가상 클래스인 :hover 또는 :active와 유사합니다.

하지만 :has()는 :is(), :where(), :not()와 유사하게 괄호 내에서 상대적인 선택자 목록을 받아들이기 때문에 복잡한 기준을 테스트할 수 있습니다. 이를 통해 :has()는 매우 강력한 선택자가 됩니다.

:has()가 어떻게 작동하는지 이해하기 위해 예제를 살펴보겠습니다.

 

다음 선택자에서는 <article> 요소가 <img> 요소를 자식으로 가지는지 테스트 합니다.

article:has(img) {}

article:has(img) {
  background-color: #81D4FA;
  padding: 0 0 2rem;

  img {
    border-radius: 0.5rem 0.5rem 0 0;
  }

  > *:not(img) {
    padding-inline: 1.5rem;
  }
}

아래 이미지는 이 선택자의 가능한 결과 중 하나를 보여줍니다.

세 개의 article 요소가 표시되며, 이미지를 포함한 두 개의 요소는 푸른색 배경과 이미지가 없는 요소와는 다른 패딩을 가지고 있습니다.

위의 선택자는 <img> 요소가 <article> 요소 내에서 어디에 있든지 상관없이 적용됩니다. 직접적인 자식으로 존재하거나 다른 중첩 요소의 하위 요소로 존재하는 경우에도 해당합니다.

만약 <img>가 <article> 요소의 직접적인 (중첩되지 않은) 자식인 경우에만 규칙이 적용되도록 하려면, 자식 조합자를 추가할 수도 있습니다:

article:has(> img) {}

article:has(> img) {
  background-color: #81D4FA;
  padding: 0 0 2rem;

  img {
    border-radius: 0.5rem 0.5rem 0 0;
  }

  > *:not(img) {
    padding-inline: 1.5rem;
  }
}

이 변경 사항의 결과는 아래 이미지에서 확인할 수 있습니다. 동일한 세 개의 카드가 표시되지만, 이번에는 이미지가 <article>의 직접 자식인 경우에만 푸른색 배경과 패딩이 적용됩니다.

두 선택자 모두 정의한 스타일이 대상 요소인 <article>에 적용됩니다. 

이것이 사람들이 종종 :has()를 "부모" 선택자라고 부르는 이유입니다. 특정 요소가 특정한 방식으로 존재하는 경우에는 해당 "부모"가 지정된 스타일을 받습니다.

:has() 의사 클래스 자체는 선택자에 특수성 가중치를 추가하지 않습니다. 
:is()와 :not()과 마찬가지로 :has()의 특수성은 선택자 목록에서 가장 높은 특수성을 가진 선택자와 동일합니다.예를 들어, :has(#id, p, .class)는 id에 할당된 특수성을 갖습니다.

우리는 인접 형제 결합자 (+)를 사용하여 특정 인접 형제 요소가 뒤따라오는 경우에도 대상 요소를 선택할 수 있습니다. 다음 예제에서는 <h1> 요소가 직접적으로 <h2> 다음에 따라오는 경우에만 선택합니다:

h1:has(+ h2) {}

h1:has(+ h2) {
  font-size: 3rem;
  background-color: #81D4FA;

  + h2 {
    margin-block-start: 0.15em;
    color: #797979;
    font-weight: 500;
    font-style: italic;
  }
}

아래 이미지에서는 두 개의 <article> 요소가 표시됩니다. 첫 번째 요소에서는 <h1> 뒤에 <h2>가 따라오기 때문에 <h1>에 연한 푸른색 배경이 적용됩니다.

일반 형제 결합자 (~)를 사용하면 대상 이후에 특정 요소가 형제로 어디에 있는지 확인할 수 있습니다. 여기에서는 <ul>의 형제로 어딘가에 <p> 요소가 있는지 확인하고 있습니다:

ul:has(~ p) {}

ul:has(~ p) {
  background-color: #81D4FA;
  margin-block-end: 2rem;
}

아래 이미지에서는 두 개의 <article> 요소가 표시됩니다. 각각의 <article> 요소는 순서 없는 목록을 포함하고 있습니다. 두 번째 <article> 요소의 목록 뒤에는 단락이 따라오기 때문에 푸른색 배경이 적용됩니다.

지금까지 사용한 선택자는 :has()에 연결된 대상 요소인 <ul>과 같은 요소를 스타일링했습니다. 일반적인 선택자와 마찬가지로 :has() 선택자를 더 복잡하게 확장하여 직접적으로 :has() 선택자에 연결되지 않은 요소에 대한 스타일링 조건을 설정할 수 있습니다.

다음 선택자에서는 스타일이 <h3>를 인접한 형제로 가지는 <h2>의 형제인 모든 <p> 요소에 적용됩니다:

h2:has(+ h3) ~ p

h2:has(+ h3) ~ p {
  background-color: #81D4FA;
  margin-inline-start: 2em;
}

아래 이미지에서는 두 개의 <article> 요소가 표시됩니다. 두 번째 <article>에서는 단락들이 푸른색 배경과 왼쪽 여백이 증가된 스타일이 적용되는데, 이는 단락들이 <h2> 뒤에 <h3>가 따라오는 형제 요소들입니다.

 

참고: :has()를 사용하는 데는 사용 가능한 CSS 선택자에 대한 충분한 이해가 필요합니다. MDN은 선택자에 대한 간결한 개요를 제공하며, 추가적인 실용적인 예제와 함께 선택자에 대한 두 부분 시리즈를 작성했습니다.

기억하세요, :has()는 선택자 목록을 받아들일 수 있으며, 이는 OR 조건으로 생각할 수 있습니다. <a> 또는 <strong> 또는 <em>을 포함하는 단락을 선택해 보겠습니다:

p:has(a, strong, em) {}

p:has(a, strong, em) {
  background-color: #81D4FA;
}

아래 이미지에서는 두 개의 단락이 있습니다. 두 번째 단락에 <strong> 요소가 포함되어 있기 때문에 푸른색 배경이 적용됩니다.

우리는 :has() 선택자를 연결하여 AND 조건을 만들 수도 있습니다. 다음 복합 선택자에서는 <img>가 <article>의 첫 번째 자식인지를 테스트하고, <article>이 <h1> 다음에 <h2>를 포함하는지도 확인하고 있습니다:

article:has(> img:first-child):has(h1 + h2) {}

article:has(> img:first-child):has(h1 + h2) {
  background-color: #81D4FA;
  padding: 0 0 2rem;

  img {
    border-radius: 0.5rem 0.5rem 0 0;
  }

  > *:not(img) {
    padding-inline: 1.5rem;
  }

  :is(h1, h2) {
    font-weight: 500;
  }

  h1 {
    font-family: NotoSans, Georgia, serif;
    font-size: 1.75rem;
    font-style: italic;
  }

  h2 {
    margin-block-start: 0.15em;
    font-size: 1.25rem;
  }
}

아래 이미지에서는 세 개의 <article> 요소가 표시됩니다. 두 번째 <article>에는 이미지가 첫 번째 자식으로 있고, <h1> 다음에 <h2>가 따라오는 경우에 푸른색 배경(및 기타 스타일링)이 적용됩니다.

 
 

 

 

반응형

댓글