如何在纯JavaScript中顺利滚动到元素

时间:2018-08-04 21:23:00

标签: javascript smooth-scrolling

我想不使用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 个答案:

答案 0 :(得分:14)

您可以对forwindow.scrollTo使用setTimeout循环,以使用纯Javascript平滑滚动。要滚动到特定元素,只需使用元素的offsetTop作为第一个参数调用scrollToSmoothly函数即可。

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);
  }
}

演示:

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

要在确切的时间内滚动到某个位置,可以使用window.requestAnimationFrame。 JSFiddle WebPage演示: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);
    }
    }
  });
}

演示:

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

或者,您可以使用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' 
});

演示:

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

如果仅需要滚动到元素,而不是文档中的特定位置,则可以将Element.scrollIntoView设置为behavior的情况下使用smooth

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

演示:

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

现代浏览器支持scroll-behavior CSS属性,该属性可用于使文档平滑滚动(无需Java脚本;可通过为锚标记提供{{1})来使用锚标记。 href的}加上要滚动到的元素的#。您还可以为诸如id之类的特定元素设置scroll-behavior属性,以使其内容平滑滚动。

演示:

div
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>时,CSS scroll-behavior属性也可以与Javascript一起使用。

演示:

window.scrollTo
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>属性是否受支持,可以检查该属性是否作为HTML元素样式的键存在。

scroll-behavior

答案 1 :(得分:1)

考虑使用Element.scrollIntoView()

答案 2 :(得分:1)

正如我在评论中提到的,scrollIntoView是一个不错的选择,可以在您尝试滚动到指定元素(例如您显然要使用的元素)时获得越来越大的浏览器支持。 scrollToSection功能。

要滚动到页面中间,可以将scrollTop和{或body元素的html属性设置为scrollHeight的一半正文和窗口的innerHeight。将以上计算与requestAnimationFrame结合起来,您就设置好了。

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

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>

答案 3 :(得分:1)

您可以使用以下简单的polyfill滚动所需的任何节点对象:

Node.prototype.scroll = window.scroll

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

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

答案 4 :(得分:0)

这个问题已经有很多答案了,但是我想我可以分享一下我的用处。

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

重要修改:确保CSS中没有html {scroll-behavior: smooth;}。否则,这将无法正常工作。

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

答案 5 :(得分:0)

使用此 CSS 属性可以将滚动行为切换为平滑。

html {
  scroll-behavior: smooth;
}

这也将通过哈希 <a href="#about"> 平滑滚动默认 html 导航到 <section id="about">,这里不需要 js。

如果您想为滚动添加自己的逻辑,请考虑此示例

在这里,我不是直接滚动到滚动目标元素,而是考虑到固定的标题高度高 90 像素。

TL;博士

document.querySelectorAll("nav a").forEach(function (a) {
        a.addEventListener("click", function (event) {
          event.preventDefault();
          const hash = event.target.getAttribute("href");
          const scrollTarget = document.querySelector(hash);
          
          // Some additional logic
          const headerHeight = 90;
          window.scrollTo(0, scrollTarget.offsetTop - headerHeight);
        });
      });