如何检测DIV的尺寸变化?

Wil*_*hoi 314 html javascript jquery

我有以下示例html,有一个100%宽度的DIV.它包含一些元素.在执行窗口重新调整大小时,可以重新定位内部元素,并且div的尺寸可以改变.我问是否可以挂钩div的尺寸变化事件?以及如何做到这一点?我目前将回调函数绑定到目标DIV上的jQuery resize事件,但是,没有输出控制台日志,请参见下文:

在调整大小之前 在此输入图像描述

<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>
Run Code Online (Sandbox Code Playgroud)

Mar*_*idt 251

有一种非常有效的方法可以确定元素的大小是否已更改.

http://marcj.github.io/css-element-queries/

该库有一个ResizeSensor可用于调整大小检测的类.
它使用基于事件的方法,因此速度快,不会浪费CPU时间.

例:

new ResizeSensor(jQuery('#divId'), function(){ 
    console.log('content dimension changed');
});
Run Code Online (Sandbox Code Playgroud)

请不要使用jQuery onresize插件,因为它使用setTimeout()循环来检查更改.
这是令人难以置信的缓慢而不准确.

披露:我直接与此库相关联.

  • @Aletheios你应该仔细看看.requestAnimationFrame不是用于慢速维度检测,而是完全相反:它平滑了由基于真实事件的调整大小传感器触发的所有极端事件数,以节省CPU时间.请参阅https://github.com/marcj/css-element-queries/blob/master/src/ResizeSensor.js#L9上的更新文档 (15认同)
  • @Orwellophile你显然没有理解任何东西.停止向你不知道内部如何运作的答案.再次:css-element-query:使用每次维度更改时触发的实际resize事件.由于你会得到太多的事件(例如对于拖拽),它添加了requestAnimationFrame检查一个简单的布尔变量,以将这些触发的事件平滑到1 /帧.jquery插件:使用setTimeout检查所有DOM布局道具(clientHeight等),其中每个检查非常慢,但需要经常检查以具有相同的功能. (9认同)
  • @Aletheios,@ Orwellophile,以及任何可能被他们误导的人:`requestAnimationFrame` vs`setTimeout`是无关紧要的,这个库_**不会循环**_.`requestAnimationFrame`仅用于去抖/限制调用你的回调的频率,它_**不会轮询**_.MutationObserver理论上可以用于类似的效果,但不适用于旧版浏览器,不像这样.这非常聪明. (9认同)
  • @jake,IE11支持现已实现. (8认同)
  • @Aletheios我猜你错过了一个观点:setTimeout不仅不准确而且速度慢.检查简单布尔值的requestAnimationFrame比一直检查DOM属性的setTimeout快几个数量级.除此之外,每隔几毫秒也会调用一次setTimeout. (6认同)
  • @Orwellophile,"纯粹是基于DOM的事件",jQuery中没有基于DOM的解决方案,因为只有`window`对象称为`resize`.setTimeout很慢,如果他们切换到window.resize事件然后它不处理所有情况都可能发生resize事件(如动画,innerHTML更改,css更改等) (6认同)
  • @ScottSelby您是否尝试过ResizeSensor课程?看起来您可以使用它来监听大小更改,然后应用您自己的逻辑. (3认同)
  • @Orwellophile老兄,在他的回答中他说'jQuery onresize插件因为它使用了setTimeout()',请注意*onresize插件*位.jQuery没有本机元素大小调整,只是一个窗口.请参阅[onresize](https://github.com/cowboy/jquery-resize/blob/master/jquery.ba-resize.js)了解他所说的内容 (3认同)
  • 好的图书馆,tks,但我错过了更好的文档 (3认同)
  • @JohnWeisz确切地说. (3认同)
  • @ MarcJ.Schmidt感谢您的澄清.然而,`requestAnimationFrame`每隔几毫秒运行一次.由于似乎没有真正解决问题的方法,我个人更喜欢不准确的`setTimeout/setInterval`,它可以更频繁地运行.但是,脏检查是一种很好的方法. (2认同)
  • @owen,来自各种触发器.如果div大小改变,它将检测它,无论原因是什么. (2认同)
  • **惊人的小工具!!!**刚刚添加了三行`<script src ="ResizeSensor.js"> </ script> <script src ="ElementQueries.js"> </ script> <script> new ResizeSensor( jQuery('#ang'),function(){parent.resizeIframe();}); </ script>`和我的包含iframe绝对可靠地调整每个子事件(复杂的AngularJS应用程序)的大小.非常感谢@Marc Schmidt,因为github主页易于使用,很容易看到(虽然为什么它没有这个例子?). (2认同)
  • 我通常不会对代码留下深刻的印象,但这对代码本身和性能都非常出色。 (2认同)
  • @CJGarcía setTimeout 本身并不坏,但你在 setTimeout 中所做的事情很重要。大多数调整大小检查器的实现方式是每隔几毫秒执行一次通过 setTimeout 的函数来检查尺寸变化。这是通过读取 DOM 元素上的“clientHeight”、“clientWidth”来实现的,这非常慢,因为它会触发渲染引擎中的布局计算。相反,我的库使用 setTimeout 来检查一个简单的布尔变量(通过基于事件的调整大小检测器设置),这可能快几百万倍。 (2认同)

Dan*_*err 191

一个新标准是Resize Observer api,可在Chrome 64中使用.

function outputsize() {
 width.value = textbox.offsetWidth
 height.value = textbox.offsetHeight
}
outputsize()

new ResizeObserver(outputsize).observe(textbox)
Run Code Online (Sandbox Code Playgroud)
Width: <output id="width">0</output><br>
Height: <output id="height">0</output><br>
<textarea id="textbox">Resize me</textarea><br>
Run Code Online (Sandbox Code Playgroud)

调整观察者大小

规范:https://wicg.github.io/ResizeObserver

Polyfills:https://github.com/WICG/ResizeObserver/issues/3

Firefox问题:https://bugzil.la/1272409

Safari问题:http://wkb.ug/157743

当前支持:http://caniuse.com/#feat=resizeobserver

  • 现在所有主流浏览器都支持 ResizeObserver。 (22认同)
  • @Philipp有一个polyfill. (8认同)
  • 在这个答案之后的两年,浏览器支持也很惨淡:https://caniuse.com/#search=resizeobserver (5认同)
  • 现在可以在 Firefox、chrome、safari、Opera 上运行。 (5认同)
  • 尚不适用于生产站点,因为目前只有Chrome支持。但是绝对是这里最好的解决方案!顺便说一句,这是实施状态的快速概述:https://www.chromestatus.com/feature/5705346022637568 (3认同)
  • 在最新的 Chrome 和 FF 中表现得非常出色。 (3认同)
  • 似乎它仍然是所有浏览器中的实验性功能http://caniuse.com/#feat=resizeobserver (2认同)
  • @Cyber​​netic Resize Observer 并不特定于文本框,它适用于包括 div 在内的任何元素。 (2认同)

Ale*_*olo 32

您必须resizewindow对象上绑定事件,而不是在通用html元素上绑定事件.

然后你可以使用这个:

$(window).resize(function() {
    ...
});
Run Code Online (Sandbox Code Playgroud)

在回调函数中,您可以检查div调用的新宽度

$('.a-selector').width();
Run Code Online (Sandbox Code Playgroud)

所以,你的问题的答案是否定的,你不能将resize事件绑定到div.

  • 这个答案还不够好.如果没有任何窗口调整大小,可能会发生大小更改 一点都不 (112认同)
  • 例如,当您有一个拆分器元素或只是将一些文本或其他内容附加到元素时. (9认同)

nkr*_*ron 27

从长远来看,您将能够使用ResizeObserver.

new ResizeObserver(callback).observe(element);
Run Code Online (Sandbox Code Playgroud)

不幸的是,许多浏览器目前默认不支持它.

同时,您可以使用如下功能.因为,大多数元素大小的更改将来自窗口大小调整或来自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
  });
};
Run Code Online (Sandbox Code Playgroud)


