独家新闻
可以通过使用ResizeObserver 来检测滚动条可见性的变化,以检查可能带有滚动条的元素大小的变化及其内容大小的变化。
基本原理
我开始使用<iframe> 方法实现一个解决方案,但很快发现要实现一个完整的实现需要打破我的应用程序视图之间的关注点分离。我有一个父视图,它需要知道子视图何时获得垂直滚动条。 (我不关心水平滚动条。)我有两种情况可能会影响垂直滚动条的可见性:
调整了父视图的大小。这是在用户的直接控制之下。
子视图的内容变大或变小。这是在用户的间接控制之下。子视图显示搜索结果。结果的数量和类型决定了子视图的大小。
我发现如果我使用<iframe>,我将不得不使用子视图来支持父母的需求。我更喜欢孩子不包含纯粹是父母关心的东西的代码。使用我在这里描述的解决方案,只需要修改父视图。
所以在寻找更好的解决方案时,我找到了 Daniel Herr 的 this answer。他建议使用ResizeObserver 来检测 div 的尺寸何时发生变化。 ResizeObserver尚未在浏览器中原生可用,但有一个强大的 ponyfill/polyfill 用于在原生支持不可用的情况下提供支持。 (这里是spec 对应的ResizeObserver。)
概念验证
我在其 ponyfill 模式下使用this polyfill。这样,全球环境就不会受到影响。此实现依赖于window.requestAnimationFrame,对于不支持window.requestAnimationFrame 的平台,将依赖setTimeout。看着support for requestAnimationFrame 上的“我可以使用...?”,我在那里看到的并不会打扰我。 YMMV。
我有一个直播proof-of-concept。关键是监听可以接受滚动条的DOM元素的大小变化(id为container的元素,绿色)并监听可能需要滚动的内容的大小变化(id为@987654337的元素@)。概念验证使用interact.js 来管理允许调整container 大小的调整大小元素(ID 为resizer,蓝色)。如果拖动resizer 的右下角,它将同时调整resizer 和container 的大小。这两个按钮允许模拟container显示的内容大小的变化。
我在当前处于预发布阶段的代码中使用此方法,这意味着它通过了多个浏览器的测试,并且正在由利益相关者进行评估,但尚未投入生产。
HTML:
<!DOCTYPE html>
<html>
<head>
<script data-require="interact.js@*" data-semver="1.0.26" src="//rawgit.com/taye/interact.js/v1.0.26/interact.js"></script>
<script src="//rawgit.com/que-etc/resize-observer-polyfill/master/dist/ResizeObserver.global.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="resizer">
<div id="container">
<ul id="content">
<li>Something</li>
</ul>
</div>
</div>
<button id="add">Add to content</button>
<button id="remove">Remove from content</button>
<p>Scroll bar is: <span id="visibility"></span></p>
<ul id="event-log"></ul>
<script src="script.js"></script>
</body>
</html>
JavaScript:
var container = document.getElementById("container");
var resizer = document.getElementById("resizer");
interact(resizer)
.resizable({
restrict: {
restriction: {
left: 0,
top: 0,
right: window.innerWidth - 10,
bottom: window.innerHeight - 10
}
}
})
.on('resizemove', function(event) {
var target = resizer;
var rect = target.getBoundingClientRect();
var width = rect.width + event.dx;
var height = rect.height + event.dy;
target.style.width = width + 'px';
target.style.height = height + 'px';
});
var content = document.getElementById("content");
var add = document.getElementById("add");
add.addEventListener("click", function() {
content.insertAdjacentHTML("beforeend", "<li>Foo</li>");
});
var remove = document.getElementById("remove");
remove.addEventListener("click", function() {
content.removeChild(content.lastChild);
});
// Here is the code that pertains to the scrollbar visibility
var log = document.getElementById("event-log");
content.addEventListener("scrollbar", function () {
log.insertAdjacentHTML("beforeend", "<li>Scrollbar changed!</li>");
});
var visiblity = document.getElementById("visibility");
var previouslyVisible;
function refreshVisibility() {
var visible = container.scrollHeight > container.clientHeight;
visibility.textContent = visible ? "visible" : "not visible";
if (visible !== previouslyVisible) {
content.dispatchEvent(new Event("scrollbar"));
}
previouslyVisible = visible;
}
// refreshVisibility();
var ro = new ResizeObserver(refreshVisibility);
ro.observe(container);
ro.observe(content);
CSS:
* {
box-sizing: border-box;
}
#container {
position: relative;
top: 10%;
left: 10%;
height: 80%;
width: 80%;
background: green;
overflow: auto;
}
#resizer {
background: blue;
height: 200px;
width: 200px;
}