Microsoftブラウザー(EdgeおよびIE11でテスト済み)でのみ複製できる奇妙な問題があります。
<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>
このコードは、ChromeおよびFirefoxではスムーズな動作で、ウィンドウを1000px左下に正しくスクロールします。ただし、EdgeおよびIEでは、まったく移動しません。
Wordの意味では本当の答えではないかもしれませんが、この便利なポリフィルを使用してこの問題を解決しました: https://github.com/iamdustan/smoothscroll これは、すべてのブラウザーで非常にうまく機能します。
Pollyfillのサンプルページ: http://iamdustan.com/smoothscroll/
著者に感謝します。
前述のように、 Scroll Behavior仕様 はChrome、Firefox、Operaにのみ実装されています。
behavior
のScrollOptions
プロパティのサポートを検出するワンライナーは次のとおりです。
const supportsNativeSmoothScroll = 'scrollBehavior' in document.documentElement.style;
そして、ここにクロスブラウザのスムーズなスクロールのための簡単な実装があります: https://nicegist.github.io/d210786daa23fd57db59634dd231f341
次のスニペットを使用すると、behavior
のscrollTo
オプションのサポートを検出できます。
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
}
Chrome、Firefox、Safari、およびEdgeでテストされ、正常に動作しているようです。 supports
がfalseの場合、ポリフィルにフォールバックします。
実際、彼らはこのバリアントをサポートしていません。MDN記事を更新する必要があります。
このメソッドをポリフィルする1つの方法は、scroll
メソッドをrequestAnimationFrameパワードループで実行することです。ここでは派手すぎません。
発生する主な問題は、このバリアントがサポートされていない場合の検出方法です。 実際 @ nlawsonの答え この問題に完璧に取り組みます...
これには、viewPortが実際にスクロールした場合、Window#scrollを呼び出すとScrollEventが発生するという事実を利用できます。
つまり、次のような非同期テストを設定できます。
scroll(left , top)
バリアントを呼び出して、Eventが確実に発生するようにします。したがって、このテストの注意点は、非同期テストであることです。しかし、このメソッドを呼び出す前に、ドキュメントが読み込まれるのを実際に待つ必要があるため、99%の場合は問題ないと思います。
メインドキュメントの負担を軽減するために、これはすでに非同期テストであるため、このテストをiframe内にラップすることもできます。
/* 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;
}
}
}
})();
回答の中に直接実行可能なデモを提供していないことに申し訳ありませんが、StackSnippet®の過剰に保護されたiframeでは、IEの内部iframeのコンテンツにアクセスできません...
代わりに、ここに jsfiddle 。へのリンクがあります
Post-scriptum:これで、CSSをチェックすることにより、同期の方法でサポートを実際にチェックできる可能性があるということが頭に浮かびますscroll-behavior
サポートしますが、履歴内のすべてのUAが本当にカバーされているかどうかはわかりません...
Post-Post-scriptum:@nlawsonの検出を使用して、機能するスニペットを作成できます;-)
/* 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>
残念ながら、これらの2つのブラウザでそのメソッドを機能させる方法はありません。ここで未解決の問題をチェックして、彼らがこの問題に対して何もしていないことを確認できます。 https://developer.Microsoft.com/en-us/Microsoft-Edge/platform/issues/15534521/
Element.ScrollLeftおよびElement.ScrollTopプロパティをWindow.scrollTo()で使用してみることができます。
以下はEdgeや他のブラウザで動作している例です。
<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>
このコードではスムーズな動作が機能しません。
参照:
よろしく
ディーパック