Mar*_*tke 20

ResizeSensor.js是一个巨大的库的一部分,但我将其功能减少到了这个:

function ResizeSensor(element, callback)
{
    let zIndex = parseInt(getComputedStyle(element));
    if(isNaN(zIndex)) { zIndex = 0; };
    zIndex--;

    let expand = document.createElement('div');
    expand.style.position = "absolute";
    expand.style.left = "0px";
    expand.style.top = "0px";
    expand.style.right = "0px";
    expand.style.bottom = "0px";
    expand.style.overflow = "hidden";
    expand.style.zIndex = zIndex;
    expand.style.visibility = "hidden";

    let expandChild = document.createElement('div');
    expandChild.style.position = "absolute";
    expandChild.style.left = "0px";
    expandChild.style.top = "0px";
    expandChild.style.width = "10000000px";
    expandChild.style.height = "10000000px";
    expand.appendChild(expandChild);

    let shrink = document.createElement('div');
    shrink.style.position = "absolute";
    shrink.style.left = "0px";
    shrink.style.top = "0px";
    shrink.style.right = "0px";
    shrink.style.bottom = "0px";
    shrink.style.overflow = "hidden";
    shrink.style.zIndex = zIndex;
    shrink.style.visibility = "hidden";

    let shrinkChild = document.createElement('div');
    shrinkChild.style.position = "absolute";
    shrinkChild.style.left = "0px";
    shrinkChild.style.top = "0px";
    shrinkChild.style.width = "200%";
    shrinkChild.style.height = "200%";
    shrink.appendChild(shrinkChild);

    element.appendChild(expand);
    element.appendChild(shrink);

    function setScroll()
    {
        expand.scrollLeft = 10000000;
        expand.scrollTop = 10000000;

        shrink.scrollLeft = 10000000;
        shrink.scrollTop = 10000000;
    };
    setScroll();

    let size = element.getBoundingClientRect();

    let currentWidth = size.width;
    let currentHeight = size.height;

    let onScroll = function()
    {
        let size = element.getBoundingClientRect();

        let newWidth = size.width;
        let newHeight = size.height;

        if(newWidth != currentWidth || newHeight != currentHeight)
        {
            currentWidth = newWidth;
            currentHeight = newHeight;

            callback();
        }

        setScroll();
    };

    expand.addEventListener('scroll', onScroll);
    shrink.addEventListener('scroll', onScroll);
};
Run Code Online (Sandbox Code Playgroud)

