如何在纯 JavaScript 中平滑滚动到元素
Posted
技术标签:
【中文标题】如何在纯 JavaScript 中平滑滚动到元素【英文标题】:How to smoothly scroll to an element in pure JavaScript 【发布时间】:2019-01-12 08:27:18 【问题描述】:我想在不使用 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 解决方案)
【问题讨论】:
scrollIntoView
是您的案例的可行选择。查看我的answer,了解其使用的详细说明。
现代浏览器对您问题的第一部分也有一个纯 CSS 解决方案:scroll-behavior: smooth;
。然后,您的 JavaScript 解决方案可以作为备用方案。
【参考方案1】:
要在准确的时间内滚动到某个位置,可以使用window.requestAnimationFrame
,每次计算适当的当前位置。在不支持requestAnimationFrame
时,可以使用setTimeout
达到类似的效果。
/*
@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);
);
演示:
/*
@param time: the exact amount of time the scrolling will take (in milliseconds)
@param pos: the y-position to scroll to (in pixels)
*/
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);
);
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 300)">
Scroll To Div (300ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 200)">
Scroll To Div (200ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 100)">
Scroll To Div (100ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 50)">
Scroll To Div (50ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 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>
对于更复杂的情况,可以使用SmoothScroll.js library,它可以处理垂直和水平平滑滚动、在其他容器元素内滚动、不同的缓动行为、从当前位置相对滚动等等。
var easings = document.getElementById("easings");
for(var key in smoothScroll.easing)
if(smoothScroll.easing.hasOwnProperty(key))
var option = document.createElement('option');
option.text = option.value = key;
easings.add(option);
document.getElementById('to-bottom').addEventListener('click', function(e)
smoothScroll(yPos: 'end', easing: easings.value, duration: 2000);
);
document.getElementById('to-top').addEventListener('click', function(e)
smoothScroll(yPos: 'start', easing: easings.value, duration: 2000);
);
<script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll@1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
<!-- Taken from one of the library examples -->
Easing: <select id="easings"></select>
<button id="to-bottom">Scroll To Bottom</button>
<br>
<button id="to-top" style="margin-top: 5000px;">Scroll To Top</button>
或者,您可以将选项对象传递给 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 property,可用于使文档滚动顺畅(无需JavaScript)。锚标签可以通过给锚标签一个href
或#
加上要滚动到的元素的id
来使用)。您还可以为div
等特定容器设置scroll-behavior
属性,以使其内容平滑滚动。
演示:
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>
在使用window.scrollTo
时,CSS scroll-behavior
属性也适用于 JavaScript。
演示:
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>
要检查 scroll-behavior
属性是否受支持,您可以检查它是否作为 HTML 元素样式中的键存在。
var scrollBehaviorSupported = 'scroll-behavior' in document.documentElement.style;
console.log('scroll-behavior supported:', scrollBehaviorSupported);
【讨论】:
非常好的答案!特别喜欢我不知道的 scrollBy :) @Baldráni 谢谢!很高兴为您提供帮助!【参考方案2】:考虑使用Element.scrollIntoView()
。
【讨论】:
这在带有对象参数的 Safari 浏览器中不起作用,还有其他选择吗? @SnowBases 查看我的回答。【参考方案3】:正如我在评论中提到的,scrollIntoView
是一个不错的选择——当你尝试滚动到指定元素时,比如你显然想用 @987654324 做的事情,它会获得越来越多的浏览器支持@函数。
要滚动到页面中间,您可以将body
和/或html
元素的scrollTop
属性设置为主体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>
【讨论】:
【参考方案4】:你可以滚动任何你想要的节点对象,使用一个简单的 polyfill,像这样:
Node.prototype.scroll = window.scroll
它会给你同样的访问滚动对象,但是对于任何 DOM 元素,你可以像这样使用它:
document.querySelector('.scrollable-div').scroll(
top: 500,
left: 0,
behavior: 'smooth'
);
【讨论】:
【参考方案5】:这个问题已经有很多答案了,但我想我可能会分享我使用的东西。
以下允许您在指定的时间内平滑滚动到页面上的任何位置,向下或向上。我不确定它是否与所有浏览器兼容,但我很确定它是。 (如果我错了,有人纠正我。)
重要修改:确保您的 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
【讨论】:
【参考方案6】:使用此 CSS 属性可以将滚动行为切换为平滑。
html
scroll-behavior: smooth;
这也将通过哈希<a href="#about">
到<section id="about">
平滑滚动默认html导航,这里不需要js。
如果您想为滚动添加自己的逻辑,请考虑此示例
https://jsfiddle.net/arsenlol/Ls3f2ujq/54/这里我不是直接滚动到滚动目标元素,而是考虑到固定的标题高度高 90 像素。
TL;DR
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);
);
);
【讨论】:
以上是关于如何在纯 JavaScript 中平滑滚动到元素的主要内容,如果未能解决你的问题,请参考以下文章