From a209bb1d80f71f3e2794730a1d5f0ff6b372245e Mon Sep 17 00:00:00 2001 From: AWESOMEDEV Date: Thu, 23 Jul 2026 12:44:26 +0900 Subject: [PATCH] =?UTF-8?q?fix(docs):=20Shadow=20DOM=20=EB=AA=A9=EC=B0=A8?= =?UTF-8?q?=20=EC=95=B5=EC=BB=A4=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 브라우저의 #프래그먼트 이동은 Shadow DOM 안의 id를 못 찾아 목차 순번 클릭이 안 먹혔다. 클릭을 가로채 그림자 안에서 대상을 찾아 scrollIntoView. --- frontend/src/pages/DocViewerPage.jsx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/frontend/src/pages/DocViewerPage.jsx b/frontend/src/pages/DocViewerPage.jsx index 291db63..2c0ff03 100644 --- a/frontend/src/pages/DocViewerPage.jsx +++ b/frontend/src/pages/DocViewerPage.jsx @@ -28,6 +28,28 @@ function DocShadow({ html }) { if (!host) return; const shadow = host.shadowRoot || host.attachShadow({ mode: 'open' }); shadow.innerHTML = html; + + // ⚠️ Shadow DOM 안의 목차 앵커()는 브라우저 기본 스크롤이 안 먹는다. + // 브라우저의 #프래그먼트 이동은 light DOM의 id만 찾고, 그림자 트리 안은 못 본다. + // 그래서 클릭을 직접 가로채, 그림자 안에서 대상을 찾아 스크롤한다. + // (스크롤은 가장 가까운 스크롤 조상 .doc-immersive-scroll이 담당한다.) + const onClick = (e) => { + const a = e.composedPath().find( + (el) => el.tagName === 'A' && el.getAttribute?.('href')?.startsWith('#'), + ); + if (!a) return; + const id = decodeURIComponent(a.getAttribute('href').slice(1)); + if (!id) return; + const target = + shadow.querySelector(`#${CSS.escape(id)}`) || + shadow.querySelector(`[name="${CSS.escape(id)}"]`); + if (target) { + e.preventDefault(); + target.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }; + shadow.addEventListener('click', onClick); + return () => shadow.removeEventListener('click', onClick); }, [html]); return
; }