window.scrollTo 选项在 Microsoft Edge 上不起作用



我有一个奇怪的问题,我只能在Microsoft浏览器上复制(Edge和IE11测试(。

<style>
body {
height: 5000px;
width: 5000px;
}
</style>
<p>Click the button to scroll the document window to 1000 pixels.</p>
<button onclick="scrollWin()">Click me to scroll!</button>
<script>
function scrollWin() {
window.scrollTo({
left: 1000, 
top: 1000,
behavior:"smooth"
});
}
</script>

此代码正确地将窗口向左和向下滚动 1000px,在 Chrome 和 Firefox 中具有平滑的行为。但是,在 Edge 和 IE 上,它根本不移动。

如前所述,滚动行为规范仅在Chrome,Firefox和Opera中实现。

下面是用于检测对ScrollOptionsbehavior属性的支持的单行代码:

const supportsNativeSmoothScroll = 'scrollBehavior' in document.documentElement.style;

下面是跨浏览器平滑滚动的简单实现:https://gist.github.com/eyecatchup/d210786daa23fd57db59634dd231f341

也许不是这个词意义上的真正答案,但我通过使用这个有用的 polyfill 解决了这个问题:https://github.com/iamdustan/smoothscroll 它在所有浏览器上都运行良好。

波利填充的示例页面:http://iamdustan.com/smoothscroll/

非常感谢作者。

您可以使用以下代码片段检测scrollTo中对behavior选项的支持:

function testSupportsSmoothScroll () {
var supports = false
try {
var div = document.createElement('div')
div.scrollTo({
top: 0,
get behavior () {
supports = true
return 'smooth'
}
})
} catch (err) {}
return supports
}

在Chrome,Firefox,Safari和Edge中进行了测试,并且似乎可以正常工作。如果supports为 false,则回退到填充文本。

事实上,他们不支持这个变体,MDN文章应该更新。

填充此方法的一种方法是在requestAnimationFrame动力循环中运行scroll方法。这里没有什么太花哨的。

出现的主要问题是如何检测何时不支持此变体。其实@nlawson的回答完美地解决了这个问题......

为此,我们可以利用这样一个事实,即如果视口确实滚动了,则对Window#scroll的调用将触发ScrollEvent
这意味着我们可以设置一个异步测试,它将:

  1. 事件处理程序附加到ScrollEvent
  2. 首次调用scroll(left , top)变体以确保事件将触发,
  3. 使用选项变体用第二个调用覆盖此调用。
  4. 事件处理程序中,如果我们不在正确的滚动位置,这意味着我们需要附加我们的 polyfill。

因此,此测试的警告是它是一个异步测试。但是由于您需要在调用此方法之前实际等待文档已加载,因此我想在 99% 的情况下都可以。

现在为了减轻主文档的负担,并且由于它已经是一个异步测试,我们甚至可以将此测试包装在 iframe 中,这给了我们类似的东西:

/* Polyfills the Window#scroll(options) & Window#scrollTo(options) */
(function ScrollPolyfill() {
// The asynchronous tester
// wrapped in an iframe (will not work in SO's StackSnippet®)
var iframe = document.createElement('iframe');
iframe.onload = function() {
var win = iframe.contentWindow;
// listen for a scroll event
win.addEventListener('scroll', function handler(e){
// when the scroll event fires, check that we did move
if(win.pageXOffset < 99) { // !== 0 should be enough, but better be safe
attachPolyfill();
}
// cleanup
document.body.removeChild(iframe);      
});
// set up our document so we can scroll
var body = win.document.body;
body.style.width = body.style.height = '1000px';
win.scrollTo(10, 0); // force the event
win.scrollTo({left:100, behavior:'instant'}); // the one we actually test
};
// prepare our frame
iframe.src = "about:blank";
iframe.setAttribute('width', 1);
iframe.setAttribute('height', 1);
iframe.setAttribute('style', 'position:absolute;z-index:-1');
iframe.onerror = function() {
console.error('failed to load the frame, try in jsfiddle');
};
document.body.appendChild(iframe);
// The Polyfill
function attachPolyfill() {
var original = window.scroll, // keep the original method around
animating = false, // will keep our timer's id
dx = 0,
dy = 0,
target = null;
// override our methods
window.scrollTo = window.scroll = function polyfilledScroll(user_opts) {
// if we are already smooth scrolling, we need to stop the previous one
// whatever the current arguments are
if(animating) {
clearAnimationFrame(animating);
}
// not the object syntax, use the default
if(arguments.length === 2) {
return original.apply(this, arguments);
}
if(!user_opts || typeof user_opts !== 'object') {
throw new TypeError("value can't be converted to a dictionnary");
}
// create a clone to not mess the passed object
// and set missing entries
var opts = {
left: ('left' in user_opts) ? user_opts.left : window.pageXOffset,
top:  ('top' in user_opts) ? user_opts.top : window.pageYOffset,
behavior: ('behavior' in user_opts) ? user_opts.behavior : 'auto',
};
if(opts.behavior !== 'instant' && opts.behavior !== 'smooth') {
// parse 'auto' based on CSS computed value of 'smooth-behavior' property
// But note that if the browser doesn't support this variant
// There are good chances it doesn't support the CSS property either...
opts.behavior = window.getComputedStyle(document.scrollingElement || document.body)
.getPropertyValue('scroll-behavior') === 'smooth' ?
'smooth' : 'instant';
}
if(opts.behavior === 'instant') {
// not smooth, just default to the original after parsing the oject
return original.call(this, opts.left, opts.top);
}
// update our direction
dx = (opts.left - window.pageXOffset) || 0;
dy = (opts.top - window.pageYOffset) || 0;
// going nowhere
if(!dx && !dy) {
return;
}
// save passed arguments
target = opts;
// save the rAF id
animating = anim();
};
// the animation loop
function anim() {
var freq = 16 / 300, // whole anim duration is approximately 300ms @60fps
posX, poxY;
if( // we already reached our goal on this axis ?
(dx <= 0 && window.pageXOffset <= +target.left) ||
(dx >= 0 && window.pageXOffset >= +target.left) 
){
posX = +target.left;
}
else {
posX = window.pageXOffset + (dx * freq);
}
if(
(dy <= 0 && window.pageYOffset <= +target.top) ||
(dy >= 0 && window.pageYOffset >= +target.top) 
){
posY = +target.top;
}
else {
posY = window.pageYOffset + (dx * freq);
}
// move to the new position
original.call(window, posX, posY);
// while we are not ok on both axis
if(posX !== +target.left || posY !== +target.top) {
requestAnimationFrame(anim);
}
else {
animating = false;
}
}
}
})();


很抱歉没有直接在答案中提供可运行的演示,但是 StackSnippet® 过度保护的 iframe 不允许我们在 IE 上访问内部 iframe 的内容......
所以相反,这里有一个jsfiddle的链接。


后记:现在我想到,实际上可以通过检查 CSSscroll-behavior支持来同步检查支持,但我不确定它是否真的涵盖了历史上的所有 UA......


Post-Post-scriptum:使用@nlawson的检测,我们现在可以有一个工作片段;-(

/* Polyfills the Window#scroll(options) & Window#scrollTo(options) */
(function ScrollPolyfill() {
// The synchronous tester from @nlawson's answer
var supports = false
test_el = document.createElement('div'),
test_opts = {top:0};
// ES5 style for IE
Object.defineProperty(test_opts, 'behavior', {
get: function() {
supports = true;
}
});
try {
test_el.scrollTo(test_opts);
}catch(e){};

if(!supports) {
attachPolyfill();
}
function attachPolyfill() {
var original = window.scroll, // keep the original method around
animating = false, // will keep our timer's id
dx = 0,
dy = 0,
target = null;
// override our methods
window.scrollTo = window.scroll = function polyfilledScroll(user_opts) {
// if we are already smooth scrolling, we need to stop the previous one
// whatever the current arguments are
if(animating) {
clearAnimationFrame(animating);
}
// not the object syntax, use the default
if(arguments.length === 2) {
return original.apply(this, arguments);
}
if(!user_opts || typeof user_opts !== 'object') {
throw new TypeError("value can't be converted to a dictionnary");
}
// create a clone to not mess the passed object
// and set missing entries
var opts = {
left: ('left' in user_opts) ? user_opts.left : window.pageXOffset,
top:  ('top' in user_opts) ? user_opts.top : window.pageYOffset,
behavior: ('behavior' in user_opts) ? user_opts.behavior : 'auto',
};
if(opts.behavior !== 'instant' && opts.behavior !== 'smooth') {
// parse 'auto' based on CSS computed value of 'smooth-behavior' property
// But note that if the browser doesn't support this variant
// There are good chances it doesn't support the CSS property either...
opts.behavior = window.getComputedStyle(document.scrollingElement || document.body)
.getPropertyValue('scroll-behavior') === 'smooth' ?
'smooth' : 'instant';
}
if(opts.behavior === 'instant') {
// not smooth, just default to the original after parsing the oject
return original.call(this, opts.left, opts.top);
}
// update our direction
dx = (opts.left - window.pageXOffset) || 0;
dy = (opts.top - window.pageYOffset) || 0;
// going nowhere
if(!dx && !dy) {
return;
}
// save passed arguments
target = opts;
// save the rAF id
animating = anim();
};
// the animation loop
function anim() {
var freq = 16 / 300, // whole anim duration is approximately 300ms @60fps
posX, poxY;
if( // we already reached our goal on this axis ?
(dx <= 0 && window.pageXOffset <= +target.left) ||
(dx >= 0 && window.pageXOffset >= +target.left) 
){
posX = +target.left;
}
else {
posX = window.pageXOffset + (dx * freq);
}
if(
(dy <= 0 && window.pageYOffset <= +target.top) ||
(dy >= 0 && window.pageYOffset >= +target.top) 
){
posY = +target.top;
}
else {
posY = window.pageYOffset + (dx * freq);
}
// move to the new position
original.call(window, posX, posY);
// while we are not ok on both axis
if(posX !== +target.left || posY !== +target.top) {
requestAnimationFrame(anim);
}
else {
animating = false;
}
}
}
})();
// OP's code,
// by the time you click the button, the polyfill should already be set up if needed
function scrollWin() {
window.scrollTo({
left: 1000,
top: 1000,
behavior: 'smooth'
});
}
body {
height: 5000px;
width: 5000px;
}
<p>Click the button to scroll the document window to 1000 pixels.</p>
<button onclick="scrollWin()">Click me to scroll!</button>

不幸的是,该方法无法在这两个浏览器中工作。 您可以在此处检查未解决的问题,并查看他们在此问题上未执行任何操作。 https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/15534521/

您可以尝试将 Element.ScrollLeft 和 Element.ScrollTop 属性与 Window.scrollTo(( 一起使用。

下面是使用 Edge 和其他浏览器的示例。

<html>
<style>
body {
height: 5000px;
width: 5000px;
}
</style>
<p>Click the button to scroll the document window to 1000 pixels.</p>
<button onclick="scrollWin(this)">Click me to scroll!</button>
<script>
function scrollWin(pos) {
window.scrollTo(pos.offsetTop+1000,pos.offsetLeft+1000);


}
</script>
</html>

平滑行为不适用于此代码。

参考:

Element.scrollLeft

元素.滚动顶部

问候

迪帕克

"smoothscroll" polyfill 仅支持"smooth"选项。要支持scrollIntoViewOptions中的所有选项,最好使用无缝滚动填充(https://www.npmjs.com/package/seamless-scroll-polyfill(

为我工作。

这是带有解释 https://github.com/Financial-Times/polyfill-library/issues/657 的链接

相关内容

最新更新