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

当前回答

这几乎是顶部答案的精确副本,但不是链接,它只是重要的代码的一部分,翻译成IMO更易读,更容易理解。其他一些小的改变包括使用cloneNode(),和不把html放入js字符串。很小的东西,但是你可以复制粘贴它就可以了。

它的工作方式是让两个看不见的div填充你正在观看的元素,然后在每个div中设置一个触发器,并设置一个滚动位置,如果大小发生变化,将导致触发滚动变化。

所有真正的荣誉都属于Marc J,但如果你只是在寻找相关的代码,这里是:

    window.El = {}

    El.resizeSensorNode = undefined;
    El.initResizeNode = function() {
        var fillParent = "display: block; position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;";
        var triggerStyle = "position: absolute; left: 0; top: 0; transition: 0s;";

        var resizeSensor = El.resizeSensorNode = document.createElement("resizeSensor");
        resizeSensor.style = fillParent;

        var expandSensor = document.createElement("div");
        expandSensor.style = fillParent;
        resizeSensor.appendChild(expandSensor);

        var trigger = document.createElement("div");
        trigger.style = triggerStyle;
        expandSensor.appendChild(trigger);

        var shrinkSensor = expandSensor.cloneNode(true);
        shrinkSensor.firstChild.style = triggerStyle + " width: 200%; height: 200%";
        resizeSensor.appendChild(shrinkSensor);
    }


    El.onSizeChange = function(domNode, fn) {
        if (!domNode) return;
        if (domNode.resizeListeners) {
            domNode.resizeListeners.push(fn);
            return;
        }

        domNode.resizeListeners = [];
        domNode.resizeListeners.push(fn);

        if(El.resizeSensorNode == undefined)
            El.initResizeNode();

        domNode.resizeSensor = El.resizeSensorNode.cloneNode(true);
        domNode.appendChild(domNode.resizeSensor);

        var expand = domNode.resizeSensor.firstChild;
        var expandTrigger = expand.firstChild;
        var shrink = domNode.resizeSensor.childNodes[1];

        var reset = function() {
            expandTrigger.style.width = '100000px';
            expandTrigger.style.height = '100000px';

            expand.scrollLeft = 100000;
            expand.scrollTop = 100000;

            shrink.scrollLeft = 100000;
            shrink.scrollTop = 100000;
        };

        reset();

        var hasChanged, frameRequest, newWidth, newHeight;
        var lastWidth = domNode.offsetWidth;
        var lastHeight = domNode.offsetHeight;


        var onResized = function() {
            frameRequest = undefined;

            if (!hasChanged) return;

            lastWidth = newWidth;
            lastHeight = newHeight;

            var listeners = domNode.resizeListeners;
            for(var i = 0; listeners && i < listeners.length; i++) 
                listeners[i]();
        };

        var onScroll = function() {
            newWidth = domNode.offsetWidth;
            newHeight = domNode.offsetHeight;
            hasChanged = newWidth != lastWidth || newHeight != lastHeight;

            if (hasChanged && !frameRequest) {
                frameRequest = requestAnimationFrame(onResized);
            }

            reset();
        };


        expand.addEventListener("scroll", onScroll);
        shrink.addEventListener("scroll", onScroll);
    }

其他回答

纯Javascript解决方案,但只有当元素用css resize按钮调整大小时才能工作:

使用offsetWidth和offsetHeight存储元素大小; 在这个元素上添加一个onclick事件监听器; 当触发时,比较当前的offsetWidth和offsetHeight与存储的值,如果不同,做你想做的,并更新这些值。

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

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']}); #div {width: 100px; border: 1px solid #bbb; resize: both; overflow: hidden;} <div id = "div">DIV</div>

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

下面是@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();