调整div元素的大小

88

jQuery有resize()事件,但它只适用于窗口。

jQuery(window).resize(function() { /* What ever */ });
这很好用!但是当我想将该事件添加到一个div元素上时,它就不起作用了。
例如:
jQuery('div').resize(function() { /* What ever */ });

我想在一个div元素的大小改变时启动回调函数,我不想启动可调整大小的事件,只是想要一个事件来检查div元素的大小是否已更改。

有没有解决方案可以实现这个功能?


3
调整大小事件只在窗口对象(和可能的 iframe 对象)上触发。 - BiAiB
3
请查看此页面 - http://benalman.com/code/projects/jquery-resize/examples/resize/ - Elen
1
这可能会有所帮助:resizeObserver。https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver。Chrome 64和Firefox 69支持... - captain puget
13个回答

35

DIV 不会触发 resize 事件,因此您无法完全实现您编码的功能,但您可以尝试查看监视 DOM 属性

如果您实际上正在处理可调整大小的对象,并且这是 div 更改大小的唯一方法,则您的调整大小插件可能会实现自己的回调函数。


您可以使用$(window).trigger("resize");触发窗口调整大小事件。 - Laurent
请更新监控DOM属性的链接。 - phoenixstudio

33

我只对元素宽度的变化感兴趣(不关心高度),因此我创建了一个使用不可见iframe元素的jQuery事件,以便触发这种情况。

$.event.special.widthChanged = {
  remove: function() {
      $(this).children('iframe.width-changed').remove();
  },
  add: function () {
      var elm = $(this);
      var iframe = elm.children('iframe.width-changed');
      if (!iframe.length) {
          iframe = $('<iframe/>').addClass('width-changed').prependTo(this);
      }
      var oldWidth = elm.width();
      function elmResized() {
          var width = elm.width();
          if (oldWidth != width) {
              elm.trigger('widthChanged', [width, oldWidth]);
              oldWidth = width;
          }
      }

      var timer = 0;
      var ielm = iframe[0];
      (ielm.contentWindow || ielm).onresize = function() {
          clearTimeout(timer);
          timer = setTimeout(elmResized, 20);
      };
  }
}

需要以下的css:

iframe.width-changed {
    width: 100%;
    display: block;
    border: 0;
    height: 0;
    margin: 0;
}
你可以在这里看到它的运作效果:widthChanged fiddle

这是正确的答案。将事件附加到iframe窗口比使用间隔更不容易出现问题。 - Kevin Beal
1
太完美了!我通过动态添加样式表,使我的代码完全基于js:var ss = document.createElement('style'); ss.type = "text/css"; ss.innerHTML = "iframe.width-changed {width: 100%;display: block;border: 0;height: 0;margin: 0;}"; document.body.appendChild(ss); - runfaj
这纯粹是天才。 - Thomas
我要使用它,但我想知道:计时器和setTimeout的作用是什么?它是否像“去抖动”/节流一样,防止用户调整元素大小时检测到太多的调整大小? - Mathieu
是的,这是为了防止过多地调用“widthChanged”触发器。 - Panos Theof
我创建了jQuery.resize插件 https://github.com/jcubic/jquery.resize 它使用完全相同的技术(它也在高度上触发),但如果浏览器支持,它还使用ResizeObserver。 - jcubic

21
// this is a Jquery plugin function that fires an event when the size of an element is changed
// usage: $().sizeChanged(function(){})

(function ($) {

$.fn.sizeChanged = function (handleFunction) {
    var element = this;
    var lastWidth = element.width();
    var lastHeight = element.height();

    setInterval(function () {
        if (lastWidth === element.width()&&lastHeight === element.height())
            return;
        if (typeof (handleFunction) == 'function') {
            handleFunction({ width: lastWidth, height: lastHeight },
                           { width: element.width(), height: element.height() });
            lastWidth = element.width();
            lastHeight = element.height();
        }
    }, 100);


    return element;
};

}(jQuery));

8
我创建了一个jQuery插件jquery.resize,它使用resizeObserver(如果支持)或基于marcj/css-element-queries滚动事件的解决方案,而不使用setTimeout/setInterval。
您只需使用


 jQuery('div').on('resize', function() { /* What ever */ });

或作为调整大小插件
jQuery('div').resizer(function() { /* What ever */ });

我为jQuery Terminal创建了这个插件并将其提取到单独的repo和npm包中,但同时我切换到了隐藏的iframe,因为如果元素在iframe内部,我会遇到调整大小的问题。我可能会相应地更新插件。您可以查看基于iframe的jQuery Terminal源代码调整大小插件编辑:新版本使用iframe并调整其窗口对象的大小,因为以前的解决方案在页面在iframe内部时无法工作。 编辑2:由于备用方案使用iframe,您无法将其用于表单控件或图像,您需要将其添加到包装器元素中。 编辑3:有更好的解决方案,使用resizeObserver polyfill,它使用变异观察器(如果不支持resizeObserver),甚至在IE中也能正常工作。它还具有TypeScript类型定义。

