HTML 요소 inert 속성에 대해 알아보자


크롬 브라우저가 102로 업데이트(22/05/24)되면서 변경 사항을 확인하다가 HTML 요소에 전역으로 넣을 수 있는 inert 속성에 대해서 접하게 되었다. inert는 필자에게 생소한 속성명이라 무엇인지 알아보고 그 내용을 정리해 보았다.

inert 의미

inert의 사전적 의미'움직이지 않거나 움직일 수 없는' 라는 '비활성 상태의' 의미가 있다. HTML 요소에 inert를 속성으로 사용하면 해당 요소를 비활성 상태로 만들겠다는 것이다.

그렇다면 노드가 비활성 상태(inert)일 때 어떻게 동작할까?

크롬 102 버전 업데이트 내용 중 inert 속성의 상세 설명 페이지에서는 다음과 같이 설명하고 있다.

  • CSS 속성 pointer-events: none;으로 설정된 것처럼 동작한다
  • CSS 속성 user-select: none;으로 설정된 것처럼 동작한다
  • 편집할 수 있는 노드를 편집할 수 없는 것(non-editable)처럼 동작한다
  • 사용자 에이전트의 페이지 내 노드 탐색 무시한다

inert 속성 사용하기

inert속성은 HTMLElement 모든 요소의 속성으로 사용할 수 있으며, 해당 요소가 마우스 클릭, 포커싱 이벤트와 텍스트 선택 같은 사용자와 상호 작용할 수 있는 이벤트 대상에 포함 되는지 여부를 부울 값으로 나타낸다.

다른 Boolean 속성과 동일하게, 요소에 inert속성이 있으면 true로 간주된다.

<div inert>...</div>
<!-- inert: true -->
<div>...</div>
<!-- inert: false -->

스크립트에서 동적으로 inert 속성을 설정할 수 있다.

isInert = HTMLElement.inert;

HTMLElement.inert = true | false;
// 또는
HTMLElement.setAttribute('inert', ''); // 요소에 inert 속성 적용
HTMLElement.removeAttribute('inert'); // 요소에 inert 속성 제거

요소에 inert 속성을 정의하면 마우스, 입력 이벤트를 무시하며, 보조 기술(assistive technology) 이벤트를 포함한 포커스 이벤트와 텍스트 선택을 무시한다.

보조 기술에 대한 설명은 이글에서 다루지 않는다. web.dev접근성 트리를 읽어보면 도움이 될 것이다.

inert 속성은 disabled와 달리 브라우저가 기본으로 가지고 있는 스타일을 따로 변경하지 않는다. 시각적으로 구별되도록 스타일 속성을 변경할 수 있다.

[inert] > * {
  opacity: 0.5;
}

inert 사용 이점

그렇다면 inert 속성을 사용했을 때 이점은 무엇일까? inert 속성을 사용하면 스크린 리더 사용자에게 더 나은 접근성을 제공할 수 있다.

웹 콘텐츠 접근성 지침에서는 포커스 관리와 실용적이고 유용한 포커스 순서를 요구한다. 포커스 순서에는 탐색 기능(discoverability)과 상호 작용(interactivity)이 포함되는데, 접근성을 향상시키기 위해 aria-hidden="true"를 요소에 설정하여 탐색 기능을 막을 수는 있지만 상호 작용은 제어하기 어렵다.

포커스 순서의 목적은 콘텐츠를 순차적으로 탐색할 때 콘텐츠의 의미와 일치하고 키보드에서 조작할 수 있는 순서로 정보를 접하게 하는 것이다. 웹 콘텐츠에서 순차 탐색 순서가 결정되는 방식은 콘텐츠의 기술에 의해 정의된다. 예를 들어, HTML은 탭 순서를 통해 순차 탐색을 정의하고 스크립트에서 tabindex 속성을 동적으로 추가해서 탐색 순서를 수정할 수 있다. 스크립트를 수정하거나 tabindex 속성을 사용하지 않는 경우 탐색 순서는 구성요소가 콘텐츠에 나타나는 순서가 된다.

개발자는 inert를 사용하여 요소를 탭 순서와 접근성 트리에서 제거할 수 있다. 이 기능을 통해 탐색 기능과 상호 작용을 모두 제어할 수 있으며, 사용자에게 더 나은 접근성을 제공할 수 있다.

더 나은 접근성을 위해 요소에 inert 속성을 적용하는 두 가지 주요 사용 사례는 다음과 같다.

  1. 요소가 DOM 트리에 포함되어있지만, 화면 밖(offscreen)에 있거나 숨겨져(hidden) 있는 경우
  2. 요소가 DOM 트리에 포함되어있지만, 상호 작용할 수 없도록(non-interactive) 설정해야하는 경우

1) 화면 밖에 있거나 숨겨져 표현되는 요소 다루기

웹 사이트에서 LNB 영역이 보였다가 닫히면 화면에 표시되지 않는 서랍형(drawer) UI를 접해본 적이 있다. inert 속성을 사용하면 LNB 영역이 화면 밖에 있을 때 사용자가 키보드로 상호작용할 수 없도록 한다.

