Chrome DevTools에 최상위 레이어 요소에 대한 지원이 추가되어 개발자가 최상위 레이어 요소를 사용하는 코드를 더 쉽게 디버그할 수 있습니다.
이 도움말에서는 최상위 레이어 요소의 정의, DevTools가 최상위 레이어 콘텐츠를 시각화하여 최상위 레이어 요소가 포함된 DOM 구조를 이해하고 디버그하는 데 어떻게 도움이 되는지, DevTools 최상위 레이어 지원이 어떻게 구현되는지 설명합니다.
최상위 레이어 및 최상위 레이어 요소란 무엇인가요?
<dialog>
를 모달로 열면 내부에서 정확히 어떤 일이 발생하나요? 🤔
최상위 레이어에 배치됩니다. 최상위 레이어 콘텐츠는 다른 모든 콘텐츠 위에 렌더링됩니다. 예를 들어 모달 대화상자는 다른 모든 DOM 콘텐츠 위에 표시되어야 하므로 브라우저는 작성자가 z-index를 수동으로 처리하도록 강요하는 대신 이 요소를 '최상위 레이어'에 자동으로 렌더링합니다. 최상위 레이어 요소는 Z-색인이 가장 높더라도 요소 위에 표시됩니다.
최상위 레이어는 '가장 높은 스택 레이어'라고 설명할 수 있습니다. 각 문서에는 연결된 단일 뷰포트가 있으므로 단일 최상위 레이어도 있습니다. 여러 요소가 동시에 최상위 레이어 내에 있을 수 있습니다. 이 경우 마지막 항목이 맨 위에 오도록 서로 겹쳐 표시됩니다. 즉, 모든 최상위 레이어 요소는 최상위 레이어의 선입 선출 (LIFO) 스택에 배치됩니다.
<dialog>
요소는 브라우저가 최상위 레이어로 렌더링하는 유일한 요소가 아닙니다. 현재 최상위 레이어 요소는 팝오버, 모달 대화상자, 전체 화면 모드의 요소입니다.
다음 대화상자 구현을 살펴보세요.
<main>
<button onclick="window.dialog.showModal();">Open Dialog</button>
</main>
<dialog id="dialog"></dialog>
다음은 배경에 스타일이 적용된 몇 가지 대화상자가 포함된 데모입니다 (배경은 아래에 설명).
배경이란 무엇인가요?
다행히 최상위 레이어 요소 아래의 콘텐츠를 맞춤설정하는 방법이 있습니다.
최상위 레이어의 모든 요소에는 배경이라는 CSS 가상 요소가 있습니다.
배경은 뷰포트 크기의 상자이며 최상위 레이어 요소 바로 아래에 렌더링됩니다. ::backdrop
가상 요소를 사용하면 요소가 최상위 레이어의 최상위 요소인 경우 요소 아래에 있는 모든 요소를 가리거나 스타일을 지정하거나 완전히 숨길 수 있습니다.
여러 요소를 모달로 만들면 브라우저는 가장 앞쪽의 이러한 요소 바로 아래와 다른 전체 화면 요소 위에 배경을 그립니다.
배경 스타일을 지정하는 방법은 다음과 같습니다.
/* The browser displays the backdrop only when the dialog.showModal() function opens the dialog.*/
dialog::backdrop {
background: rgba(255,0,0,.25);
}
첫 번째 배경만 표시하려면 어떻게 해야 하나요?
모든 최상위 레이어 요소에는 최상위 레이어 스택에 속하는 배경이 있습니다. 이러한 배경은 서로 겹치도록 설계되어 있으므로 배경의 불투명도가 100%가 아니면 아래 배경이 표시됩니다.
최상위 레이어 스택의 첫 번째 배경만 표시해야 하는 경우 최상위 레이어 스택의 항목 식별자를 추적하여 이를 실행할 수 있습니다.
추가된 요소가 최상위 레이어의 첫 번째 요소가 아닌 경우 요소가 최상위 레이어에 배치될 때 호출되는 함수가 ::backdrop
에 hiddenBackdrop
클래스를 적용합니다. 이 클래스는 요소가 최상위 레이어에서 삭제되면 삭제됩니다.
다음 데모 예시의 코드를 확인하세요.
DevTools의 최상위 레이어 지원 설계
최상위 레이어에 대한 DevTools 지원을 통해 개발자는 최상위 레이어의 개념을 이해하고 최상위 레이어 콘텐츠가 변경되는 방식을 시각화할 수 있습니다. 이러한 기능은 개발자가 다음을 식별하는 데 도움이 됩니다.
- 특정 시점의 최상위 레이어에 있는 요소와 순서
- 어느 시점에서든 스택의 맨 위에 있는 요소입니다.
또한 DevTools 최상위 레이어 지원을 사용하면 최상위 레이어 스택에서 배경 시뮬레이션 가상 요소의 위치를 시각화할 수 있습니다. 트리 요소는 아니지만 최상위 레이어의 작동 방식에 중요한 역할을 하며 개발자에게 유용할 수 있습니다.
최상위 레이어 지원 기능을 사용하면 다음 작업을 할 수 있습니다.
- 언제든지 최상위 레이어 스택에 있는 요소를 관찰합니다. 최상위 레이어 표현 스택은 요소가 최상위 레이어에 추가되거나 삭제될 때 동적으로 변경됩니다.
- 최상위 레이어 스택에서 요소 위치를 확인합니다.
- 트리의 최상위 레이어 요소 또는 요소의 배경화면 가상 요소에서 최상위 레이어 표현 컨테이너의 요소 또는 배경화면 가상 요소로 이동했다가 다시 돌아갑니다.
이러한 기능을 사용하는 방법을 알아보겠습니다.
최상위 레이어 컨테이너
최상위 레이어 요소를 시각화하는 데 도움이 되도록 DevTools는 요소 트리에 최상위 레이어 컨테이너를 추가합니다. 닫는 </html>
태그 뒤에 있습니다.
이 컨테이너를 사용하면 언제든지 최상위 레이어 스택의 요소를 관찰할 수 있습니다. 최상위 레이어 컨테이너는 최상위 레이어 요소 및 배경에 대한 링크 목록입니다. 최상위 레이어 표현 스택은 요소가 최상위 레이어에 추가되거나 삭제될 때 동적으로 변경됩니다.
요소 트리 또는 최상위 컨테이너 내에서 최상위 요소를 찾으려면 최상위 컨테이너의 최상위 요소 표현에서 요소 트리의 동일한 요소로 연결되는 링크를 클릭했다가 다시 클릭합니다.
최상위 레이어 컨테이너 요소에서 최상위 레이어 트리 요소로 이동하려면 최상위 레이어 컨테이너의 요소 옆에 있는 표시 버튼을 클릭합니다.
최상위 레이어 트리 요소에서 최상위 레이어 컨테이너의 링크로 이동하려면 요소 옆에 있는 최상위 레이어 배지를 클릭합니다.
최상위 레이어 배지를 포함한 모든 배지를 사용 중지할 수 있습니다. 배지를 사용 중지하려면 배지를 마우스 오른쪽 버튼으로 클릭하고 배지 설정을 선택한 다음 숨기려는 배지 옆의 체크표시를 선택 해제합니다.
최상위 레이어 스택의 요소 순서
최상위 레이어 컨테이너는 스택에 표시되는 요소를 역순으로 보여줍니다. 스택 요소의 상단은 최상위 레이어 컨테이너의 요소 목록에서 마지막에 있습니다. 즉, 최상위 레이어 컨테이너 목록의 마지막 요소가 문서에서 현재 상호작용할 수 있는 요소입니다.
트리 요소 옆에 있는 배지는 요소가 최상위 레이어에 속하는지 여부를 나타내며 스택의 요소 위치 번호를 포함합니다.
이 스크린샷에서 최상위 레이어 스택은 두 요소로 구성되며 두 번째 요소가 스택 상단에 있습니다. 두 번째 요소를 삭제하면 첫 번째 요소가 맨 위로 이동합니다.
최상위 레이어 컨테이너의 배경
위에서 언급한 대로 모든 최상위 레이어 요소에는 배경이라는 CSS 가상 요소가 있습니다. 이 요소의 스타일을 지정할 수 있으므로 이를 검사하고 표현을 확인하는 것도 유용합니다.
요소 트리에서 배경화면 요소는 속한 요소의 닫는 태그 앞에 있습니다. 하지만 최상위 레이어 컨테이너에서는 배경 링크가 속한 최상위 레이어 요소 바로 위에 표시됩니다.
DOM 트리 변경사항
DevTools에서 개별 DOM 트리 요소를 만들고 관리하는 클래스인 ElementsTreeElement
는 최상위 레이어 컨테이너를 구현하기에 충분하지 않았습니다.
최상위 레이어 컨테이너를 트리의 노드로 표시하기 위해 DevTools 트리 요소 노드를 만드는 새 클래스를 추가했습니다. 이전에는 DevTools 요소 트리를 만드는 클래스가 backendNodeId
및 기타 백엔드 관련 속성이 있는 클래스인 DOMNode
로 모든 TreeElement
을 초기화했습니다. backendNodeId
는 백엔드에서 할당됩니다.
최상위 레이어 요소로 연결되는 링크 목록이 있는 최상위 레이어 컨테이너 노드는 일반 트리 요소 노드처럼 작동해야 했습니다. 그러나 이 노드는 '실제' DOM 노드가 아니며 백엔드에서 최상위 레이어 컨테이너 노드를 만들 필요가 없습니다.
최상위 레이어를 나타내는 프런트엔드 노드를 만들기 위해 DOMNode
없이 생성되는 새로운 유형의 프런트엔드 노드를 추가했습니다. 이 최상위 레이어 컨테이너 요소는 DOMNode
가 없는 첫 번째 프런트엔드 노드입니다. 즉, 프런트엔드에만 존재하며 백엔드에서는 이를 '모릅니다'. 다른 노드와 동일한 동작을 하도록 프런트엔드 노드의 동작을 담당하는 UI.TreeOutline.TreeElement
클래스를 확장하는 새 TopLayerContainer
클래스를 만들었습니다.
원하는 게재위치를 달성하기 위해 요소를 렌더링하는 클래스는 TopLayerContainer
를 <html>
태그의 다음 동료 요소로 연결합니다.
새 최상위 레이어 배지는 요소가 최상위 레이어에 있음을 나타내며 TopLayerContainer
요소에서 이 요소의 바로가기에 대한 링크 역할을 합니다.
초기 설계
처음에는 요소에 대한 링크 목록을 만드는 대신 최상위 레이어 요소를 최상위 레이어 컨테이너에 복제할 계획이었습니다. DevTools에서 요소의 하위 요소를 가져오는 방식으로 인해 이 솔루션을 구현하지 않았습니다. 각 요소에는 하위 요소 가져오기에 사용되는 상위 포인터가 있으며 포인터를 여러 개 가질 수는 없습니다. 따라서 트리의 여러 위치에서 모든 하위 요소를 올바르게 펼치고 포함하는 노드를 가질 수 없습니다. 일반적으로 시스템은 중복 하위 트리를 고려하여 빌드되지 않았습니다.
절충안으로 노드를 복제하는 대신 프런트엔드 DOM 노드에 대한 링크를 만들었습니다. DevTools에서 요소로 연결되는 링크를 만드는 클래스는 UI.TreeOutline.TreeElement
를 확장하는 ShortcutTreeElement
입니다. ShortcutTreeElement
는 다른 DevTools DOM 트리 요소와 동일하게 동작하지만 백엔드에 상응하는 노드가 없으며 ElementsTreeElement
에 연결되는 버튼이 있습니다.
최상위 레이어 노드의 각 ShortcutTreeElement
에는 DevTools DOM 트리의 ::backdrop
가상 요소 표현에 연결되는 하위 ShortcutTreeElement
가 있습니다.
초기 설계:
Chrome DevTools 프로토콜 (CDP) 변경사항
최상위 레이어 지원을 구현하려면 Chrome DevTools 프로토콜 (CDP)을 변경해야 합니다. CDP는 DevTools와 Chromium 간의 통신 프로토콜 역할을 합니다.
다음을 추가해야 합니다.
- 언제든지 프런트엔드에서 호출할 수 있는 명령어입니다.
- 백엔드 측에서 프런트엔드에서 트리거할 이벤트입니다.
CDP: DOM.getTopLayerElements
명령어
현재 최상위 레이어 요소를 표시하려면 최상위 레이어에 있는 요소의 노드 ID 목록을 반환하는 새로운 실험용 CDP 명령어가 필요합니다. DevTools가 열리거나 최상위 레이어 요소가 변경될 때마다 DevTools에서 이 명령어를 호출합니다. 명령어는 다음과 같습니다.
# Returns NodeIds of the current top layer elements.
# Top layer renders closest to the user within a viewport, therefore, its elements always
# appear on top of all other content.
experimental command getTopLayerElements
returns
# NodeIds of the top layer elements.
array of NodeId nodeIds
CDP: DOM.topLayerElementsUpdated
이벤트
최상위 레이어 요소의 최신 목록을 가져오려면 실험용 CDP 이벤트를 트리거하기 위해 최상위 레이어 요소의 모든 변경사항이 필요합니다. 이 이벤트는 변경사항을 프런트엔드에 알리고 프런트엔드는 DOM.getTopLayerElements
명령어를 호출하고 새 요소 목록을 수신합니다.
이벤트는 다음과 같습니다.
# Called by the change of the top layer elements.
experimental event topLayerElementsUpdated
CDP 고려사항
최상위 레이어의 CDP 지원을 구현하는 방법에는 여러 가지 옵션이 있었습니다. 고려한 또 다른 옵션은 프런트엔드에 최상위 레이어 요소의 추가 또는 삭제에 관해 알리는 대신 최상위 레이어 요소의 목록을 반환하는 이벤트를 만드는 것이었습니다.
또는 명령어 대신 topLayerElementAdded
및 topLayerElementRemoved
라는 두 가지 이벤트를 만들 수 있습니다. 이 경우 요소를 수신하고 프런트엔드에서 최상위 레이어 요소의 배열을 관리해야 합니다.
현재 프런트엔드 이벤트는 getTopLayerElements
명령어를 호출하여 업데이트된 요소 목록을 가져옵니다. 이벤트가 트리거될 때마다 변경을 일으킨 요소 목록이나 특정 요소를 전송하면 명령어를 호출하는 한 단계를 건너뛸 수 있습니다.
하지만 이 경우 프런트엔드는 푸시되는 요소를 제어할 수 없습니다.
프런트엔드에서 최상위 레이어 노드를 요청할 시기를 결정하는 것이 더 낫다고 생각하여 이렇게 구현했습니다. 예를 들어 UI에서 최상위 레이어가 접히거나 사용자가 요소 트리가 없는 DevTools 패널을 사용하는 경우 트리 깊숙한 곳에 있을 수 있는 추가 노드를 가져올 필요가 없습니다.