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

IT技术 javascript smooth-scrolling
2021-02-10 15:53:33

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

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

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

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

这就是我想要做的:

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>

在 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>

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

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

6个回答

要在精确的时间内滚动到某个位置,window.requestAnimationFrame可以使用,每次计算适当的当前位置。不支持setTimeout时可用于类似效果requestAnimationFrame

/*
   @param pos: the y-position to scroll to (in pixels)
   @param time: the exact amount of time the scrolling will take (in milliseconds)
*/
function scrollToSmoothly(pos, time) {
    var currentPos = window.pageYOffset;
    var start = null;
    if(time == null) time = 500;
    pos = +pos, time = +time;
    window.requestAnimationFrame(function step(currentTime) {
        start = !start ? currentTime : start;
        var progress = currentTime - start;
        if (currentPos < pos) {
            window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
        } else {
            window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
        }
        if (progress < time) {
            window.requestAnimationFrame(step);
        } else {
            window.scrollTo(0, pos);
        }
    });
}

演示:

对于更复杂的情况,可以使用SmoothScroll.js 库,它处理垂直和水平平滑滚动、在其他容器元素内部滚动、不同的缓动行为、从当前位置相对滚动等等。

或者,您可以传递一个选项对象,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' 
});

演示:

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

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

演示:

现代浏览器支持scroll-behaviorCSS 属性,可用于使文档滚动顺畅(无需 JavaScript)。锚标记可以通过给锚标签可以用于此href#id的元素滚动到)。您还可以为scroll-behavior特定容器设置属性,例如 adiv以使其内容平滑滚动。

演示:

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

演示:

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

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

@Baldráni 谢谢!乐意效劳!
2021-03-24 15:53:33
真的很好的答案!特别喜欢我不知道的 scrollBy :)
2021-04-02 15:53:33
@SnowBases 看我的回答。
2021-03-17 15:53:33
这在带有对象参数的 Safari 浏览器中不起作用,还有其他选择吗?
2021-03-23 15:53:33

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

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

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

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>

我刚刚修复了它@org.package.example。看看这个!
2021-04-07 15:53:33

您可以使用简单的 polyfill 滚动您想要的任何节点对象,如下所示:

Node.prototype.scroll = window.scroll

它将为您提供对滚动对象的相同访问,但是对于任何 DOM 元素,您可以像这样使用它:

document.querySelector('.scrollable-div').scroll({
  top: 500, 
  left: 0, 
  behavior: 'smooth' 
});

这个问题已经有很多答案了,但我想我可能会分享我使用的东西。

以下允许您在指定的时间内向下或向上平滑滚动到页面上的任何位置。我不确定它是否与所有浏览器兼容,但我很确定它是。(如果我错了,有人纠正我。)

重要编辑:确保您html {scroll-behavior: smooth;}的 CSS 中没有。否则,这将不起作用。

function scrollToInTime(element, duration) {
  const endPoint = document.querySelector(element).offsetTop,
    distance = endPoint - window.pageYOffset,
    rate = (distance * 4) / duration, // px/4ms
    interval = setInterval(scrollIncrement, 4) //4ms is minimum interval for browser

  function scrollIncrement() {
    const yOffset = Math.ceil(window.pageYOffset)

    if (
      (yOffset >= endPoint && rate >= 0) ||
      (yOffset <= endPoint && rate <= 0)
    ) {
      clearInterval(interval)
    } else {
      //keep in mind that scrollBy doesn't work with decimal pixels < 1 like 0.4px, so
      //if duration is too big, function won't work. rate must end up being >= 1px
      window.scrollBy(0, rate)
    }
  }
}

以 codepen 为例:https ://codepen.io/isaac-svi/pen/xxZgPZp?editors = 0110