window.scrollTo с опциями, не работающими в Microsoft Edge - PullRequest
0 голосов
/ 11 сентября 2018

У меня странная проблема, которую я могу воспроизвести только в браузерах 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 он вообще не перемещается.

Ответы [ 6 ]

0 голосов
/ 18 марта 2019

Как упоминалось ранее, спецификация поведения прокрутки была реализована только в Chrome, Firefox и Opera.

Вот одна строка для определения поддержки свойства behavior в ScrollOptions:

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

А вот простая реализация для кросс-браузерной плавной прокрутки: https://nicegist.github.io/d210786daa23fd57db59634dd231f341

0 голосов
/ 07 декабря 2018

Вы можете определить поддержку опции 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 ложно, вы возвращаетесь к полизаполнению.

0 голосов
/ 12 сентября 2018

Действительно, они не поддерживают этот вариант, статьи MDN должны быть обновлены.

Один из способов заполнить этот метод - запустить метод scroll в requestAnimationFrame приведенном в действие цикле,Ничего особенного в этом нет.

Основная проблема, которая возникает, это как определить, когда этот вариант не поддерживается. на самом деле @ ответ Нлавсона решаетэта проблема совершенно ...

Для этого мы можем использовать тот факт, что вызов Window # scroll вызовет ScrollEvent , если viewPort действительно прокручивал.
Это означает, что мы можем настроить асинхронный тест, который будет:

  1. Присоединить обработчик событий к ScrollEvent ,
  2. Вызовите первый вариант scroll(left , top), чтобы убедиться, что Событие сработает,
  3. Перезапишите этот вызов вторым, используя вариант options .
  4. В обработчике событий , если мы не находимся в правильном положении прокрутки, это означает, что нам нужно присоединить наш polyfill.

Таким образом, предостережение этого теста заключается в том, чтоэто асинхронный тест.Но так как вам нужно фактически дождаться загрузки документа перед вызовом этого метода, я думаю, что в 99% случаев все будет в порядке.

Теперь, чтобы уменьшить нагрузку на основной документ, и так как он уже асинхронныйtest, мы можем даже обернуть этот тест в 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®чрезмерно защищенные iframes не позволяют нам получать доступ к содержимому внутреннего iframe в IE ...
Итак, вместо этого здесь есть ссылка на jsfiddle .


Постскриптум: Теперь мне приходит в голову, что на самом деле можно было бы проверить синхронную проверку поддержки, проверяя поддержку 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>
0 голосов
/ 12 сентября 2018

Вы можете попробовать использовать свойства 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 голосов
/ 11 сентября 2018

Возможно, это не верный ответ в смысле этого слова, но я решил эту проблему с помощью этого полезного полифилла: https://github.com/iamdustan/smoothscroll, который действительно хорошо работает во всех браузерах.

Пример страницы для pollyfill: http://iamdustan.com/smoothscroll/

Огромное спасибо автору.

0 голосов
/ 11 сентября 2018

К сожалению, этот метод не может работать в этих двух браузерах.Вы можете проверить открытые проблемы здесь и увидеть, что они ничего не сделали по этому вопросу.https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/15534521/

...