서랍형 UI를 사용하는 간단한 예시를 작성해 보았다. 서랍형 UI인 "Drawer" 영역이 있고, 서랍형 UI를 열고 닫을 수 있는 토글 버튼이 있다. "Drawer" 영역 내부에는 상호 작용할 수 있는 입력 폼이 있다.

<body>
  <header>
    <input type="text" placeholder="Search..." />
  </header>

  <main>
    <!-- 서랍형 UI -->
    <aside class="drawer-container">
      <div class="drawer">
        <h1>Drawer</h1>
        <form action="" method="get">
          <fieldset>
            <!-- 이름 입력 -->
            <label for="name">Name: </label>
            <input type="text" name="name" id="name" required />
          </fieldset>
          <fieldset>
            <label for="email">Email: </label>
            <input type="email" name="email" id="email" required />
          </fieldset>
          <button type="submit">Subscribe!</button>
        </form>
      </div>

      <!-- 서랍형 UI를 열고 닫을 수 있는 토글 버튼 -->
      <button type="button" class="toggle-btn">&lt;</button>
      <!-- '<' -->
    </aside>

    <!-- 메인 영역 -->
    <section>
      <p id="log"></p>
    </section>
  </main>

  <script src="src/index.js"></script>
</body>

이름을 입력할 때마다 콘솔에 입력값이 로그로 남도록 이벤트를 등록했다.

const drawerContainer = document.querySelector('.drawer-container');
const toggleBtn = drawerContainer.querySelector('.toggle-btn');
const nameInput = drawerContainer.querySelector('#name');

toggleBtn.addEventListener('click', (e) => {
  const isOpened = drawerContainer.classList.contains('drawer-closed');

  drawerContainer.classList.toggle('drawer-closed');
  toggleBtn.textContent = isOpened ? '<' : '>';
});

nameInput.addEventListener('input', (e) => {
  console.log(`input name... ${e.target.value}`);
});

메인 영역에 키보드 이벤트가 발생하면 키 코드를 남기도록 작성했다.

const log = document.getElementById('log');

document.addEventListener('keyup', (e) => {
  log.innerHTML += ` ${e.code}<br>`;
});

서랍형 UI가 닫혀있는 상태에서 키보드를 사용해 포커스를 이동한 후 타이핑해 보았다. 콘솔에 입력한 글자가 찍히고 있었다. 😕 drawer_new_1

서랍형 UI가 닫힌 상태에서는 inert속성을 추가해주었다.

const drawer = drawerContainer.querySelector('.drawer');

toggleBtn.addEventListener('click', (e) => {
  // ...
  drawer.inert = !isOpened;
});

drawer_new_2

키보드 이벤트가 발생하여도 요소에 입력되는 값은 없다. 페이지에서 닫힌 서랍형 UI에 inert속성을 추가하면, 스크린 리더의 포커싱 대상에서 서랍형 UI는 제외되며 사용자가 실수하더라도 화면에 보이지 않는 영역에서 상호 작용이 제한될 것이다.

2) 상호 작용할 수 없는 DOM 요소 다루기

페이지 상에서는 표시되지만, 사용자와 상호 작용할 수 없는 콘텐츠를 다룰 때 활용할 수 있다. 예를 들면 모달 같은 대화상자가 열려있을 때는 배경에 있는 콘텐츠들을 선택할 수 없다.

사용자에게 좋은 경험을 제공하기 위해 전체 페이지에서 사용자와 상호 작용할 수 있는 대화형 UI 요소 부분에 포커스를 맞추도록 한다. 이를 "포커스 트래핑"이라 한다.

포커스 트래핑은 좋은 UI 접근성의 핵심 개념이다. 스크린 리더는 포커스가 대화형 UI 요소에 있는지 확인하고 요소가 대화형 UI에서 더 이상 상호 작용하지 않는 상태로 변경됨을 인식한다.

inert를 사용하면 탐색 가능한 콘텐츠에만 접근할 수 있으며, 다음과 같은 경우에 유용하다.

  • 모달, 포커스 트래핑 메뉴, 사이드 네비게이션 메뉴와 같은 요소를 차단하는 경우
  • 캐러샐이나 슬라이드에서 비활성(non-active) 항목이 있는 경우
  • 적용할 수 없는 폼 콘텐츠의 경우 (예: "청구서 수신 주소와 동일"확인 체크박스가 선택된 경우 "배송 주소" 필드가 페이드 아웃(fade-out)되거나 비활성화됨)
  • 특정 영역의 UI를 전체 비활성화해야하는 경우

모달 UI를 띄우는 간단한 테스트 페이지를 만들었다.

<!-- 메인 영역 -->
<main>
  <h1>Main Page</h1>
  <input type="text" name="name" id="search" placeholder="Search..." />
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto recusandae amet, rerum quasi
    quaerat ipsum nam earum vitae distinctio deleniti. Rerum earum excepturi ab, totam tenetur fuga
    praesentium illum repudiandae.
  </p>
  <!-- ... --->

  <!-- 모달 UI를 여는 "Open Modal" 버튼 -->
  <button class="open-modal-btn">Open Modal</button>