那是一个很棒的想法。做得好,真的。 - Marco Luzzara
为 EDIT3 欢呼。我被迫使用 IE11,这个解决了问题,谢谢! - samneric

5

1
你说它不会持续轮询,但实际上它确实会,它使用了requestAnimationFrame:https://github.com/sdecima/javascript-detect-element-resize/blob/master/detect-element-resize.js - Muhammad Umer
1
有趣的是,这个GitHub项目的灵感来自于这篇博客文章:http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/ 但是,博客文章中的代码后来被显著修改,而GitHub项目似乎使用了较旧的方法。因此,在决定使用哪种方法之前,值得同时查看两者。 - C-F

5

现在有一个Resize Observer可用。

您可以像这样使用它:

const resizeObserver = new ResizeObserver((entries) => {
  entries.forEach(console.log);
})
resizeObserver.observe(document.getElementById("ExampleElement"));

这应该是正确的答案。谢谢。 - mXaln

4
这个怎么样?
divH = divW = 0;
jQuery(document).ready(function(){
    divW = jQuery("div").width();
    divH = jQuery("div").height();
});
function checkResize(){
    var w = jQuery("div").width();
    var h = jQuery("div").height();
    if (w != divW || h != divH) {
        /*what ever*/
        divH = h;
        divW = w;
    }
}
jQuery(window).resize(checkResize);
var timer = setInterval(checkResize, 1000);

顺便提一下,建议给这个div添加一个id,并将$("div")改为$("#yourid"),这样会更快,而且当您添加其他div时也不会出错。


当然 - 那只是一个例子 - 我当然会使用id和class来选择我的元素。我喜欢你的想法 - 我认为它会起作用!非常感谢。我会在几分钟内尝试并给出反馈。 - TJR
没错!这就是我的项目中的问题。DIV元素位于一个外部包装器中,该包装器具有CSS属性Overflow:hidden——因此窗口大小不会改变。但是在其他方面,示例将正常工作——我刚刚测试过了。 - TJR
@Timo:$(window).resize 监听浏览器窗口的物理调整,例如拖动右下角。无论是否使用 overflow:hidden,都没有任何 DOM 事件会触发此事件。这个答案并没有解决你的问题。最好是在计时器中运行此代码,而不是附加到可能永远不会被触发的随机事件侦听器上。如果您可以使用 DOM 属性监视并回退到计时器(或在 div 大小可能更改的位置手动引发某些事件),那就更好了。 - David Hedlund
添加了 setInterval 的代码。你需要对其进行微调,因为1000毫秒可能太多了,另一方面,一个过低的数值很容易让你的CPU负荷过重。如果你知道你不再需要监听了,那就调用 clearInterval(timer)! - Gavriel
是的,它确实会起作用。这就是我刚才做的方式,但我不喜欢这个解决方案。我不知道调整大小事件是如何工作的,但每隔x秒使用setTimeout来重新调用函数似乎会影响性能。 - TJR
显示剩余2条评论

2

1
不,这正是 OP 尝试过的,并指出它不起作用。 - David Hedlund
如果您多次使用setTimeout,它可能会非常昂贵。 - jcubic

0

document.addEventListener('transitionend', function(e) {
  if ($(e.target).is("div")) {
    $("div").text("width: "+$("div").width());
  }
});
$("div").css({"width":"150px"});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="width: 100px;transition-delay: 0.000000001s;">width: 100</div>


欢迎来到 Stack Overflow _ “好的答案”还包括一两句简单的文本,解释为什么您认为您的解决方案会起作用。请访问 SO 帮助中心,特别是此指南,以获取更多详细信息 >>> stackoverflow.com/help/how-to-answer - inputforcolor

0
一个非常简单的实现。

    <script>
    var move = function(e) {
        if ((e.w && e.w !== e.offsetWidth) || (e.h && e.h !== e.offsetHeight)) {
            new Function(e.getAttribute('onresize')).call(e);
        }
        e.w = e.offsetWidth;
        e.h = e.offsetHeight;
    }
    var resize = function() {
        console.log('Resized')
    }
    </script>

    <style>
    .resizable {
        resize: both;
        overflow: auto;
        width: 200px;
        border: 1px solid black;
        padding: 20px;
    }
    </style>

    <div class='resizable' onresize="resize(this)" onmousemove="move(this)">
      Pure vanilla implementation
    </div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接