当overflow为auto,且内部元素溢出时.元素沿padding edge向被内裁剪,在单侧显示滚动条
一般来说, 元素会从右/下侧被裁剪,具体数值取决于滚动条宽度
如上图,外层元素width为200px,padding为40px,border为10px.内有一宽高为300px的子元素
设置scroll属性后, 元素被从右/下方裁剪掉17px.并添加滚动条, 滚动条宽度为17px
被裁剪后,外层元素的实际显示宽度由200px变成了183px.再加上滚动条17px,总宽度跟原来相比没有发生变化
显示滚动条的条件
子元素不超出父元素也可能产生滚动条,具体取决于 内部元素宽度 + 滚动条宽度 是否大于容器宽度
如上图,左侧内部子元素宽度为220px,未超出容器宽度
右侧内部子元素宽度为224px,加上滚动条宽度,总宽度超出容器1px
scroll area 是scroll box的实际内容区域,有以下4条边
- top edge: 等于scroll box的top padding edge
- left edge: 等于scroll box的left padding edge
- right edge: 等于 max(right padding edge, 位于最右的后代元素的right margin edge)
- bottom edge: 等于 max(bottom padding edge, 位于最下的后代元素的right margin edge)
横轴最大滚动距离
scroll area用于scroll box可滚动范围的计算,计算方式为
1
| scroll area width - padding area width
|
如上图, a到b为最大滚动距离
元素的scroll area宽度为340px(40px + 300px), padding edge width为263px(40px + 200px + 40px - 17px)
因此,横向的最大滚动距离为77px(340px - 263px)
纵轴最大滚动距离
对于padding-bottom属性,chrome和firefox/ie的表现不同.
firefox/ie会忽略padding-bottom,好象其不存在一样
而chrome会显示padding-bottom,哪怕内部子元素低于padding-bottom也一样
chrome的计算方式
1
| (40 + 300 + 40) - (280 - 17) = 117
|
firefox的计算方式
1
| (40 + 300) - (280 - 17) = 77
|
滚动事件
1 当元素滚动时,会触发scroll事件
ie9+
1 2 3 4
| container.addEventListener('scroll', function (event) { var target = event.target; console.log(target.scrollTop, target.scrollLeft); })
|
ie5+
1 2 3 4 5
| container.onscroll = function (event) { event = event || window.event; var target = event.target || event.srcElement; console.log(target.scrollTop, target.scrollLeft); }
|
ie5+
1 2 3 4
| container.attachEvent('onscroll', function (event) { var target = event.target || event.srcElement; console.log(target.scrollTop, target.scrollLeft); })
|
注意attachEvent的事件名为onscroll
2 当元素大小发生变化时,触发滚动事件
是否触发滚动事件,取决于原滚动条位置是否大于当前的最大滚动距离,计算方式为
1 2
| max(0, min(x, element scrolling area width - element padding edge width)) max(0, min(y, element scrolling area height - element padding edge height))
|
如上图,元素初始width值为200时,最大滚动距离为
1
| (40 + 300) - (40 + 200 + 40 - 17) = 77
|
当width值变为201时,最大滚动距离为
1
| (40 + 300) - (40 + 201 + 40 - 17) = 76
|
max(0, min(77, 76)) = 76
如果之前x轴滚动距离为77, 则触发滚动事件
如果width值由200变为199,则width值为199时的最大滚动距离为
1
| (40 + 300) - (40 + 199 + 40 - 17) = 78
|
max(0, min(77, 78)) = 77
因此,当元素尺寸变小时,不会触发滚动事件
如果需要在变小时触发滚动事件,可以将子元素大小修改为百分比
如上图,滚动元素内子元素大小为200%
当滚动元素width值为200时,最大滚动距离为
1
| (40 + (200 - 17) * 200%) - (40 + 200 + 40 -17) = 143
|
当width值变为199时,最大滚动距离为
1
| (40 + (199 - 17) * 200%) - (40 + 199 + 40 -17) = 142
|
max(0, min(143, 142)) = 142
触发滚动事件
scrollTop可用于获取/设置元素的纵向动距离
scrollLeft可用于获取/设置元素的横向动距离
获取滚动距离
设置滚动距离
1
| element.scrollTop = 1000
|
scrollTop/scrollLeft接受整数值,当值
- 小于0时,设置为0
- 当值超出最大滚动值时,设置为最大滚动值
注 overflow值为hidden也可以滚动
Element.scrollHeight 是一个只读属性,返回内部元素的实际高度
Element.scrollWidth 是一个只读属性,返回内部元素的实际宽度
Chrome
1 2
| container.scrollHeight container.scrollWidth
|
Firefox
1 2
| container.scrollHeight container.scrollWidth
|
因为Firefox不计算padding-bottom,因此高度比Chrome小了40px
而Chorme添加滚动条后会计算padding-bottom,因此滚动时高度是380px,不滚动时高度是340px
Element.scrollIntoView()用于将元素滚动到页面可视区域内
1
| element.scrollIntoView({behavior: 'smooth'});
|
具体参数见 MDN
兼容性见 CanIuse
滚动动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
function scroll(element, scrollTop, duration) {
function easeInOut(t, b, c, d) { if (t == 0) return b; if (t == d) return b + c; if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b; return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; }
function getMaxScrollTop(element) { var temp = element.scrollTop; element.scrollTop = 10000000; var maxScrollTop = element.scrollTop; element.scrollTop = temp; return maxScrollTop; }
var startPosition = element.scrollTop; var endPosition = scrollTop;
var maxScrollTop = getMaxScrollTop(element); if (scrollTop > maxScrollTop) scrollTop = maxScrollTop; if (scrollTop < 0) scrollTop = 0;
if (startPosition === endPosition) return;
var startTime = Date.now(); var endTime = startTime + duration; var moveDistance = endPosition - startPosition; function animate() { var currentTime = Date.now(); if (currentTime >= endTime) { element.scrollTop = endPosition; return; } var scrollTop = easeInOut(currentTime - startTime, startPosition, moveDistance, duration); element.scrollTop = scrollTop; setTimeout(animate, 16.6); } function animateByRAF() { var currentTime = Date.now(); if (currentTime >= endTime) { element.scrollTop = endPosition; return; } var scrollTop = easeInOut(currentTime - startTime, startPosition, moveDistance, duration); element.scrollTop = scrollTop; window.requestAnimationFrame(animateByRAF); } if (window.requestAnimationFrame) { window.requestAnimationFrame(animateByRAF); } else { animate(); } } scroll(document.documentElement, 1000, 1000);
|
也可以用jQuery的animate函数实现滚动动画
1 2 3
| $('html, body').animate({ scrollTop: 500;, }, 200);
|
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> body { padding-top: 100px; padding-left: 100px; }
.container { overflow: scroll; width: 200px; height: 200px; padding: 40px; border: 10px solid; background: #ff0; background-clip: content-box; }
.child { width: 300px; height: 300px; }
.child { background-image: url('data:image/svg+xml,%3Csvg width=\'6\' height=\'6\' viewBox=\'0 0 6 6\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cg fill=\'%23ff0000\' fill-opacity=\'0.4\' fill-rule=\'evenodd\'%3E%3Cpath d=\'M5 0h1L0 6V5zM6 5v1H5z\'/%3E%3C/g%3E%3C/svg%3E'); }
::-webkit-scrollbar { width: 17px; height: 17px; background: transparent; }
::-webkit-scrollbar-thumb { background-color: blue; }
::-webkit-scrollbar-corner { display: none; } </style> </head>
<body> <div class="container" id="container"> <div class="child" id="child"></div> </div> <pre class="info" id="info"></pre> <div> <pre id="container-width"></pre> <button id="des-width">-1</button> <button id="ins-width">+1</button> </div> <script>; var container = document.getElementById('container'); var child = document.getElementById('child'); var info = document.getElementById('info'); var containerWidth = document.getElementById('container-width'); var insWidth = document.getElementById('ins-width'); var desWidth = document.getElementById('des-width');
info.innerText += 'scrollTop ' + container.scrollTop + '\n'; info.innerText += 'scrollLeft ' + container.scrollLeft; containerWidth.innerText = container.offsetWidth - 100; container.addEventListener('scroll', function () { var target = event.target; console.log(target.scrollTop, target.scrollLeft) info.innerText = ''; info.innerText += 'scrollTop ' + container.scrollTop + '\n'; info.innerText += 'scrollLeft ' + container.scrollLeft; }) insWidth.addEventListener('click', function (event) { var width = container.offsetWidth - 100; width++; container.style.width = width + 'px'; containerWidth.innerText = width; }) desWidth.addEventListener('click', function (event) { var width = container.offsetWidth - 100; width--; container.style.width = width + 'px'; containerWidth.innerText = width; }) </script> </body> </html>
|
参考
W3C scroll-an-element
W3C scrolling-box
巧妙监测元素尺寸变化
css - applying padding to box with scroll, bottom-padding doesn't work