From 6d5ccc7ac81f85e86fd42184111fa76b299f0bf0 Mon Sep 17 00:00:00 2001 From: Dream <1012377328@qq.com> Date: Fri, 24 Jul 2026 17:32:51 +0800 Subject: [PATCH] fix(@vben/layouts): scope hash targets to layout --- .../basic/__tests__/use-layout-scroll.test.ts | 25 +++++++++++++++++++ .../layouts/src/basic/use-layout-scroll.ts | 10 ++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/effects/layouts/src/basic/__tests__/use-layout-scroll.test.ts b/packages/effects/layouts/src/basic/__tests__/use-layout-scroll.test.ts index f4a12e3e..fdee67ee 100644 --- a/packages/effects/layouts/src/basic/__tests__/use-layout-scroll.test.ts +++ b/packages/effects/layouts/src/basic/__tests__/use-layout-scroll.test.ts @@ -116,6 +116,31 @@ describe('useLayoutScroll', () => { expect(element.scrollTo).not.toHaveBeenCalled(); }); + it('should ignore matching hash targets outside the layout', async () => { + window.history.replaceState({ position: 0 }, ''); + const hostTarget = document.createElement('div'); + hostTarget.id = 'section'; + hostTarget.scrollIntoView = vi.fn(); + document.body.append(hostTarget); + const element = createScrollElement(); + const layoutTarget = document.createElement('div'); + layoutTarget.id = 'section'; + layoutTarget.scrollIntoView = vi.fn(); + element.append(layoutTarget); + const routerMock = createRouterMock(); + mountLayoutScroll(routerMock.router); + const { afterHook } = routerMock.getHooks(); + + window.history.replaceState({ position: 1 }, ''); + await runAfterHook(afterHook, '#section'); + + expect(layoutTarget.scrollIntoView).toHaveBeenCalledWith({ + behavior: 'smooth', + block: 'start', + }); + expect(hostTarget.scrollIntoView).not.toHaveBeenCalled(); + }); + it('should restore a saved position on history navigation', async () => { window.history.replaceState({ position: 0 }, ''); const element = createScrollElement(); diff --git a/packages/effects/layouts/src/basic/use-layout-scroll.ts b/packages/effects/layouts/src/basic/use-layout-scroll.ts index d4ee372c..9aa89aca 100644 --- a/packages/effects/layouts/src/basic/use-layout-scroll.ts +++ b/packages/effects/layouts/src/basic/use-layout-scroll.ts @@ -16,18 +16,18 @@ function getHistoryPosition() { return typeof position === 'number' ? position : undefined; } -function getHashTarget(hash: string) { - if (typeof document === 'undefined' || !hash.startsWith('#')) { +function getHashTarget(scrollElement: HTMLElement, hash: string) { + if (!hash.startsWith('#')) { return null; } const id = hash.slice(1); try { - return document.querySelector( + return scrollElement.querySelector( `#${CSS.escape(decodeURIComponent(id))}`, ); } catch { - return document.querySelector(`#${CSS.escape(id)}`); + return scrollElement.querySelector(`#${CSS.escape(id)}`); } } @@ -62,7 +62,7 @@ export function useLayoutScroll(router: LayoutScrollRouter = useRouter()) { : undefined; if (savedPosition === undefined) { - const hashTarget = getHashTarget(to.hash); + const hashTarget = getHashTarget(scrollElement, to.hash); if (hashTarget) { hashTarget.scrollIntoView({ behavior: 'smooth', block: 'start' }); } else {