Skip to content

Commit 0e89b82

Browse files
authored
👌 Improve scrollToContent (#8)
1 parent 895c5ee commit 0e89b82

File tree

1 file changed

+52
-24
lines changed

1 file changed

+52
-24
lines changed

src/sphinx_peek/assets/sphinx_peek.js

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,13 @@ function setPreviewPosition(preview, anchor, config) {
184184

185185
/**
186186
* Activated on iframe load.
187-
* It ensures that the iframe is scrolled to the target link position,
188-
* since sometimes the browser does not handle this properly automatically.
189-
* Note, this can be blocked by CORS (obtaining the iframe content),
190-
* if the iframe is not on the same domain, or the page is not being served.
187+
* It ensures that the iframe is scrolled to the target position,
188+
* if the src URL contains a target, e.g. `index.html#target`,
189+
* since sometimes the browser does not handle this automatically
190+
* (it seems to work in Firefox, not in Chrome, and Safari scrolls but to the wrong location!).
191+
*
192+
* Note, this can be blocked by CORS (obtaining the iframe.contentDocument),
193+
* if the iframe.src is not from the same origin, or the page is not being served.
191194
*
192195
* @param {HTMLIFrameElement} iframe - The iframe that was loaded.
193196
*/
@@ -199,34 +202,59 @@ function scrollToContent(iframe) {
199202
);
200203
return;
201204
}
202-
var targetId = target.split("#")[1];
203-
if (targetId !== undefined) {
205+
let targetIndex = target.indexOf("#");
206+
var targetId = target.substring(targetIndex + 1);
207+
if (targetIndex !== -1 && targetId !== "") {
204208
setTimeout(function () {
209+
// try to get iframe contentDocument (this is the same as iframe.contentWindow.document)
210+
// if we cannot access the iframe content, we cannot scroll to the target
211+
// this can happen if the iframe is not on the same domain, or the page is not being served, due to CORS
212+
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/contentDocument
205213
try {
206-
let contentWindow = iframe.contentWindow;
207-
if (contentWindow === null) {
208-
console.warn(
209-
`Sphinx Peek: Cannot access iframe content window for '${target}'`,
210-
);
211-
return;
212-
}
213-
// try to get the target element by id
214-
var element = contentWindow.document.getElementById(targetId);
214+
var contentDocument = iframe.contentDocument;
215215
} catch (e) {
216-
// if we cannot access the iframe content, we cannot scroll to the target
217216
console.warn(
218-
`Sphinx Peek: Cannot access iframe content for '${target}': ${e}`,
217+
`Sphinx Peek: Cannot access iframe contentDocument for '${target}': ${e}`,
219218
);
220219
return;
221220
}
222-
if (element) {
223-
// see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#behavior
224-
element.scrollIntoView({ behavior: "instant" });
225-
} else {
226-
console.warn(`Sphinx Peek: Target does not exist: ${target}`);
227-
// if target link doesn't exist, scroll to the top of the page
228-
// iframe.contentWindow.scrollTo(0, 0);
221+
if (contentDocument === null) {
222+
console.warn(
223+
`Sphinx Peek: Cannot access iframe contentDocument for '${target}'`,
224+
);
225+
return;
229226
}
227+
228+
// try to get the target element by id
229+
var element = contentDocument.getElementById(targetId);
230+
if (element === null) {
231+
console.warn(
232+
`Sphinx Peek: Anchor does not exist in iframe contentDocument: ${target}`,
233+
);
234+
return;
235+
}
236+
237+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#behavior
238+
// Note this seems to have some differing behavior between browsers:
239+
// - Firefox (OSX arm64, 121.0): works as expected
240+
// - Chrome (OSX arm64, 120.0.6099.216):
241+
// - does not appear to respect the behavior "instant", i.e. the scrolling is visible
242+
// - Safari (OSX arm64, 17.2.1):
243+
// - it can sometimes scroll the parent page as well to bring the element to the top of parent page
244+
let scrollTop = window.document.documentElement.scrollTop;
245+
element.scrollIntoView({ behavior: "instant" });
246+
if (window.document.documentElement.scrollTop !== scrollTop) {
247+
// "fix" the Safari behavior by restoring the original scroll position
248+
// TODO this should also be done for all window parents, i.e. when opening nested iframes
249+
window.document.documentElement.scrollTo({
250+
top: scrollTop,
251+
behavior: "instant",
252+
});
253+
}
254+
255+
// Note this was another solution proposed, but it does not seem to work
256+
// https://stackoverflow.com/questions/20956663/scrollintoview-to-apply-to-iframe-only/20958953#20958953
257+
// contentDocument.documentElement.scrollTop = element.offsetTop;
230258
}, 500);
231259
}
232260
}

0 commit comments

Comments
 (0)