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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/52276194/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 09:53:59  来源:igfitidea点击:

window.scrollTo with options not working on Microsoft Edge

javascriptmicrosoft-edge

提问by CDK

I have a strange issue which I can only replicate on Microsoft browsers (Edge and IE11 tested).

我有一个奇怪的问题,我只能在 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>

This code correctly scrolls the window 1000px to the left and down, with a smooth behaviour in Chrome and Firefox. However, on Edge and IE, it does not move at all.

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

回答by eyecatchUp

As mentioned before, the Scroll Behavior specificationhas only been implemented in Chrome, Firefox and Opera.

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

Here's a one-liner to detect support for the behaviorproperty in ScrollOptions:

这是检测对 中的behavior属性的支持的单行代码ScrollOptions

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

And here's a simple implementation for cross-browser smooth scrolling: https://nicegist.github.io/d210786daa23fd57db59634dd231f341

这是跨浏览器平滑滚动的简单实现:https: //nicegist.github.io/d210786daa23fd57db59634dd231f341

回答by CDK

Maybe not a true answer in the sense of the word, but I have solved this problem by using this helpful polyfill: https://github.com/iamdustan/smoothscrollwhich works really well across all browsers.

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

Example page for pollyfill: http://iamdustan.com/smoothscroll/

pollyfill 的示例页面:http://iamdustan.com/smoothscroll/

Many thanks to the author.

非常感谢作者。

回答by Kaiido

Indeed, they don't support this variant, MDN articles should be updated.

确实,他们不支持这种变体,应该更新 MDN 文章。

One way to polyfill this method is to run the scrollmethod in a requestAnimationFramepowered loop. Nothing too fancy here.

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

The main problem that arises is how to detect when this variant is not supported.actually @nlawson's answertackles this problem perfectly...

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

For this, we can use the fact that a call to Window#scrollwill fire a ScrollEventif the viewPort actually did scroll.
This means we can set up an asynchronous test that will:

为此,我们可以使用以下事实:如果 viewPort 确实滚动,则调用Window#scroll将触发ScrollEvent
这意味着我们可以设置一个异步测试,它将:

  1. Attach an event handlerto the ScrollEvent,
  2. Call a first time scroll(left , top)variant to be sure the Eventwill fire,
  3. Overwrite this call with a second one using the optionsvariant.
  4. In the event handler, if we aren't at the correct scroll position, this means we need to attach our polyfill.
  1. 事件处理程序附加到ScrollEvent
  2. 调用第一个时间scroll(left , top)变量以确保事件会触发,
  3. 使用选项变体用第二个调用覆盖此调用。
  4. 事件处理程序中,如果我们不在正确的滚动位置,这意味着我们需要附加我们的 polyfill。

So the caveat of this test is that it is an asynchronous test. But since you need to actually wait for the document has loaded before calling this method, I guess in 99% of cases it will be ok.

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

Now to less burden the main doc, and since it is already an asynchronous test, we can even wrap this test inside an iframe, which gives us something like:

现在为了减轻主文档的负担,并且由于它已经是一个异步测试,我们甚至可以将此测试包装在 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;
      }
    }
  }
})();




Sorry for not providing a runable demo inside the answer directly, but StackSnippet?'s over-protected iframes don't allow us to access the content of an inner iframe on IE...
So instead, here is a link to a jsfiddle.

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



Post-scriptum:Now comes to my mind that it might actually be possible to check for support in a synchronous way by checking for the CSS scroll-behaviorsupport, but I'm not sure it really covers all UAs in the history...

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



Post-Post-scriptum:Using @nlawson's detection we can now have a working snippet ;-)

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>

回答by nlawson

You can detect support for the behavioroption in scrollTousing this snippet:

您可以使用此代码段检测对该behavior选项的支持scrollTo

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
}

Tested in Chrome, Firefox, Safari, and Edge, and seems to work correctly. If supportsis false, you fall back to a polyfill.

在 Chrome、Firefox、Safari 和 Edge 中测试,似乎可以正常工作。如果supports为 false,则返回到 polyfill。

回答by Lazar Nikolic

Unfortunately, there is no way for that method to work in these two browsers. You can check open issues here and see that they have done nothing on this issue. https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/15534521/

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

回答by Deepak-MSFT

You can try to use Element.ScrollLeft and Element.ScrollTop property with Window.scrollTo().

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

Below is the example which is working with Edge and other browsers.

下面是使用 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>

Smooth behavior is not working with this code.

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

Reference:

参考:

Element.scrollLeft

元素.scrollLeft

Element.scrollTop

元素.scrollTop

Regards

问候

Deepak

迪帕克