web-dev-qa-db-fra.com

Comment faire défiler en douceur un élément en JavaScript pur

Je veux faire défiler en douceur un élément sans utiliser jQuery - juste du javascript pur. Je voudrais qu'une fonction générique puisse faire défiler vers le bas et vers le haut en douceur jusqu'à une position spécifique dans le document.

Je sais que je peux utiliser les éléments suivants dans jQuery:

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

Comment pourrais-je le faire avec du javascript?

C'est ce que j'essaie de faire:

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>

Dans jQuery je le ferais comme suit:

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: J'aimerais aussi pouvoir faire défiler en douceur jusqu'à une certaine position sur la page

EDIT: les solutions CSS sont également les bienvenues (bien que je préfère les solutions javascript)

8
user10181216

Vous pouvez utiliser une boucle for avec window.scrollTo et setTimeout pour faire défiler en douceur avec du Javascript simple. Pour faire défiler un élément spécifique, appelez simplement la fonction scrollToSmoothly avec offsetTop comme premier 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);
  }
}

Démo:

<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 peut être utilisé pour accéder à une position donnée dans un laps de temps précis. 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);
    }
    }
  });
}

Démo:

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

Vous pouvez également utiliser window.scroll qui fait défiler jusqu'à une position x et y spécifique et window.scrollBy qui défile depuis la position actuelle:

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

Démo:

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

Si vous avez seulement besoin de faire défiler jusqu'à un élément, pas une position spécifique dans le document, vous pouvez utiliser Element.scrollIntoView avec behavior défini sur smooth

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

Démo:

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

Les navigateurs modernes supportent la propriété CSS scroll-behavior, qui peut être utilisée pour rendre le défilement plus fluide dans le document (sans recourir à Javascript; les balises anchor peuvent être utilisées à cet effet en donnant à la balise ancre une href de # plus la id de l'élément à faire défiler à). Vous pouvez également définir la propriété scroll-behavior pour un élément spécifique, tel que div, afin que son contenu défile de manière fluide.

Démo:

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>

La propriété CSS scroll-behavior fonctionne également avec Javascript avec window.scrollTo.

Démo:

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>

Pour vérifier si la propriété scroll-behavior est prise en charge, vous pouvez vérifier si elle existe en tant que clé dans le style de l'élément HTML.

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

11
hev1

Comme je l’ai mentionné dans mon commentaire, scrollIntoView est une bonne option à envisager - elle prend de plus en plus en charge les navigateurs - lorsque vous essayez de faire défiler jusqu’à un élément spécifié, comme ce que vous essayez apparemment de faire avec votre fonction scrollToSection.

Pour faire défiler l'écran jusqu'au milieu de la page, définissez la propriété scrollTop de l'élément body et/ou html sur la moitié de la différence entre scrollHeight du corps et innerHeight de la fenêtre. Coupler le calcul ci-dessus avec requestAnimationFrame et vous êtes défini.

Voici comment vous pouvez intégrer les suggestions ci-dessus dans votre 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>

1
Angel Politis

Pensez à utiliser Element.scrollIntoView() .

1
Kosh Very