如何在纯 javascript 中平滑滚动到元素

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51689653/
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:38:58  来源:igfitidea点击:

How to smoothly scroll to an element in pure javascript

javascriptsmooth-scrolling

提问by hev1

I want to smoothly scroll to an element without using jQuery – just pure javascript. I would like a generic function to be able to both scroll down and scroll up smoothly to a specific position in the document.

我想在不使用 jQuery 的情况下顺利滚动到一个元素——只是纯 javascript。我想要一个通用函数,既能够向下滚动,也能够平滑地向上滚动到文档中的特定位置。

I know I can use the following in jQuery:

我知道我可以在 jQuery 中使用以下内容:

$('html, body').animate({
     scrollTop: $('#myelementid').offset().top
}, 500);

How would I do it with just javascript?

我将如何只用 javascript 做到这一点?

This is what I am trying to do:

这就是我想要做的:

function scrollToHalf(){
  //what do I do?
}
function scrollToSection(){
 //What should I do here?
}
<input type="button" onClick="scrollToHalf()" value="Scroll To 50% of Page">
    <br>
    <input type="button" onClick="scrollToSection()" value="Scroll To Section1">
    <section style="margin-top: 1000px;" id="section1">
      This is a section
</section>

In jquery I would do it like so:

在 jquery 中,我会这样做:

