原文地址:http://zhangyiheng.com/blog/articles/div_resize.html
需求
开发过程中经常遇到的一个问题就是如何监听一个div的size变化。
比如我用canvas绘制了一个chart,当canvas的size发生变化的时候,需要重新绘制里面的内容,这个时候就需要监听resize事件做处理。
window上虽然可以添加resize事件监听,但这并不能满足我们的需求,因为很多时候,div的size发生了变化,但是window的size并没有改变。
不过我们可以间接利用window的resize事件监听来实现对于某个div的resize事件监听,请看下面具体实现。
对于div的resize事件的监听,实现方式有很多,比如周期性检查,通过scroll事件等等,本文主要介绍通过object元素来实现监听。
具体实现
1 /** 2 * Created by taozh on 2017/5/6. 3 * taozh1982@gmail.com 4 */ 5 var EleResize = { 6 _handleResize: function (e) { 7 var ele = e.target || e.srcElement; 8 var trigger = ele.__resizeTrigger__; 9 if (trigger) { 10 var handlers = trigger.__z_resizeListeners; 11 if (handlers) { 12 var size = handlers.length; 13 for (var i = 0; i < size; i++) { 14 var h = handlers[i]; 15 var handler = h.handler; 16 var context = h.context; 17 handler.apply(context, [e]); 18 } 19 } 20 } 21 }, 22 _removeHandler: function (ele, handler, context) { 23 var handlers = ele.__z_resizeListeners; 24 if (handlers) { 25 var size = handlers.length; 26 for (var i = 0; i < size; i++) { 27 var h = handlers[i]; 28 if (h.handler === handler && h.context === context) { 29 handlers.splice(i, 1); 30 return; 31 } 32 } 33 } 34 }, 35 _createResizeTrigger: function (ele) { 36 var obj = document.createElement('object'); 37 obj.setAttribute('style', 38 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden;opacity: 0; pointer-events: none; z-index: -1;'); 39 obj.onload = EleResize._handleObjectLoad; 40 obj.type = 'text/html'; 41 ele.appendChild(obj); 42 obj.data = 'about:blank'; 43 return obj; 44 }, 45 _handleObjectLoad: function (evt) { 46 this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__; 47 this.contentDocument.defaultView.addEventListener('resize', EleResize._handleResize); 48 } 49 }; 50 if (document.attachEvent) {//ie9-10 51 EleResize.on = function (ele, handler, context) { 52 var handlers = ele.__z_resizeListeners; 53 if (!handlers) { 54 handlers = []; 55 ele.__z_resizeListeners = handlers; 56 ele.__resizeTrigger__ = ele; 57 ele.attachEvent('onresize', EleResize._handleResize); 58 } 59 handlers.push({ 60 handler: handler, 61 context: context 62 }); 63 }; 64 EleResize.off = function (ele, handler, context) { 65 var handlers = ele.__z_resizeListeners; 66 if (handlers) { 67 EleResize._removeHandler(ele, handler, context); 68 if (handlers.length === 0) { 69 ele.detachEvent('onresize', EleResize._handleResize); 70 delete ele.__z_resizeListeners; 71 } 72 } 73 } 74 } else { 75 EleResize.on = function (ele, handler, context) { 76 var handlers = ele.__z_resizeListeners; 77 if (!handlers) { 78 handlers = []; 79 ele.__z_resizeListeners = handlers; 80 81 if (getComputedStyle(ele, null).position === 'static') { 82 ele.style.position = 'relative'; 83 } 84 var obj = EleResize._createResizeTrigger(ele); 85 ele.__resizeTrigger__ = obj; 86 obj.__resizeElement__ = ele; 87 } 88 handlers.push({ 89 handler: handler, 90 context: context 91 }); 92 }; 93 EleResize.off = function (ele, handler, context) { 94 var handlers = ele.__z_resizeListeners; 95 if (handlers) { 96 EleResize._removeHandler(ele, handler, context); 97 if (handlers.length === 0) { 98 var trigger = ele.__resizeTrigger__; 99 if (trigger) { 100 trigger.contentDocument.defaultView.removeEventListener('resize', EleResize._handleResize); 101 ele.removeChild(trigger); 102 delete ele.__resizeTrigger__; 103 } 104 delete ele.__z_resizeListeners; 105 } 106 } 107 } 108 }
测试代码:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Resize</title> <script src="./EleResize.js"></script> <style> html, body { margin: 0; padding: 0; width: 100%; height: 100%; } #resizeDiv { width: 60%; height: 60%; border: 1px solid red; margin: 20px; } button { margin: 20px 20px 0; } </style> </head> <body> <button onclick="addListener()">addListener</button> <button onclick="removeListener()">removeListener</button> <button onclick="resize()">resize</button> <div id="resizeDiv"></div> <script> var resizeDiv = document.getElementById('resizeDiv'); function resize() { resizeDiv.style.width = "200px"; } var listener = function () { console.log("resize"); }; function addListener() { EleResize.on(resizeDiv, listener); } function removeListener() { EleResize.off(resizeDiv, listener) } </script> </body> </html>
原理
这里的具体实现分两类,
- ie9-10
默认支持div的resize事件,可以直接通过div.attachEvent('onresize', handler);的方式实现
- 其它浏览器
通过在div中添加一个内置object元素实现监听。
- 设置object元素的style使其填充满div,这样当div的size发生变化时,object的size也会发生变化。
- 然后监听object元素的contentDocument.defaultView(window对象)的resize事件。
注:本文提供的是如何监听resize事件,其实在resize时,可能会连续快速的触发(比如拖动浏览器),为了提高效率,可以考虑使用批处理的模式。