问题
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.
I know I can use the following in jQuery:
$('html, body').animate({
scrollTop: $('#myelementid').offset().top
}, 500);
How would I do it with just 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:
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)
回答1:
You can use a for
loop with window.scrollTo
and setTimeout
to scroll smoothly with plain Javascript. To scroll to a specific element, just call the scrollToSmoothly
function with the element's offsetTop as the first argument.
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.requestAnimationFrame
can be put to use. JSFiddle WebPage Demo: 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.scroll
which scrolls to a specific x and y position and window.scrollBy
which scrolls from the current position:
// 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.scrollIntoView
with behavior
set 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-behavior
CSS 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 href
of #
plus the id
of the Element to scroll to). You can also set the scroll-behavior
property for a specific element like a div
to make its contents scroll smoothly.
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-behavior
property works with Javascript as well when using 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-behavior
property is supported, you can check if it exists as a key in the style of the HTML element.
var scrollBehaviorSupported = 'scroll-behavior' in document.documentElement.style;
console.log('scroll-behavior supported:',scrollBehaviorSupported);
回答2:
Consider using Element.scrollIntoView().
回答3:
As I mentioned in my comment, scrollIntoView is 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 scrollToSection
function.
To scroll to the middle of the page you can set the scrollTop
property of the body
and/or the html
element to half the difference of the scrollHeight
of the body and the innerHeight
of the window. Couple the above calculation with requestAnimationFrame and you are set.
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>
来源:https://stackoverflow.com/questions/51689653/how-to-smoothly-scroll-to-an-element-in-pure-javascript