html, body{
  height: 3000px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" onClick="scrollToHalf()" value="Scroll To 50% of Page">
<br>
<input type="button" onClick="scrollToSection()" value="Scroll To Section1">
<section style="margin-top: 1000px;" id="section1">
  This is a section
</section>
<script>
function scrollToHalf(){
  var height = $('body').height();
 $('html, body').animate({
         scrollTop: height/2
    }, 500);
}
function scrollToSection(){
 $('html, body').animate({
         scrollTop: $('#section1').offset().top
    }, 500);
}
</script>

EDIT: I would also like to be able to smooth scroll to a certain position on the page

编辑:我还希望能够平滑滚动到页面上的某个位置

EDIT: CSS solutions are also welcome (although I would prefer javascript solutions)

编辑:也欢迎使用 CSS 解决方案(尽管我更喜欢 javascript 解决方案)

回答by hev1

You can use a forloop with window.scrollToand setTimeoutto scroll smoothly with plain Javascript. To scroll to a specific element, just call the scrollToSmoothlyfunction with the element's offsetTop as the first argument.

您可以使用for循环和window.scrollTosetTimeout使用纯 Javascript 平滑滚动。要滚动到特定元素,只需scrollToSmoothly使用元素的 offsetTop 作为第一个参数调用该函数。

function scrollToSmoothly(pos, time) {
  /*Time is only applicable for scrolling upwards*/
  /*Code written by hev1*/
  /*pos is the y-position to scroll to (in pixels)*/
  if (isNaN(pos)) {
    throw "Position must be a number";
  }
  if (pos < 0) {
    throw "Position can not be negative";
  }
  var currentPos = window.scrollY || window.screenTop;
  if (currentPos < pos) {
    var t = 10;
    for (let i = currentPos; i <= pos; i += 10) {
      t += 10;
      setTimeout(function() {
        window.scrollTo(0, i);
      }, t / 2);
    }
  } else {
    time = time || 2;
    var i = currentPos;
    var x;
    x = setInterval(function() {
      window.scrollTo(0, i);
      i -= 10;
      if (i <= pos) {
        clearInterval(x);
      }
    }, time);
  }
}

Demo:

演示:

<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element<p/>
<button onClick="scrollToSmoothly(Number(0))">Scroll back to top</button>
<p/>
<button onClick="scrollToSmoothly(document.body.offsetHeight)">
Scroll To Bottom
</button>
</div>
<button onClick="scrollToSmoothly(Number(500))">
Scroll to y-position 500px
</button>
<script>
function scrollToSmoothly(pos, time){
/*Time is only applicable for scrolling upwards*/
/*Code written by hev1*/
/*pos is the y-position to scroll to (in pixels)*/
     if(isNaN(pos)){
      throw "Position must be a number";
     }
     if(pos<0){
     throw "Position can not be negative";
     }
    var currentPos = window.scrollY||window.screenTop;
    if(currentPos<pos){
    if(time){
     var x;
      var i = currentPos;
      x = setInterval(function(){
         window.scrollTo(0, i);
         i += 10;
         if(i>=pos){
          clearInterval(x);
         }
     }, time);
    } else {
    var t = 10;
       for(let i = currentPos; i <= pos; i+=10){
       t+=10;
        setTimeout(function(){
       window.scrollTo(0, i);
        }, t/2);
      }
      }
    } else {
    time = time || 2;
       var i = currentPos;
       var x;
      x = setInterval(function(){
         window.scrollTo(0, i);
         i -= 10;
         if(i<=pos){
          clearInterval(x);
         }
     }, time);
      }
}
function scrollToDiv(){
  var elem = document.querySelector("div");
  scrollToSmoothly(elem.offsetTop);
}
</script>

To scroll to a certain position in an exact amount of time, window.requestAnimationFramecan be put to use. JSFiddle WebPage Demo: http://jsfiddle.net/4xwnzgj5/embedded/result

要在精确的时间内滚动到某个位置,window.requestAnimationFrame就可以使用了。JSFiddle网页演示:http: //jsfiddle.net/4xwnzgj5/embedded/result

function scrollToSmoothly(pos, time){
  /*Time is exact amount of time the scrolling will take (in milliseconds)*/
  /*Pos is the y-position to scroll to (in pixels)*/
  /*Code written by hev1*/
  if(typeof pos!== "number"){
  pos = parseFloat(pos);
  }
  if(isNaN(pos)){
   console.warn("Position must be a number or a numeric String.");
   throw "Position must be a number";
  }
  if(pos<0||time<0){
  return;
  }
  var currentPos = window.scrollY || window.screenTop;
    var start = null;
  time = time || 500;
  window.requestAnimationFrame(function step(currentTime){
    start = !start? currentTime: start;
    if(currentPos<pos){
    var progress = currentTime - start;
    window.scrollTo(0, ((pos-currentPos)*progress/time)+currentPos);
    if(progress < time){
        window.requestAnimationFrame(step);
    } else {
        window.scrollTo(0, pos);
    }
    } else {
     var progress = currentTime - start;
    window.scrollTo(0, currentPos-((currentPos-pos)*progress/time));
    if(progress < time){
        window.requestAnimationFrame(step);
    } else {
        window.scrollTo(0, pos);
    }
    }
  });
}

Demo:

演示:

<button onClick="scrollToSmoothly(Number(document.querySelector('div').offsetTop), Number(300))">
Scroll To Div (300ms)
</button>
<button onClick="scrollToSmoothly(Number(document.querySelector('div').offsetTop), Number(200))">
Scroll To Div (200ms)
</button>
<button onClick="scrollToSmoothly(Number(document.querySelector('div').offsetTop), Number(100))">
Scroll To Div (100ms)
</button>
<button onClick="scrollToSmoothly(Number(document.querySelector('div').offsetTop), 50)">
Scroll To Div (50ms)
</button>
<button onClick="scrollToSmoothly(Number(document.querySelector('div').offsetTop), Number(1000))">
Scroll To Div (1000ms)
</button>
<div style="margin: 500px 0px;">
DIV<p/>
<button onClick="scrollToSmoothly(0, 500)">
Back To Top
</button>
<button onClick="scrollToSmoothly(document.body.scrollHeight)">
Scroll To Bottom
</button>
</div>
<div style="margin: 500px 0px;">

</div>
<button style="margin-top: 100px;" onClick="scrollToSmoothly(500, 3000)">
Scroll To y-position 500px (3000ms)
</button>
<script>
function scrollToSmoothly(pos, time){
  /*Time is exact amount of time the scrolling will take (in milliseconds)*/
  /*Pos is the y-position to scroll to (in pixels)*/
  /*Code written by hev1*/
  if(typeof pos!== "number"){
  pos = parseFloat(pos);
  }
  if(isNaN(pos)){
   console.warn("Position must be a number or a numeric String.");
   throw "Position must be a number";
  }
  if(pos<0||time<0){
  return;
  }
  var currentPos = window.scrollY || window.screenTop;
 var start = null;
  time = time || 500;
  window.requestAnimationFrame(function step(currentTime){
   start = !start? currentTime: start;
    if(currentPos<pos){
    var progress = currentTime - start;
    window.scrollTo(0, ((pos-currentPos)*progress/time)+currentPos);
    if(progress < time){
     window.requestAnimationFrame(step);
    } else {
     window.scrollTo(0, pos);
    }
    } else {
     var progress = currentTime - start;
    window.scrollTo(0, currentPos-((currentPos-pos)*progress/time));
    if(progress < time){
     window.requestAnimationFrame(step);
    } else {
     window.scrollTo(0, pos);
    }
    }
  });
}
</script>

Alternatively, you can use window.scrollwhich scrolls to a specific x and y position and window.scrollBywhich scrolls from the current position:

或者,您可以使用window.scroll哪个滚动到特定的 x 和 y 位置,window.scrollBy哪个从当前位置滚动:

// Scroll to specific values
// scrollTo is the same
window.scroll({
  top: 2500, 
  left: 0, 
  behavior: 'smooth' 
});

// Scroll certain amounts from current position 
window.scrollBy({ 
  top: 100, // could be negative value
  left: 0, 
  behavior: 'smooth' 
});

Demo:

演示:

<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
var elem = document.querySelector("div");
window.scroll({
      top: elem.offsetTop, 
      left: 0, 
      behavior: 'smooth' 
});
}
</script>