如何使用它:

let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
    console.log("dimension changed:", container.clientWidth, container.clientHeight);
});
Run Code Online (Sandbox Code Playgroud)

  • 我很困惑“让 zIndex = parseInt(getComputedStyle(element));” 有效的?我假设你的意思是:“让 zIndex = parseInt(getComputedStyle(element).zIndex); (2认同)

Jer*_*yal 17

我不建议setTimeout()破解,因为它会降低性能!相反,您可以使用 DOM突变观察器来收听Div大小更改.

JS:

var observer = new MutationObserver(function(mutations) {
    console.log('size changed!');
  });
  var target = document.querySelector('.mydiv');
  observer.observe(target, {
    attributes: true
  });
Run Code Online (Sandbox Code Playgroud)

HTML:

<div class='mydiv'>
</div>
Run Code Online (Sandbox Code Playgroud)

这是小提琴
尝试改变div大小.


您可以在方法中进一步包装debounce方法以提高效率.debounce将每x毫秒触发您的方法,而不是每隔毫秒触发DIV的大小调整.

  • 这个问题唯一的问题是它不会报告更改,除非元素实际发生了突变.如果元素由于添加了其他元素而调整大小,则不起作用. (9认同)

jos*_*ley 15

当MarcJ的解决方案没有找到时,我发现这个库可以工作:

https://github.com/sdecima/javascript-detect-element-resize

它非常轻巧,可以通过CSS或简单的HTML加载/渲染来检测自然调整大小.

代码示例(取自链接):

<script type="text/javascript" src="detect-element-resize.js"></script>
<script type="text/javascript">
  var resizeElement = document.getElementById('resizeElement'),
      resizeCallback = function() {
          /* do something */
      };
  addResizeListener(resizeElement, resizeCallback);
  removeResizeListener(resizeElement, resizeCallback);
</script>
Run Code Online (Sandbox Code Playgroud)


Sta*_*tan 14

最好的解决方案是使用所谓的Element Queries.但是,它们不是标准的,没有规范存在 - 唯一的选择是使用其中一个可用的polyfill /库,如果你想这样做的话.

元素查询背后的想法是允许页面上的某个容器响应提供给它的空间.这将允许一次编写一个组件,然后将其放在页面的任何位置,同时将其内容调整为当前大小.无论窗口大小是多少.这是我们在元素查询和媒体查询之间看到的第一个区别.每个人都希望在某个时候创建​​一个规范来标准化元素查询(或实现相同目标的东西),并使它们本机,干净,简单和健壮.大多数人都认为媒体查询非常有限,对模块化设计和真正的响应性没有帮助.

有一些polyfill /库以不同的方式解决问题(可称为变通方法,而不是解决方案):

我已经看到了提出类似问题的其他解决方案.通常他们使用计时器或窗口/视口大小,这不是一个真正的解决方案.此外,我认为理想情况下这应该主要在CSS中解决,而不是在javascript或html中.


Bha*_*til 13

看看这个http://benalman.com/code/projects/jquery-resize/examples/resize/

