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

时间:2018-09-11 12:40:56

标签: javascript 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上,它根本不会移动。

7 个答案:

答案 0 :(得分:4)

从词义上讲可能不是一个正确的答案,但是我已经通过使用这个有用的polyfill解决了这个问题:https://github.com/iamdustan/smoothscroll在所有浏览器中都非常有效。

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

非常感谢作者。

答案 1 :(得分:3)

如前所述,Scroll Behavior specification仅在Chrome,Firefox和Opera中实现。

这里是检测behavior中的ScrollOptions属性支持的单线:

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

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

答案 2 :(得分:2)

实际上,他们不支持此变体,应更新MDN文章。

这种方法的一种填充方法是在 requestAnimationFrame 动力循环中运行scroll方法。这里没什么好看的。

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

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

  1. 事件处理程序附加到 ScrollEvent
  2. 调用第一次scroll(left , top)变体以确保 Event 将触发,
  3. 使用 options 变体用第二个覆盖此调用。
  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的链接。


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


后期脚本: 使用@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>

答案 3 :(得分:2)

您可以使用以下代码段来检测对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
}

在Chrome,Firefox,Safari和Edge中进行了测试,并且似乎可以正常工作。如果supports为假,则返回到polyfill。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

您可以尝试将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

Element.scrollTop

致谢

Deepak

答案 6 :(得分:0)

“平滑滚动” polyfill仅支持“平滑”选项。要支持scrollIntoViewOptions中的所有选项,最好使用 seamless-scroll-polyfill https://www.npmjs.com/package/seamless-scroll-polyfill

为我工作。

以下是带有说明https://github.com/Financial-Times/polyfill-library/issues/657的链接