If you only need to scroll to an element, not a specific position in the document, you can use Element.scrollIntoViewwith behaviorset to smooth.

如果您只需要滚动到一个元素,而不是文档中的特定位置,则可以使用Element.scrollIntoViewwith behaviorset to smooth

document.getElementById("elemID").scrollIntoView({ 
  behavior: 'smooth' 
});

Demo:

演示:

<button onClick="scrollToDiv()">Scroll To Element</button>
<div id="myDiv" style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
    document.getElementById("myDiv").scrollIntoView({ 
      behavior: 'smooth' 
   });
}
</script>

Modern browsers support the scroll-behaviorCSS property, which can be used to make scrolling in the document smooth (without the need for Javascript; anchor tags can be used for this by giving the anchor tag a hrefof #plus the idof the Element to scroll to). You can also set the scroll-behaviorproperty for a specific element like a divto make its contents scroll smoothly.

现代浏览器支持scroll-behaviorCSS 属性,该属性可用于使文档中的滚动平滑(无需 Javascript;可以通过给锚标记 a hrefof#加上id要滚动到的元素来使用锚标记)。您还可以为scroll-behavior特定元素设置属性,例如 adiv以使其内容平滑滚动。

Demo:

演示:

html, body{
  scroll-behavior: smooth;
}
a, a:visited{
  color: initial;
}
<a href="#elem">Scroll To Element</a>
<div id="elem" style="margin: 500px 0px;">Div</div>

The CSS scroll-behaviorproperty works with Javascript as well when using window.scrollTo.

scroll-behavior使用 .css 时,CSS属性也适用于 Javascript window.scrollTo

Demo:

演示:

html, body{
  scroll-behavior: smooth;
}
<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
  var elem = document.querySelector("div");
  window.scrollTo(0, elem.offsetTop);
}
</script>

To check if the scroll-behaviorproperty is supported, you can check if it exists as a key in the style of the HTML element.

要检查该scroll-behavior属性是否受支持,您可以检查它是否作为 HTML 元素样式中的键存在。

var scrollBehaviorSupported = 'scroll-behavior' in document.documentElement.style;
console.log('scroll-behavior supported:',scrollBehaviorSupported);

回答by Kosh

回答by Angel Politis

As I mentioned in my comment, scrollIntoViewis a good option to consider – that gets greater and greater browser support – when you try to scroll to a specified element such as what you are apparently trying to do with your scrollToSectionfunction.

正如我在我的评论中提到的,scrollIntoView当您尝试滚动到指定元素(例如您显然试图用您的scrollToSection函数执行的操作)时,这是一个值得考虑的不错选择——它获得越来越多的浏览器支持。

To scroll to the middle of the page you can set the scrollTopproperty of the bodyand/or the htmlelement to half the difference of the scrollHeightof the body and the innerHeightof the window. Couple the above calculation with requestAnimationFrameand you are set.

要滚动到页面中间,您可以将和/或元素的scrollTop属性设置为主体和窗口的差异的一半。结合上面的计算,你就设置好了。bodyhtmlscrollHeightinnerHeightrequestAnimationFrame

Here's how you can incorporate the above suggestions in your code:

以下是将上述建议合并到代码中的方法:

function scrollToHalf(duration) {
  var
    heightDiff = document.body.scrollHeight - window.innerHeight,
    endValue = heightDiff / 2,
    start = null;
    
  /* Set a default for the duration, in case it's not given. */
  duration = duration || 300;
  
  /* Start the animation. */
  window.requestAnimationFrame(function step (now) {
    /* Normalise the start date and calculate the current progress. */
    start = !start ? now : start;
    var progress = now - start;
    
    /* Increment by a calculate step the value of the scroll top. */
    document.documentElement.scrollTop = endValue * progress / duration;
    document.body.scrollTop = endValue * progress / duration;
    
    /* Check whether the current progress is less than the given duration. */
    if (progress < duration) {
      /* Execute the function recursively. */
      window.requestAnimationFrame(step);
    }
    else {
      /* Set the scroll top to the end value. */
      document.documentElement.scrollTop = endValue;
      document.body.scrollTop = endValue;
    }
  });
}

function scrollToSection(element) {
  /* Scroll until the button's next sibling comes into view. */
  element.nextElementSibling.scrollIntoView({block: "start", behavior: "smooth"});
}
#section1 {
  margin: 1000px 0;
  border: 1px solid red
}
<input type="button" onClick="scrollToHalf()" value="Scroll To 50% of Page">
<br>
<input type="button" onClick="scrollToSection(this)" value="Scroll To Section1">
<section id="section1">
  This is a section
</section>