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 badges3 Answers
Reset to default 3After 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,
});