我有下面的样本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>

当前回答

下面是@nkron的简化版本的解决方案,适用于单个元素(而不是@nkron的答案中的元素数组,我不需要复杂性)。

function onResizeElem(element, callback) {    
  // Save the element we are watching
  onResizeElem.watchedElementData = {
    element: element,
    offsetWidth: element.offsetWidth,
    offsetHeight: element.offsetHeight,
    callback: callback
  };

  onResizeElem.checkForChanges = function() {
    const data = onResizeElem.watchedElementData;
    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 resize event
  window.addEventListener('resize', onResizeElem.checkForChanges);

  // Listen to the element being checked for width and height changes
  onResizeElem.observer = new MutationObserver(onResizeElem.checkForChanges);
  onResizeElem.observer.observe(document.body, {
    attributes: true,
    childList: true,
    characterData: true,
    subtree: true
  });
}

事件监听器和观察者可以通过以下方式删除:

window.removeEventListener('resize', onResizeElem.checkForChanges);
onResizeElem.observer.disconnect();

其他回答

从长远来看,您将能够使用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
  });
};

我不建议使用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被调整大小的每一毫秒触发一次。

有一种非常有效的方法来确定元素的大小是否已经改变。

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

这个库有一个resizessensor类,可以用于调整大小检测。它使用基于事件的方法,所以它非常快,而且不会浪费CPU时间。

例子:

new ResizeSensor(jQuery('#divId'), function(){ 
    console.log('content dimension changed');
});

请不要使用jQuery onresize插件,因为它使用setTimeout()结合在循环中读取DOM clienttheight /clientWidth属性来检查更改。这是令人难以置信的缓慢和不准确,因为它会导致布局抖动。

披露:我与这个库直接相关。

当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>

通过@gman扩展这个答案,这里有一个允许每个元素多次回调的函数,将宽度和高度分解为一个准事件对象。请参阅关于堆栈溢出的嵌入式演示(您可能需要彻底调整主浏览器的大小以触发它)

function elementResizeWatcher(element, callback) { var resolve=function(element) { return (typeof element==='string' ? document[ ['.','#'].indexOf(element.charAt(0)) < 0 ? "getElementById" : "querySelector" ] (element) : element); }, observer, watched = [], checkForElementChanges = function (data) { var w=data.el.offsetWidth,h=data.el.offsetHeight; if ( data.offsetWidth !== w || data.offsetHeight !== h ) { data.offsetWidth = w; data.offsetHeight = h; data.cb({ target : data.el, width : w, height : h }); } }, checkForChanges=function(){ watched.forEach(checkForElementChanges); }, started=false, self = { start: function () { if (!started) { // Listen to the window resize event window.addEventListener("resize", checkForChanges); // Listen to the element being checked for width and height changes observer = new MutationObserver(checkForChanges); observer.observe(document.body, { attributes: true, childList: true, characterData: true, subtree: true }); started=true; } }, stop : function ( ) { if (started) { window.removeEventListener('resize', checkForChanges); observer.disconnect(); started = false; } }, addListener : function (element,callback) { if (typeof callback!=='function') return; var el = resolve(element); if (typeof el==='object') { watched.push({ el : el, offsetWidth : el.offsetWidth, offsetHeight : el.offsetHeight, cb : callback }); } }, removeListener : function (element,callback) { var el = resolve(element); watched = watched.filter(function(data){ return !((data.el===el) && (data.cb===callback)); }); } }; self.addListener(element,callback); self.start(); return self; } var watcher = elementResizeWatcher("#resize_me_on_stack_overflow", function(e){ e.target.innerHTML="i am "+e.width+"px&nbsp;x&nbsp;"+e.height+"px"; }); watcher.addListener(".resize_metoo",function(e) { e.target.innerHTML="and i am "+e.width+"px&nbsp;x&nbsp;"+e.height+"px"; }); var mainsize_info = document.getElementById("mainsize"); watcher.addListener(document.body,function(e) { mainsize_info.innerHTML=e.width+"px&nbsp;x&nbsp;"+e.height+"px"; }); #resize_me_on_stack_overflow{ background-color:lime; } .resize_metoo { background-color:yellow; font-size:36pt; width:50%; } <p> resize the main browser window! <span id="mainsize"><span> </p> <p id="resize_me_on_stack_overflow"> hey, resize me. </p> <p class="resize_metoo"> resize me too. </p>