我正在尝试检测我的document
高度何时发生变化。一旦发生变化,我需要运行几个函数来帮助组织我的页面布局。
我不是在寻找window.onresize
。我需要整个大于窗口的文档。
如何观察这种变化?
我正在尝试检测我的document
高度何时发生变化。一旦发生变化,我需要运行几个函数来帮助组织我的页面布局。
我不是在寻找window.onresize
。我需要整个大于窗口的文档。
如何观察这种变化?
resizeObserver 是一个非常优秀的 API (兼容性表格)。
// create an Observer instance
const resizeObserver = new ResizeObserver(entries =>
console.log('Body height changed:', entries[0].target.clientHeight)
)
// start observing a DOM node
resizeObserver.observe(document.body)
// click anywhere to rnadomize height
window.addEventListener('click', () =>
document.body.style.height = Math.floor((Math.random() * 5000) + 1) + 'px'
)
click anywhere to change the height
虽然这个简单的函数是一种“hack”,但它通过setTimeout不断地“监听”元素高度的变化,并在检测到变化时触发回调。
需要注意的是,一个元素的高度可能会发生变化,而不管用户是否采取任何操作(例如缩放、点击等)。因此,为了确保100%的检测准确性,可以使用定时器来定期检查元素的高度:
function onElementHeightChange(elm, callback) {
var lastHeight = elm.clientHeight, newHeight;
(function run() {
newHeight = elm.clientHeight;
if (lastHeight != newHeight)
callback(newHeight)
lastHeight = newHeight
if (elm.onElementHeightChangeTimer)
clearTimeout(elm.onElementHeightChangeTimer)
elm.onElementHeightChangeTimer = setTimeout(run, 200)
})()
}
// to clear the timer use:
// clearTimeout(document.body.onElementHeightChangeTimer);
// DEMO:
document.write("click anywhere to change the height")
onElementHeightChange(document.body, function(h) {
console.log('Body height changed:', h)
})
window.addEventListener('click', function() {
document.body.style.height = Math.floor((Math.random() * 5000) + 1) + 'px'
})
iframe
,并监听其contentWindow
上的resize
事件。例如:
HTML
<body>
Your content...
<iframe class="height-change-listener" tabindex="-1"></iframe>
</body>
CSS
body {
position: relative;
}
.height-change-listener {
position: absolute;
top: 0;
bottom: 0;
left: 0;
height: 100%;
width: 0;
border: 0;
background-color: transparent;
}
JavaScript(使用jQuery,但可以适应纯JS)
$('.height-change-listener').each(function() {
$(this.contentWindow).resize(function() {
// Do something more useful
console.log('doc height is ' + $(document).height());
});
});
如果因为某些原因您在 body
上设置了 height:100%
,则需要找到(或添加)另一个容器元素来实现此功能。如果您想动态添加 iframe
,则可能需要使用 <iframe>.load
事件来附加 contentWindow.resize
监听器。如果您希望这也适用于 IE7 和其他浏览器,则需要向容器元素添加 *zoom:1
垫片,并在 <iframe>
元素本身上监听“专有”的 resize
事件(这将在 IE8-10 中复制 contentWindow.resize
)。这是一个演示示例...
100vh
是视口(也就是窗口)的高度,而我们想要检测文档的高度(即内容,这不是同一件事),以便在其更改时进行检测,这可以通过使用 100%
来实现。 (使用 window.resize
事件很容易检测视口高度的变化。) - Jake$($('.height-change-listener')[0] .contentWindow).on('resize',doSomethingMoreUseful);
- Bungle更新:2020年
现在可以使用新的 ResizeObserver 来实现此操作。这使您可以监听整个元素列表以便在其元素更改大小时进行响应。基本用法非常简单:
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
// each entry is an instance of ResizeObserverEntry
console.log(entry.contentRect.height)
}
})
observer.observe(document.querySelector('body'))
唯一的缺点是目前只支持Chrome/Firefox,但你可以找到一些靠谱的polyfills。这里有一个我写的codepen示例:
https://codepen.io/justin-schroeder/pen/poJjGJQ?editors=1111
这只是我的个人建议。如果你碰巧正在使用Angular,那么这个代码可以胜任:
$scope.$watch(function(){
return document.height();
},function onHeightChange(newValue, oldValue){
...
});
正如vsync所提到的,没有事件,但您可以使用计时器或将处理程序附加在其他位置:
// get the height
var refreshDocHeight = function(){
var h = $(document).height();
$('#result').html("Document height: " + h);
};
// update the height every 200ms
window.setInterval(refreshDocHeight, 200);
// or attach the handler to all events which are able to change
// the document height, for example
$('div').keyup(refreshDocHeight);
这里有jsfiddle示例。
vsync的答案完全没问题。如果您不想使用 setTimeout
,您可以使用requestAnimationFrame
(查看支持),当然,如果您仍然感兴趣。
在下面的示例中,body会获得一个额外的事件sizechange
。每次body的高度或宽度发生变化时,都会触发该事件。
(function checkForBodySizeChange() {
var last_body_size = {
width: document.body.clientWidth,
height: document.body.clientHeight
};
function checkBodySizeChange()
{
var width_changed = last_body_size.width !== document.body.clientWidth,
height_changed = last_body_size.height !== document.body.clientHeight;
if(width_changed || height_changed) {
trigger(document.body, 'sizechange');
last_body_size = {
width: document.body.clientWidth,
height: document.body.clientHeight
};
}
window.requestAnimationFrame(checkBodySizeChange);
}
function trigger(element, event_name, event_detail)
{
var evt;
if(document.dispatchEvent) {
if(typeof CustomEvent === 'undefined') {
var CustomEvent;
CustomEvent = function(event, params) {
var evt;
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
evt = document.createEvent("CustomEvent");
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
};
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
}
evt = new CustomEvent(event_name, {"detail": event_detail});
element.dispatchEvent(evt);
}
else {
evt = document.createEventObject();
evt.eventType = event_name;
evt.eventName = event_name;
element.fireEvent('on' + event_name, evt);
}
}
window.requestAnimationFrame(checkBodySizeChange);
})();
如果您在项目中拥有自己的triggerEvent
函数,那么代码可以大大减少。因此,只需删除完整的trigger
函数,并将trigger(document.body, 'sizechange');
这一行替换为例如jQuery中的$(document.body).trigger('sizechange');
。
requestAnimationFrame
替代setTimout
(或者setInterval
)的一个缺点是,大小检查循环会每隔几毫秒运行一次。使用setTimeout
可以将循环间隔增加到合理的时间。 - Gustavo Straube我正在使用@vsync的解决方案,就像这样。我将其用于类似Twitter页面的自动滚动。
const scrollInterval = (timeInterval, retry, cb) => {
let tmpHeight = 0;
const myInterval = setInterval(() => {
console.log('interval');
if (retry++ > 3) {
clearInterval(this);
}
const change = document.body.clientHeight - tmpHeight;
tmpHeight = document.body.clientHeight;
if (change > 0) {
cb(change, (retry * timeInterval));
scrollBy(0, 10000);
}
retry = 0;
}, timeInterval);
return myInterval;
};
const onBodyChange = (change, timeout) => {
console.log(`document.body.clientHeight, changed: ${change}, after: ${timeout}`);
}
const createdInterval = scrollInterval(500, 3, onBodyChange);
// stop the scroller on some event
setTimeout(() => {
clearInterval(createdInterval);
}, 10000);
你也可以添加最小更改和许多其他功能……但这对我有效
$.animate()
,但它可以在元素被删除/添加时进行选择。 - Steve Robbins