原文地址: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時,可能會連續快速的觸發(比如拖動瀏覽器),為了提高效率,可以考慮使用批處理的模式。