它有各种例子.尝试调整窗口大小,看看容器元素中的元素是如何调整的.

用js来解释如何使其工作的示例.
看看这个小提琴http://jsfiddle.net/sgsqJ/4/

因为resize()事件绑定到具有类"test"的元素以及窗口对象和窗口对象$('.test')的resize回调中.调用resize().

例如

$('#test_div').bind('resize', function(){
            console.log('resized');
});

$(window).resize(function(){
   $('#test_div').resize();
});
Run Code Online (Sandbox Code Playgroud)

  • 这不会直接检测尺寸变化.它只是resize事件监听器的副产品.尺寸变化可以通过许多其他方式发生. (24认同)
  • 如果调整div的大小来自样式或dom的改变而不是通过窗口调整大小,该怎么办? (4认同)
  • 请不要使用jQuery的resize插件.它真的很慢而且不准确.请参阅我的答案以获得更好的解决方案. (3认同)

Poi*_*nty 8

只有window对象生成"resize"事件.我知道做你想做的唯一方法是运行一个定期检查大小的间隔计时器.


Ami*_*ein 8

您可以使用iframeobject使用contentWindowcontentDocument调整大小.没有setIntervalsetTimeout

步骤:

  1. 将元素位置设置为 relative
  2. 添加透明绝对隐藏的IFRAME
  3. IFRAME.contentWindow- onresize活动

HTML的一个例子:

<div style="height:50px;background-color:red;position:relative;border:1px solid red">
<iframe style=width:100%;height:100%;position:absolute;border:none;background-color:transparent allowtransparency=true>
</iframe>
This is my div
</div>
Run Code Online (Sandbox Code Playgroud)

Javascript:

$('div').width(100).height(100);
$('div').animate({width:200},2000);

$('object').attr({
    type : 'text/html'
})
$('object').on('resize,onresize,load,onload',function(){
    console.log('ooooooooonload')
})

$($('iframe')[0].contentWindow).on('resize',function(){
    console.log('div changed')
})
Run Code Online (Sandbox Code Playgroud)

运行示例

JsFiddle:https://jsfiddle.net/qq8p470d/


看更多:

  • 虽然这是有效的,但它是一个重量级的解决方案,只需要创建一个全新的浏览上下文来观察大小的变化.此外,在某些情况下,更改元素的`display`属性并不理想或不可行. (3认同)

Aik*_*wai 7

var div = document.getElementById('div');
div.addEventListener('resize', (event) => console.log(event.detail));

function checkResize (mutations) {
    var el = mutations[0].target;
    var w = el.clientWidth;
    var h = el.clientHeight;
    
    var isChange = mutations
      .map((m) => m.oldValue + '')
      .some((prev) => prev.indexOf('width: ' + w + 'px') == -1 || prev.indexOf('height: ' + h + 'px') == -1);

    if (!isChange)
      return;

    var event = new CustomEvent('resize', {detail: {width: w, height: h}});
    el.dispatchEvent(event);
}

var observer = new MutationObserver(checkResize); 
observer.observe(div, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
Run Code Online (Sandbox Code Playgroud)
#div {width: 100px; border: 1px solid #bbb; resize: both; overflow: hidden;}
Run Code Online (Sandbox Code Playgroud)
<div id = "div">DIV</div>
Run Code Online (Sandbox Code Playgroud)


小智 7

令人惊讶的是,尽管这个问题已经存在,但在大多数浏览器中这仍然是一个问题。

正如其他人所说,Chrome 64+ 现在原生附带 Resize Observes,但是,规范仍在微调中,Chrome 目前(截至 2019-01-29)落后于最新版本的规范

我在野外看到了几个很好的 ResizeObserver polyfill,但是,有些没有严格遵循规范,有些则有一些计算问题。

我迫切需要这种行为来创建一些可以在任何应用程序中使用的响应式 Web 组件。为了让它们很好地工作,它们需要始终知道它们的尺寸,所以 ResizeObservers 听起来很理想,我决定创建一个尽可能符合规范的 polyfill。

回购:https : //github.com/juggle/resize-observer

演示:https : //codesandbox.io/s/myqzvpmmy9


Zza*_*con 5

使用 Clay.js ( https://github.com/zzarcon/clay ) 检测元素大小的变化非常简单:

var el = new Clay('.element');

el.on('resize', function(size) {
    console.log(size.height, size.width);
});
Run Code Online (Sandbox Code Playgroud)

  • 鉴于这是您的图书馆,请提供适当的披露信息。 (7认同)