diff --git a/packages/@headlessui-react/src/components/dialog/dialog.tsx b/packages/@headlessui-react/src/components/dialog/dialog.tsx
index a7c0e40c0d..cf321d788b 100644
--- a/packages/@headlessui-react/src/components/dialog/dialog.tsx
+++ b/packages/@headlessui-react/src/components/dialog/dialog.tsx
@@ -101,6 +101,7 @@ function useScrollLock(
if (!ownerDocument) return
let d = disposables()
+ let scrollPosition = window.pageYOffset
function style(node: HTMLElement, property: string, value: string) {
let previous = node.style.getPropertyValue(property)
@@ -123,10 +124,36 @@ function useScrollLock(
}
if (isIOS()) {
- let scrollPosition = window.pageYOffset
- style(document.body, 'marginTop', `-${scrollPosition}px`)
+ style(ownerDocument.body, 'marginTop', `-${scrollPosition}px`)
window.scrollTo(0, 0)
+ // Relatively hacky, but if you click a link like `` in the Dialog, and there
+ // exists an element on the page (outside of the Dialog) with that id, then the browser will
+ // scroll to that position. However, this is not the case if the element we want to scroll to
+ // is higher and the browser needs to scroll up, but it doesn't do that.
+ //
+ // Let's try and capture that element and store it, so that we can later scroll to it once the
+ // Dialog closes.
+ let scrollToElement: HTMLElement | null = null
+ d.addEventListener(
+ ownerDocument,
+ 'click',
+ (e) => {
+ if (e.target instanceof HTMLElement) {
+ try {
+ let anchor = e.target.closest('a')
+ if (!anchor) return
+ let { hash } = new URL(anchor.href)
+ let el = ownerDocument.querySelector(hash)
+ if (el && !resolveAllowedContainers().some((container) => container.contains(el))) {
+ scrollToElement = el as HTMLElement
+ }
+ } catch (err) {}
+ }
+ },
+ true
+ )
+
d.addEventListener(
ownerDocument,
'touchmove',
@@ -161,6 +188,13 @@ function useScrollLock(
// (Since the value of window.pageYOffset is 0 in the first case, we should be able to
// always sum these values)
window.scrollTo(0, window.pageYOffset + scrollPosition)
+
+ // If we captured an element that should be scrolled to, then we can try to do that if the
+ // element is still connected (aka, still in the DOM).
+ if (scrollToElement && scrollToElement.isConnected) {
+ scrollToElement.scrollIntoView({ block: 'nearest' })
+ scrollToElement = null
+ }
})
}
diff --git a/packages/@headlessui-vue/src/components/dialog/dialog.ts b/packages/@headlessui-vue/src/components/dialog/dialog.ts
index fcc924dfc3..fec5d4ff6f 100644
--- a/packages/@headlessui-vue/src/components/dialog/dialog.ts
+++ b/packages/@headlessui-vue/src/components/dialog/dialog.ts
@@ -227,6 +227,7 @@ export let Dialog = defineComponent({
if (!owner) return
let d = disposables()
+ let scrollPosition = window.pageYOffset
function style(node: HTMLElement, property: string, value: string) {
let previous = node.style.getPropertyValue(property)
@@ -249,10 +250,36 @@ export let Dialog = defineComponent({
}
if (isIOS()) {
- let scrollPosition = window.pageYOffset
- style(document.body, 'marginTop', `-${scrollPosition}px`)
+ style(owner.body, 'marginTop', `-${scrollPosition}px`)
window.scrollTo(0, 0)
+ // Relatively hacky, but if you click a link like `` in the Dialog, and there
+ // exists an element on the page (outside of the Dialog) with that id, then the browser will
+ // scroll to that position. However, this is not the case if the element we want to scroll to
+ // is higher and the browser needs to scroll up, but it doesn't do that.
+ //
+ // Let's try and capture that element and store it, so that we can later scroll to it once the
+ // Dialog closes.
+ let scrollToElement: HTMLElement | null = null
+ d.addEventListener(
+ owner,
+ 'click',
+ (e) => {
+ if (e.target instanceof HTMLElement) {
+ try {
+ let anchor = e.target.closest('a')
+ if (!anchor) return
+ let { hash } = new URL(anchor.href)
+ let el = owner!.querySelector(hash)
+ if (el && !resolveAllowedContainers().some((container) => container.contains(el))) {
+ scrollToElement = el as HTMLElement
+ }
+ } catch (err) {}
+ }
+ },
+ true
+ )
+
d.addEventListener(
owner,
'touchmove',
@@ -288,6 +315,13 @@ export let Dialog = defineComponent({
// (Since the value of window.pageYOffset is 0 in the first case, we should be able to
// always sum these values)
window.scrollTo(0, window.pageYOffset + scrollPosition)
+
+ // If we captured an element that should be scrolled to, then we can try to do that if the
+ // element is still connected (aka, still in the DOM).
+ if (scrollToElement && scrollToElement.isConnected) {
+ scrollToElement.scrollIntoView({ block: 'nearest' })
+ scrollToElement = null
+ }
})
}