最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Prevent Safari from automatically scrolling to focused element (scrollback technique stopped working in Safari 12)

programmeradmin4浏览0评论

Safari for desktop automatically scrolls the page to any input element that I focus from javascript. This behavior can be seen right here:

I have found no way to prevent this automatic scrolling. However, there is a known workaround - save the screen position first and scroll back to that position after focusing:

var el = document.getElementById("editable");
var x = window.scrollX, y = window.scrollY; // save position
el.focus();
// manipulate selection inside the focused element with `document.createRange()`
// and do other stuff
window.scrollTo(x, y); // restore position

This workaround used to work fine in Safari 10 and stopped working in Safari 12. Calling scrollTo after focusing doesn't do anything anymore. However, if scrollTo is executed with a delay (even a really short one), everything works:

var el = document.getElementById("editable");
var x = window.scrollX, y = window.scrollY; // save position
el.focus();
// manipulate selection inside the focused element with `document.createRange()`
// and do other stuff
setTimeout(function() {
    window.scrollTo(x, y); // restore position
}, 1);

But with this 1-millisecond delay one can see the page first scrolls to the input field and then very quickly back to the original position, so the new workaround is far from perfect.

Is there any way to gracefully prevent desktop Safari from scrolling the page to focused element automatically or at least a good workaround to mitigate that behavior?

Safari for desktop automatically scrolls the page to any input element that I focus from javascript. This behavior can be seen right here:

https://codepen.io/anon/pen/JmKPwZ

I have found no way to prevent this automatic scrolling. However, there is a known workaround - save the screen position first and scroll back to that position after focusing:

var el = document.getElementById("editable");
var x = window.scrollX, y = window.scrollY; // save position
el.focus();
// manipulate selection inside the focused element with `document.createRange()`
// and do other stuff
window.scrollTo(x, y); // restore position

This workaround used to work fine in Safari 10 and stopped working in Safari 12. Calling scrollTo after focusing doesn't do anything anymore. However, if scrollTo is executed with a delay (even a really short one), everything works:

var el = document.getElementById("editable");
var x = window.scrollX, y = window.scrollY; // save position
el.focus();
// manipulate selection inside the focused element with `document.createRange()`
// and do other stuff
setTimeout(function() {
    window.scrollTo(x, y); // restore position
}, 1);

But with this 1-millisecond delay one can see the page first scrolls to the input field and then very quickly back to the original position, so the new workaround is far from perfect.

Is there any way to gracefully prevent desktop Safari from scrolling the page to focused element automatically or at least a good workaround to mitigate that behavior?

Share Improve this question asked Oct 4, 2018 at 17:33 BeowulfenatorBeowulfenator 2,30017 silver badges26 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 3

After much poking around I've stumbled upon a solution that works:

var el = document.getElementById("editable");
var scrollTop = document.body.scrollTop; // save position
el.focus();
// manipulate selection inside the focused element with `document.createRange()`
// and do other stuff
document.body.scrollTop = scrollTop;

For some reason saving document.body.scrollTop works, while saving window.scrollX and window.scrollY doesn't.

I just had the same bug and was able to get my version working with:

setTimeout(function() {
    window.scrollTo(x, y);
}, 0);

I think maybe that in Safari 12 (for some reason) the focus method in the HTMLElement Web API takes longer to run than the scrollTo method in the Window Web API. If this is the case, then there ends up being a race condition between the pletion of the focus method and the scrollTo method, resulting in the scroll position staying at the focused element.

Using setTimeout with a delay of 0ms essentially just says "Run this once everything in the call stack is done executing," which seems to fix the issue for me.

Here's a video I used as a resource when debugging: https://www.youtube./watch?v=8aGhZQkoFbQ

Hope this helps!

you can use the preventScroll options to do this.

current?.focus({
    preventScroll: true,
});

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论