web-dev-qa-db-ja.com

Microsoft Edgeでオプションが機能しないwindow.scrollTo

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>

このコードは、ChromeおよびFirefoxではスムーズな動作で、ウィンドウを1000px左下に正しくスクロールします。ただし、EdgeおよびIEでは、まったく移動しません。

9
CDK

Wordの意味では本当の答えではないかもしれませんが、この便利なポリフィルを使用してこの問題を解決しました: https://github.com/iamdustan/smoothscroll これは、すべてのブラウザーで非常にうまく機能します。

Pollyfillのサンプルページ: http://iamdustan.com/smoothscroll/

著者に感謝します。

10
CDK

前述のように、 Scroll Behavior仕様 はChrome、Firefox、Operaにのみ実装されています。

behaviorScrollOptionsプロパティのサポートを検出するワンライナーは次のとおりです。

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

そして、ここにクロスブラウザのスムーズなスクロールのための簡単な実装があります: https://nicegist.github.io/d210786daa23fd57db59634dd231f341

11
eyecatchUp

次のスニペットを使用すると、behaviorscrollToオプションのサポートを検出できます。

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の場合、ポリフィルにフォールバックします。

3
nlawson

実際、彼らはこのバリアントをサポートしていません。MDN記事を更新する必要があります。

このメソッドをポリフィルする1つの方法は、scrollメソッドをrequestAnimationFrameパワードループで実行することです。ここでは派手すぎません。

発生する主な問題は、このバリアントがサポートされていない場合の検出方法です。 実際 @ nlawsonの答え この問題に完璧に取り組みます...

これには、viewPortが実際にスクロールした場合、Window#scrollを呼び出すとScrollEventが発生するという事実を利用できます。
つまり、次のような非同期テストを設定できます。

  1. イベントハンドラーScrollEventにアタッチし、
  2. 初めてscroll(left , top)バリアントを呼び出して、Eventが確実に発生するようにします。
  3. optionsバリアントを使用して、この呼び出しを2番目の呼び出しで上書きします。
  4. イベントハンドラーで、正しいスクロール位置にいない場合、これはポリフィルをアタッチする必要があることを意味します。

したがって、このテストの注意点は、非同期テストであることです。しかし、このメソッドを呼び出す前に、ドキュメントが読み込まれるのを実際に待つ必要があるため、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へのリンクがあります


Post-scriptum:これで、CSSをチェックすることにより、同期の方法でサポートを実際にチェックできる可能性があるということが頭に浮かびますscroll-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>
2
Kaiido

残念ながら、これらの2つのブラウザでそのメソッドを機能させる方法はありません。ここで未解決の問題をチェックして、彼らがこの問題に対して何もしていないことを確認できます。 https://developer.Microsoft.com/en-us/Microsoft-Edge/platform/issues/15534521/

0
Lazar Nikolic

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

よろしく

ディーパック

0
Deepak-MSFT