J'ai un problème étrange que je ne peux répliquer que sur les navigateurs Microsoft (Edge et IE11 testés).
<style>
body {
height: 5000px;
width: 5000px;
}
</style>
<p>Click the button to scroll the document window to 1000 pixels.</p>
<button onclick="scrollWin()">Click me to scroll!</button>
<script>
function scrollWin() {
window.scrollTo({
left: 1000,
top: 1000,
behavior:"smooth"
});
}
</script>
Ce code fait défiler correctement la fenêtre de 1000 pixels vers la gauche et le bas, avec un comportement fluide dans Chrome et Firefox. Cependant, sur Edge et IE, il ne bouge pas du tout.
Ce n'est peut-être pas une vraie réponse au sens de la Parole, mais j'ai résolu ce problème en utilisant ce polyfill utile: https://github.com/iamdustan/smoothscroll qui fonctionne vraiment bien sur tous les navigateurs.
Exemple de page pour pollyfill: http://iamdustan.com/smoothscroll/
Un grand merci à l'auteur.
Comme mentionné précédemment, la spécification du comportement de défilement n'a été implémentée que dans Chrome, Firefox et Opera.
Voici une ligne pour détecter la prise en charge de la propriété behavior
dans ScrollOptions
:
const supportsNativeSmoothScroll = 'scrollBehavior' in document.documentElement.style;
Et voici une implémentation simple pour un défilement fluide entre les navigateurs: https://nicegist.github.io/d210786daa23fd57db59634dd231f341
Vous pouvez détecter la prise en charge de l'option behavior
dans scrollTo
à l'aide de cet extrait:
function testSupportsSmoothScroll () {
var supports = false
try {
var div = document.createElement('div')
div.scrollTo({
top: 0,
get behavior () {
supports = true
return 'smooth'
}
})
} catch (err) {}
return supports
}
Testé dans Chrome, Firefox, Safari et Edge, et semble fonctionner correctement. Si supports
est faux, vous revenez à un polyfill.
En effet, ils ne supportent pas cette variante, les articles MDN devraient être mis à jour.
Une façon de remplir cette méthode consiste à exécuter la méthode scroll
dans une boucle alimentée requestAnimationFrame. Rien d'extraordinaire ici.
Le principal problème qui se pose est comment détecter quand cette variante n'est pas prise en charge. en fait @ réponse de nlawson résout parfaitement ce problème ...
Pour cela, nous pouvons utiliser le fait qu'un appel à Window # scroll déclenchera un ScrollEvent si le viewPort a effectivement défilé.
Cela signifie que nous pouvons configurer un test asynchrone qui:
scroll(left , top)
pour être sûr que le Event se déclenchera,La mise en garde de ce test est donc qu'il s'agit d'un test asynchrone. Mais comme vous devez réellement attendre que le document soit chargé avant d'appeler cette méthode, je suppose que dans 99% des cas, ce sera correct.
Maintenant, pour alléger le document principal, et puisqu'il s'agit déjà d'un test asynchrone, nous pouvons même envelopper ce test dans un iframe, ce qui nous donne quelque chose comme:
/* Polyfills the Window#scroll(options) & Window#scrollTo(options) */
(function ScrollPolyfill() {
// The asynchronous tester
// wrapped in an iframe (will not work in SO's StackSnippet®)
var iframe = document.createElement('iframe');
iframe.onload = function() {
var win = iframe.contentWindow;
// listen for a scroll event
win.addEventListener('scroll', function handler(e){
// when the scroll event fires, check that we did move
if(win.pageXOffset < 99) { // !== 0 should be enough, but better be safe
attachPolyfill();
}
// cleanup
document.body.removeChild(iframe);
});
// set up our document so we can scroll
var body = win.document.body;
body.style.width = body.style.height = '1000px';
win.scrollTo(10, 0); // force the event
win.scrollTo({left:100, behavior:'instant'}); // the one we actually test
};
// prepare our frame
iframe.src = "about:blank";
iframe.setAttribute('width', 1);
iframe.setAttribute('height', 1);
iframe.setAttribute('style', 'position:absolute;z-index:-1');
iframe.onerror = function() {
console.error('failed to load the frame, try in jsfiddle');
};
document.body.appendChild(iframe);
// The Polyfill
function attachPolyfill() {
var original = window.scroll, // keep the original method around
animating = false, // will keep our timer's id
dx = 0,
dy = 0,
target = null;
// override our methods
window.scrollTo = window.scroll = function polyfilledScroll(user_opts) {
// if we are already smooth scrolling, we need to stop the previous one
// whatever the current arguments are
if(animating) {
clearAnimationFrame(animating);
}
// not the object syntax, use the default
if(arguments.length === 2) {
return original.apply(this, arguments);
}
if(!user_opts || typeof user_opts !== 'object') {
throw new TypeError("value can't be converted to a dictionnary");
}
// create a clone to not mess the passed object
// and set missing entries
var opts = {
left: ('left' in user_opts) ? user_opts.left : window.pageXOffset,
top: ('top' in user_opts) ? user_opts.top : window.pageYOffset,
behavior: ('behavior' in user_opts) ? user_opts.behavior : 'auto',
};
if(opts.behavior !== 'instant' && opts.behavior !== 'smooth') {
// parse 'auto' based on CSS computed value of 'smooth-behavior' property
// But note that if the browser doesn't support this variant
// There are good chances it doesn't support the CSS property either...
opts.behavior = window.getComputedStyle(document.scrollingElement || document.body)
.getPropertyValue('scroll-behavior') === 'smooth' ?
'smooth' : 'instant';
}
if(opts.behavior === 'instant') {
// not smooth, just default to the original after parsing the oject
return original.call(this, opts.left, opts.top);
}
// update our direction
dx = (opts.left - window.pageXOffset) || 0;
dy = (opts.top - window.pageYOffset) || 0;
// going nowhere
if(!dx && !dy) {
return;
}
// save passed arguments
target = opts;
// save the rAF id
animating = anim();
};
// the animation loop
function anim() {
var freq = 16 / 300, // whole anim duration is approximately 300ms @60fps
posX, poxY;
if( // we already reached our goal on this axis ?
(dx <= 0 && window.pageXOffset <= +target.left) ||
(dx >= 0 && window.pageXOffset >= +target.left)
){
posX = +target.left;
}
else {
posX = window.pageXOffset + (dx * freq);
}
if(
(dy <= 0 && window.pageYOffset <= +target.top) ||
(dy >= 0 && window.pageYOffset >= +target.top)
){
posY = +target.top;
}
else {
posY = window.pageYOffset + (dx * freq);
}
// move to the new position
original.call(window, posX, posY);
// while we are not ok on both axis
if(posX !== +target.left || posY !== +target.top) {
requestAnimationFrame(anim);
}
else {
animating = false;
}
}
}
})();
Désolé de ne pas avoir fourni de démonstration exécutable directement dans la réponse, mais les iframes sur-protégés de StackSnippet® ne nous permettent pas d'accéder au contenu d'une iframe interne sur IE ...
Donc, à la place, voici un lien vers un jsfiddle .
Post-scriptum: Je pense maintenant qu'il pourrait être possible de vérifier le support de manière synchrone en vérifiant le CSS scroll-behavior
support, mais je ne suis pas sûr qu'il couvre vraiment tous les UA de l'histoire ...
Post-Post-scriptum: En utilisant la détection de @ nlawson, nous pouvons maintenant avoir un extrait de travail ;-)
/* Polyfills the Window#scroll(options) & Window#scrollTo(options) */
(function ScrollPolyfill() {
// The synchronous tester from @nlawson's answer
var supports = false
test_el = document.createElement('div'),
test_opts = {top:0};
// ES5 style for IE
Object.defineProperty(test_opts, 'behavior', {
get: function() {
supports = true;
}
});
try {
test_el.scrollTo(test_opts);
}catch(e){};
if(!supports) {
attachPolyfill();
}
function attachPolyfill() {
var original = window.scroll, // keep the original method around
animating = false, // will keep our timer's id
dx = 0,
dy = 0,
target = null;
// override our methods
window.scrollTo = window.scroll = function polyfilledScroll(user_opts) {
// if we are already smooth scrolling, we need to stop the previous one
// whatever the current arguments are
if(animating) {
clearAnimationFrame(animating);
}
// not the object syntax, use the default
if(arguments.length === 2) {
return original.apply(this, arguments);
}
if(!user_opts || typeof user_opts !== 'object') {
throw new TypeError("value can't be converted to a dictionnary");
}
// create a clone to not mess the passed object
// and set missing entries
var opts = {
left: ('left' in user_opts) ? user_opts.left : window.pageXOffset,
top: ('top' in user_opts) ? user_opts.top : window.pageYOffset,
behavior: ('behavior' in user_opts) ? user_opts.behavior : 'auto',
};
if(opts.behavior !== 'instant' && opts.behavior !== 'smooth') {
// parse 'auto' based on CSS computed value of 'smooth-behavior' property
// But note that if the browser doesn't support this variant
// There are good chances it doesn't support the CSS property either...
opts.behavior = window.getComputedStyle(document.scrollingElement || document.body)
.getPropertyValue('scroll-behavior') === 'smooth' ?
'smooth' : 'instant';
}
if(opts.behavior === 'instant') {
// not smooth, just default to the original after parsing the oject
return original.call(this, opts.left, opts.top);
}
// update our direction
dx = (opts.left - window.pageXOffset) || 0;
dy = (opts.top - window.pageYOffset) || 0;
// going nowhere
if(!dx && !dy) {
return;
}
// save passed arguments
target = opts;
// save the rAF id
animating = anim();
};
// the animation loop
function anim() {
var freq = 16 / 300, // whole anim duration is approximately 300ms @60fps
posX, poxY;
if( // we already reached our goal on this axis ?
(dx <= 0 && window.pageXOffset <= +target.left) ||
(dx >= 0 && window.pageXOffset >= +target.left)
){
posX = +target.left;
}
else {
posX = window.pageXOffset + (dx * freq);
}
if(
(dy <= 0 && window.pageYOffset <= +target.top) ||
(dy >= 0 && window.pageYOffset >= +target.top)
){
posY = +target.top;
}
else {
posY = window.pageYOffset + (dx * freq);
}
// move to the new position
original.call(window, posX, posY);
// while we are not ok on both axis
if(posX !== +target.left || posY !== +target.top) {
requestAnimationFrame(anim);
}
else {
animating = false;
}
}
}
})();
// OP's code,
// by the time you click the button, the polyfill should already be set up if needed
function scrollWin() {
window.scrollTo({
left: 1000,
top: 1000,
behavior: 'smooth'
});
}
body {
height: 5000px;
width: 5000px;
}
<p>Click the button to scroll the document window to 1000 pixels.</p>
<button onclick="scrollWin()">Click me to scroll!</button>
Malheureusement, il n'y a aucun moyen pour cette méthode de fonctionner dans ces deux navigateurs. Vous pouvez vérifier les problèmes ouverts ici et voir qu'ils n'ont rien fait sur ce problème. https://developer.Microsoft.com/en-us/Microsoft-Edge/platform/issues/15534521/
Vous pouvez essayer d'utiliser les propriétés Element.ScrollLeft et Element.ScrollTop avec Window.scrollTo ().
Voici l'exemple qui fonctionne avec Edge et d'autres navigateurs.
<html>
<style>
body {
height: 5000px;
width: 5000px;
}
</style>
<p>Click the button to scroll the document window to 1000 pixels.</p>
<button onclick="scrollWin(this)">Click me to scroll!</button>
<script>
function scrollWin(pos) {
window.scrollTo(pos.offsetTop+1000,pos.offsetLeft+1000);
}
</script>
</html>
Le comportement fluide ne fonctionne pas avec ce code.
Référence:
Cordialement
Deepak