fix(@vben/layouts): scope hash targets to layout

This commit is contained in:
Dream
2026-07-24 17:32:51 +08:00
parent b9c6d56f74
commit 6d5ccc7ac8
2 changed files with 30 additions and 5 deletions

View File

@@ -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();

View File

@@ -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<HTMLElement>(
return scrollElement.querySelector<HTMLElement>(
`#${CSS.escape(decodeURIComponent(id))}`,
);
} catch {
return document.querySelector<HTMLElement>(`#${CSS.escape(id)}`);
return scrollElement.querySelector<HTMLElement>(`#${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 {