@@ -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