我有下面的样本html,有一个DIV有100%的宽度。它包含了一些元素。在执行窗口调整大小时,内部元素可能会被重新定位,div的尺寸可能会改变。我在问是否有可能挂钩div的维度变化事件?以及如何做到这一点?我目前绑定回调函数到目标DIV上的jQuery调整大小事件,但是,没有输出控制台日志,如下所示:
<html>
<head>
<script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" language="javascript">
$('#test_div').bind('resize', function(){
console.log('resized');
});
</script>
</head>
<body>
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" />
<input type="button" value="button 2" />
<input type="button" value="button 3" />
</div>
</body>
</html>
我不建议使用setTimeout(),因为它会降低性能!
相反,你可以使用DOM ResizeObserver方法来监听Div大小的变化。
const myObserver = new ResizeObserver(entries => {
// this will get called whenever div dimension changes
entries.forEach(entry => {
console.log('width', entry.contentRect.width);
console.log('height', entry.contentRect.height);
});
});
const someEl = document.querySelector('.some-element');
// start listening to changes
myObserver.observe(someEl);
// later, stop listening to changes
myObserver.disconnect();
使用MutationObserver的旧答案:
用于监听HTML元素属性、子树和类的更改:
JS:
var observer = new MutationObserver(function(mutations) {
console.log('size changed!');
});
var target = document.querySelector('.mydiv');
observer.observe(target, {
attributes: true,
childList: true,
subtree: true
});
HTML:
<div class='mydiv'>
</div>
这是小提琴。试着改变div大小。
您可以将您的方法进一步封装在debounce方法中以提高效率。debounce将每x毫秒触发一次你的方法,而不是在DIV被调整大小的每一毫秒触发一次。
这是一个非常老的问题,但我想我会把我的解决方案贴出来。
我尝试使用resizsensor,因为每个人似乎都对它有很大的好感。在实现之后,我意识到在底层,元素查询要求所讨论的元素具有相对位置或绝对位置,这不适用于我的情况。
我最终用Rxjs间隔来处理这个问题,而不是像以前的实现那样直接使用setTimeout或requestAnimationFrame。
间隔的可观察对象的好处在于,你可以修改流,但任何其他可观察对象都可以被处理。对我来说,一个基本的实现就足够了,但是你可以疯狂地做各种各样的合并等等。
在下面的例子中,我正在跟踪内部(绿色)div的宽度变化。它的宽度设置为50%,但max-width为200px。拖动滑块会影响包装器(灰色)div的宽度。你可以看到,只有当内部div的宽度发生变化时,可观察对象才会触发,只有当外部div的宽度小于400px时才会发生这种情况。
const { interval } = rxjs;
const { distinctUntilChanged, map, filter } = rxjs.operators;
const wrapper = document.getElementById('my-wrapper');
const input = document.getElementById('width-input');
function subscribeToResize() {
const timer = interval(100);
const myDiv = document.getElementById('my-div');
const widthElement = document.getElementById('width');
const isMax = document.getElementById('is-max');
/*
NOTE: This is the important bit here
*/
timer
.pipe(
map(() => myDiv ? Math.round(myDiv.getBoundingClientRect().width) : 0),
distinctUntilChanged(),
// adding a takeUntil(), here as well would allow cleanup when the component is destroyed
)
.subscribe((width) => {
widthElement.innerHTML = width;
isMax.innerHTML = width === 200 ? 'Max width' : '50% width';
});
}
function defineRange() {
input.min = 200;
input.max = window.innerWidth;
input.step = 10;
input.value = input.max - 50;
}
function bindInputToWrapper() {
input.addEventListener('input', (event) => {
wrapper.style.width = `${event.target.value}px`;
});
}
defineRange();
subscribeToResize();
bindInputToWrapper();
.inner {
width: 50%;
max-width: 200px;
}
/* Aesthetic styles only */
.inner {
background: #16a085;
}
.wrapper {
background: #ecf0f1;
color: white;
margin-top: 24px;
}
.content {
padding: 12px;
}
body {
font-family: sans-serif;
font-weight: bold;
}
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
<h1>Resize Browser width</h1>
<label for="width-input">Adjust the width of the wrapper element</label>
<div>
<input type="range" id="width-input">
</div>
<div id="my-wrapper" class="wrapper">
<div id="my-div" class="inner">
<div class="content">
Width: <span id="width"></span>px
<div id="is-max"></div>
</div>
</div>
</div>
从长远来看,您将能够使用ResizeObserver。
new ResizeObserver(callback).observe(element);
不幸的是,目前许多浏览器默认不支持它。
同时,您可以像下面这样使用函数。因为,大部分元素大小的变化将来自窗口大小的调整或DOM中的某些更改。你可以通过窗口的resize事件来监听窗口的大小调整,也可以使用MutationObserver监听DOM的变化。
下面是一个函数的例子,当提供的元素的大小因为这两个事件中的任何一个而改变时,它会回调你:
var onResize = function(element, callback) {
if (!onResize.watchedElementData) {
// First time we are called, create a list of watched elements
// and hook up the event listeners.
onResize.watchedElementData = [];
var checkForChanges = function() {
onResize.watchedElementData.forEach(function(data) {
if (data.element.offsetWidth !== data.offsetWidth ||
data.element.offsetHeight !== data.offsetHeight) {
data.offsetWidth = data.element.offsetWidth;
data.offsetHeight = data.element.offsetHeight;
data.callback();
}
});
};
// Listen to the window's size changes
window.addEventListener('resize', checkForChanges);
// Listen to changes on the elements in the page that affect layout
var observer = new MutationObserver(checkForChanges);
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
// Save the element we are watching
onResize.watchedElementData.push({
element: element,
offsetWidth: element.offsetWidth,
offsetHeight: element.offsetHeight,
callback: callback
});
};