我有一个奇怪的问题,我只能在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>
此代码正确地将窗口向左和向下滚动1000px,在Chrome和Firefox中行为流畅。但是,在Edge和IE上,它根本不会移动。
答案 0 :(得分:4)
从词义上讲可能不是一个正确的答案,但是我已经通过使用这个有用的polyfill解决了这个问题:https://github.com/iamdustan/smoothscroll在所有浏览器中都非常有效。
polyfill的示例页面:http://iamdustan.com/smoothscroll/
非常感谢作者。
答案 1 :(得分:3)
如前所述,Scroll Behavior specification仅在Chrome,Firefox和Opera中实现。
这里是检测behavior
中的ScrollOptions
属性支持的单线:
const supportsNativeSmoothScroll = 'scrollBehavior' in document.documentElement.style;
这是跨浏览器平滑滚动的简单实现:https://nicegist.github.io/d210786daa23fd57db59634dd231f341
答案 2 :(得分:2)
实际上,他们不支持此变体,应更新MDN文章。
这种方法的一种填充方法是在 requestAnimationFrame 动力循环中运行scroll
方法。这里没什么好看的。
出现的主要问题是如何检测何时不支持此变体。 实际上@nlawson's answer完美地解决了这个问题...
为此,我们可以使用以下事实:如果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的链接。
后记:
现在我想到,实际上可以通过检查CSS scroll-behavior
支持来以同步方式检查支持,但是我不确定它是否真的涵盖了历史上的所有UA ... >
后期脚本: 使用@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>
答案 3 :(得分:2)
您可以使用以下代码段来检测对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
为假,则返回到polyfill。
答案 4 :(得分:0)
不幸的是,该方法无法在这两种浏览器中使用。 您可以在此处检查未解决的问题,看看他们在此问题上没有做任何事情。 https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/15534521/
答案 5 :(得分:0)
您可以尝试将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>
此代码无法正常运行。
参考:
致谢
Deepak
答案 6 :(得分:0)
“平滑滚动” polyfill仅支持“平滑”选项。要支持scrollIntoViewOptions
中的所有选项,最好使用 seamless-scroll-polyfill (https://www.npmjs.com/package/seamless-scroll-polyfill)
为我工作。
以下是带有说明https://github.com/Financial-Times/polyfill-library/issues/657的链接