J'utilise la méthode Javascript Element.scrollIntoView()
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
Existe-t-il un moyen de savoir quand le défilement est terminé? Disons qu'il y a eu une animation, ou j'ai défini {behavior: smooth}
.
Je suppose que le défilement est asynchrone et je veux savoir s'il y a un mécanisme de rappel.
Vous pouvez utiliser IntersectionObserver
, vérifiez si l'élément .isIntersecting
à IntersectionObserver
fonction de rappel
const element = document.getElementById("box");
const intersectionObserver = new IntersectionObserver((entries) => {
let [entry] = entries;
if (entry.isIntersecting) {
setTimeout(() => alert(`${entry.target.id} is visible`), 100)
}
});
// start observing
intersectionObserver.observe(box);
element.scrollIntoView({behavior: "smooth"});
body {
height: calc(100vh * 2);
}
#box {
position: relative;
top:500px;
}
<div id="box">
box
</div>
Il n'y a pas d'événement scrollEnd
, mais vous pouvez écouter l'événement scroll
et vérifier s'il fait toujours défiler la fenêtre:
var scrollTimeout;
addEventListener('scroll', function(e) {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(function() {
console.log('Scroll ended');
}, 100);
});
je ne suis pas un expert en javascript mais je l'ai fait avec jQuery. j'espère que ça aide
$("#mybtn").click(function() {
$('html, body').animate({
scrollTop: $("div").offset().top
}, 2000);
});
$( window ).scroll(function() {
$("div").html("scrolling");
if($(window).scrollTop() == $("div").offset().top) {
$("div").html("Ended");
}
})
body { height: 2000px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="mybtn">Scroll to Text</button>
<br><br><br><br><br><br><br><br>
<div>example text</div>
Pour ce comportement "fluide", tous les spécifications disons que
Lorsqu'un agent utilisateur doit effectuer un défilement régulier d'une zone de boîte de défilement jusqu'à sa position, il doit mettre à jour la position de défilement de la zone d'une manière définie par l'agent utilisateur sur une durée définie par l'agent utilisateur .
(souligne le mien)
Donc non seulement il n'y a pas d'événement unique qui se déclenche une fois qu'il est terminé, mais nous ne pouvons même pas supposer un comportement stabilisé entre différents navigateurs.
Et en effet, Firefox actuel et Chrome diffèrent déjà dans leur comportement:
Donc, cela disqualifie déjà toutes les solutions basées sur le délai d'attente pour ce problème.
Maintenant, l'une des réponses ici a proposé d'utiliser un MutationObserver, qui n'est pas une trop mauvaise solution, mais qui n'est pas trop portable, et ne prend pas les options inline
et block
en Compte.
Donc, le mieux est peut-être de vérifier régulièrement si nous avons arrêté le défilement. Pour ce faire de manière non invasive, nous pouvons démarrer une boucle alimentée requestAnimationFrame
, afin que nos vérifications ne soient effectuées qu'une seule fois par trame.
Voici une telle implémentation, qui renverra une promesse qui sera résolue une fois l'opération de défilement terminée.
Remarque: Ce code manque un moyen de vérifier si l'opération a réussi, car si une autre opération de défilement se produit sur la page, toutes les opérations en cours sont annulées, mais je laisse cela comme un exercice pour le lecteur.
const buttons = [ ...document.querySelectorAll( 'button' ) ];
document.addEventListener( 'click', ({ target }) => {
// handle delegated event
target = target.closest('button');
if( !target ) { return; }
// find where to go next
const next_index = (buttons.indexOf(target) + 1) % buttons.length;
const next_btn = buttons[next_index];
const block_type = target.dataset.block;
// make it red
document.body.classList.add( 'scrolling' );
smoothScroll( next_btn, { block: block_type })
.then( () => {
// remove the red
document.body.classList.remove( 'scrolling' );
} )
});
/*
*
* Promised based scrollIntoView( { behavior: 'smooth' } )
* @param { Element } elem
** ::An Element on which we'll call scrollIntoView
* @param { object } [options]
** ::An optional scrollIntoViewOptions dictionary
* @return { Promise } (void)
** ::Resolves when the scrolling ends
*
*/
function smoothScroll( elem, options ) {
return new Promise( (resolve) => {
if( !( elem instanceof Element ) ) {
throw new TypeError( 'Argument 1 must be an Element' );
}
let same = 0; // a counter
let lastPos = null; // last known Y position
// pass the user defined options along with our default
const scrollOptions = Object.assign( { behavior: 'smooth' }, options );
// let's begin
elem.scrollIntoView( scrollOptions );
requestAnimationFrame( check );
// this function will be called every painting frame
// for the duration of the smooth scroll operation
function check() {
// check our current position
const newPos = elem.getBoundingClientRect().top;
if( newPos === lastPos ) { // same as previous
if(same ++ > 2) { // if it's more than two frames
/* @todo: verify it succeeded
* if(isAtCorrectPosition(elem, options) {
* resolve();
* } else {
* reject();
* }
* return;
*/
return resolve(); // we've come to an halt
}
}
else {
same = 0; // reset our counter
lastPos = newPos; // remember our current position
}
// check again next painting frame
requestAnimationFrame(check);
}
});
}
p {
height: 400vh;
width: 5px;
background: repeat 0 0 / 5px 10px
linear-gradient(to bottom, black 50%, white 50%);
}
body.scrolling {
background: red;
}
<button data-block="center">scroll to next button <code>block:center</code></button>
<p></p>
<button data-block="start">scroll to next button <code>block:start</code></button>
<p></p>
<button data-block="nearest">scroll to next button <code>block:nearest</code></button>
<p></p>
<button>scroll to top</button>
Ces réponses ci-dessus laissent le gestionnaire d'événements en place même après le défilement (de sorte que si l'utilisateur défile, sa méthode continue d'être appelée). Ils ne vous informent pas non plus si aucun défilement n'est requis. Voici une réponse légèrement meilleure:
$("#mybtn").click(function() {
$('html, body').animate({
scrollTop: $("div").offset().top
}, 2000);
$("div").html("Scrolling...");
callWhenScrollCompleted(() => {
$("div").html("Scrolling is completed!");
});
});
// Wait for scrolling to stop.
function callWhenScrollCompleted(callback, checkTimeout = 200, parentElement = $(window)) {
const scrollTimeoutFunction = () => {
// Scrolling is complete
parentElement.off("scroll");
callback();
};
let scrollTimeout = setTimeout(scrollTimeoutFunction, checkTimeout);
parentElement.on("scroll", () => {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(scrollTimeoutFunction, checkTimeout);
});
}
body { height: 2000px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="mybtn">Scroll to Text</button>
<br><br><br><br><br><br><br><br>
<div>example text</div>