</main>

"Open Modal" 버튼을 클릭하면 아래와 같은 "Join Form"이라는 가입 양식을 포함한 모달 UI가 열린다.

HTML <dialog> 요소를 사용할 수 있지만 여기서 사용된 예시는 일반 <div> 요소로 작성해 주었다. <dialog> 요소를 사용하면 탭 키를 사용한 포커싱 문제가 발생하지 않는다.

<!-- Modal UI -->
<div class="modal hidden">
  <div class="modal-content">
    <h1>Join Form</h1>
    <form action="" method="get">
      <fieldset>
        <label for="name">Enter your name: </label>
        <input type="text" name="name" id="name" required />
      </fieldset>
      <fieldset>
        <label for="email">Enter your email: </label>
        <input type="email" name="email" id="email" required />
      </fieldset>
      <button type="submit" value="Subscribe!">Submit</button>
    </form>
    <button aria-label="close" class="close-btn x"></button>
  </div>
</div>

메인 영역의 "Open Modal" 버튼과 모달 UI의 닫기 버튼에 이벤트 리스너를 등록한다.

const main = document.querySelector('main');
const openModalButton = main.querySelector('.open-modal-btn');
const modal = document.querySelector('.modal');
const closeButton = modal.querySelector('.close-btn');

function displayModal() {
  modal.classList.toggle('hidden');
}

openModalButton.addEventListener('click', displayModal);
closeButton.addEventListener('click', displayModal);

키보드 탭 키를 사용해 포커스를 이동해 보았다. "Join Form" 모달이 열려있는 상태이지만, 메인 영역의 검색 input 요소와 모달 UI를 여는 "Open Modal"버튼에도 포커싱되있는 것을 확인할 수 있다. modal_new_2

모달 UI가 열린 상태에서는 메인 영역에 inert속성을 추가해 주었다.

function displayModal() {
  // ...

  main.inert = !modal.classList.contains('hidden');
}

키보드 탭키를 사용하여 포커스를 이동하면 페이지에서 열린 모달에서만 포커스가 이동되는 것을 확인할 수 있다. modal_new_1

페이지에서 열린 모달 UI을 제외하고 나머지 요소에 inert 속성을 적용하면 스크린 리더의 탐색 대상이 모달 UI로 제한할 수 있다.

inert vs disabled vs readonly

추가로, disabledreadonly속성과의 차이점을 표로 정리해보았다.

inert disabled readonly
지원 요소 전체 HTML 요소 <button>, <fieldset>, <keygen>, <optgroup>, <option>, <select>, <textarea>, <input> text, search, url, tel, email, password, date, month, week, time, datetime-local, number 타입의 <input>, <textarea>
내용 편집 유무 X X X
스타일 변경 유무 X O - 회색 음영 또는 연하게 표시 X
포커스 가능 유무 X X O
required와 함께 사용할 때 폼 전송 유무 X O O
텍스트 선택 유무 X O O

브라우저 지원 현황

inert를 범용적으로 사용하기에 아직은 무리가 있다. 하지만 프로덕트 지원 브라우저 범위가 크롬 최신 버전이면서 웹 접근성을 고려한다면 inert 속성을 한 번 사용해 보는 것도 좋으리라 생각된다.

Chrome Edge Firefox Internet Explorer Opera Safari
102 102 Nightly X X 15.5

(버전 확인 날짜 : 22/06/03 기준)

정리

aria-hidden="true"를 사용하면 탐색과 스크린 리더와 다른 보조 기술 사용자에게 해당 요소를 숨길 수 있다. 하지만 이 요소 자손으로 포커싱 가능한(focusable) 요소가 존재한다면 키보드 사용자들은 포커싱 가능한 요소에 접근할 수 있는데 사용자에게 혼란을 줄 수 있다. inert는 브라우저 지원 범위가 좁아 일반적으로 사용하기에는 아직 한계가 있다. 하지만 요소를 탐색 불가능하거나 상호 작용하지 않도록 만들기 위해 개별적으로 요소의 tabindex를 조절하거나 특정 HTML요소에 적용할 수 있는 disabled 속성 또는 CSS point-events, user-select 사용하여 제어하기보다는 모든 HTML 요소에서 사용할 수 있는 inert 속성을 상호 작용하지 않는 전체 영역에 한꺼번에 적용하는 것이 더 편리할 것이다. 주의할 점은 inert속성은 HTML 스타일을 따로 변경하지 않는다. 따라서 상호 작용하지 않는 요소에 [inert]선택자를 활용해 스타일을 변경하면 사용자에게 더 좋은 사용성을 제공할 것이다. 마지막으로 inert속성에 대한 폴리필 코드는 github WICG/inert에서 확인할 수 있다.

참고

조정은2022.06.03
Back to list