在进行H5开发时,有时候需要单独监听一个网页元素的尺寸变化事件,譬如:div的事件,笔者尝试通过如下代码添加监听,结果没有反应:
someDiv.addEventListener('resize', function(event) {
console.log('div resize');
});
没有反应的原因是:
DIV元素不像window对象,是没有resize事件的,我们无法直接监听DIV的resize事件。
新的浏览器版本已经发布了一个 ResizeObserver
类用来监听网页元素的尺寸变化事件, 但由于该类需要在比较新的浏览器里才能用,在旧一点的浏览器中无法使用。ResizeObserver
要求的Chrome版本至少是 64,在老的浏览器中不能用。
为了解决这个问题,笔者采用间接方法,即通过在要监听尺寸变化的DIV对象中创建一个可以支持resize事件监听的object(实际是iframe)来间接转发resize事件。
本代码短小,可以很方便的复制粘贴到需要的项目中,没有必要独立成一个js模块,兼容jQuery。
具体实现代码如下:
var ResizeHandler = {
//@private method
_onResize: function (e) {
var ele = e.target || e.srcElement;
var trigger = ele.__resizeTrigger__;
if (trigger) {
var listeners = trigger.__z_resizeListeners;
if (listeners) {
var size = listeners.length;
for (var i = 0; i < size; i++) {
var h = listeners[i];
var handler = h.handler;
var context = h.context;
if (!context) context = trigger;
if (handler) {
handler.apply(context, [e]);
}
else if(e.type === 'resize') {
var event = document.createEvent('Event');
event.initEvent('resize', false, false);
trigger.dispatchEvent(event);
}
}
}
}
},
//@public method
addElement : function (ele, handler, context) {
var listeners = ele.__z_resizeListeners;
if (!listeners) {
listeners = [];
ele.__z_resizeListeners = listeners;
if (getComputedStyle(ele, null).position === 'static') {
ele.style.position = 'relative';
}
// create resize Detector object
var obj = document.createElement('object');
obj.resizer = this;
obj.__resizeElement__ = ele;
obj.setAttribute('style','display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; opacity: 0; pointer-events: none; z-index: -9999;');
obj.type = 'text/html';
obj.data = 'about:blank';
obj.onload = function (evt) {
this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__;
this.contentDocument.defaultView.addEventListener('resize', this.resizer._onResize);
};
ele.appendChild(obj);
ele.__resizeDetector__ = obj;
}
listeners.push({
handler: handler,
context: context
});
},
removeElement : function (ele, handler, context) {
var listeners = ele.__z_resizeListeners;
if (listeners) {
var size = listeners.length;
for (var i = 0; i < size; i++) {
var h = listeners[i];
if (h.handler === handler && h.context === context) {
listeners.splice(i, 1);
break;
}
}
if (listeners.length === 0) {
var detector = ele.__resizeDetector__;
if (detector) {
detector.contentDocument.defaultView.removeEventListener('resize', this._onResize);
ele.removeChild(detector);
delete ele.__resizeDetector__;
}
delete ele.__z_resizeListeners;
}
}
}
};
方法说明如下:
addElement : function (ele, handler, context)
removeElement : function (ele, handler, context)
参数说明
ele 为要添加resize事件的元素,不能省略,必须是实际存在的HTML dom对象。
handler 为事件处理器回调,如果省略,则分派消息,即通过 对元素调用 addEventListener 来添加事件回调,否者之间调用指定的回调;
context:为回调函数的this指针,如果省略,则为元素dom对象
1)通过 ResizeHandler 的 addElement 方法可以添加一个元素与其对应的resize 事件处理回调;
2)通过 removeElement 方法移除需要监听的元素的事件与回调;
3)可以添加无限个元素/事件处理函数 对
4)一个元素也可以添加多个事件处理回调
其中 context 参数为 回调函数的this 指针绑定的对象,如果没有提供,则回调函数的this指针就是元素本身。
注意:如果在调用 addElement 函数时不给与处理函数handler参数 ,则会向该元素分派resize事件,这样后续就可以通过元素的 addEventListener('resize' , xxx) 来添加事件处理函数了,这种方式也可以支持jQuery的 .on('resize', xxx(event)) 方法来监听该元素的resize事件了。
调用的样本代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Resize Demo</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#resizeDiv {
width: 60%;
height: 60%;
border: 1px solid red;
margin: 20px;
position:relative;
}
button {
margin: 20px 20px 0;
}
.resizeInner {
width: 100%;
height: 100%;
left: 0;
top: 0;
position: absolute;
}
</style>
<script>
var ResizeHandler = {
//@private method
_onResize: function (e) {
var ele = e.target || e.srcElement;
var trigger = ele.__resizeTrigger__;
if (trigger) {
var listeners = trigger.__z_resizeListeners;
if (listeners) {
var size = listeners.length;
for (var i = 0; i < size; i++) {
var h = listeners[i];
var handler = h.handler;
var context = h.context;
if (!context) context = trigger;
if (handler) {
handler.apply(context, [e]);
}
else if(e.type === 'resize') {
var event = document.createEvent('Event');
event.initEvent('resize', false, false);
trigger.dispatchEvent(event);
}
}
}
}
},
//@public method
addElement : function (ele, handler, context) {
var listeners = ele.__z_resizeListeners;
if (!listeners) {
listeners = [];
ele.__z_resizeListeners = listeners;
if (getComputedStyle(ele, null).position === 'static') {
ele.style.position = 'relative';
}
// create resize Detector object
var obj = document.createElement('object');
obj.resizer = this;
obj.__resizeElement__ = ele;
obj.setAttribute('style','display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; opacity: 0; pointer-events: none; z-index: -9999;');
obj.type = 'text/html';
obj.data = 'about:blank';
obj.onload = function (evt) {
this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__;
this.contentDocument.defaultView.addEventListener('resize', this.resizer._onResize);
};
ele.appendChild(obj);
ele.__resizeDetector__ = obj;
}
listeners.push({
handler: handler,
context: context
});
},
removeElement : function (ele, handler, context) {
var listeners = ele.__z_resizeListeners;
if (listeners) {
var size = listeners.length;
for (var i = 0; i < size; i++) {
var h = listeners[i];
if (h.handler === handler && h.context === context) {
listeners.splice(i, 1);
break;
}
}
if (listeners.length === 0) {
var detector = ele.__resizeDetector__;
if (detector) {
detector.contentDocument.defaultView.removeEventListener('resize', this._onResize);
ele.removeChild(detector);
delete ele.__resizeDetector__;
}
delete ele.__z_resizeListeners;
}
}
}
};
</script>
</head>
<body>
<button onclick="addElement()">addElement</button>
<button onclick="removeElement()">removeElement</button>
<button onclick="addEvent()">addEvent</button>
<button onclick="removeEvent()">removeEvent</button>
<button onclick="resize()">Do Resize</button>
<div id="resizeDiv">
<div class="resizeInner" id="resizeInner">
</div>
</div>
<script>
var resizeDiv = document.getElementById('resizeDiv');
var oldSize = 60;
function resize() {
oldSize += 10;
resizeDiv.style.width = oldSize + '%';
if (oldSize >= 90) {
oldSize = 40;
}
}
var onElementResize = function (e) {
console.log("resize");
alert(this.id);
};
function addElement() {
var innerDiv = resizeDiv.querySelector('.resizeInner');
// 这种方式只能通过传入的handler参数 onElementResize 来监听事件,无法通过 addEventListener 方式 来添加事件处理函数
ResizeHandler.addElement(innerDiv, onElementResize);
}
function removeElement() {
var innerDiv = resizeDiv.querySelector('.resizeInner');
ResizeHandler.removeElement(innerDiv, onElementResize)
}
// 如果不给与 handler 参数,则可以通过给元素添加 addEventListener('resize',fn) 来监听
// jQuery 可以通过 on/off 来添加或删除元素的 resize 事件处理函数
function addEvent() {
var innerDiv = resizeDiv.querySelector('.resizeInner');
ResizeHandler.addElement(innerDiv);
}
function removeEvent() {
var innerDiv = resizeDiv.querySelector('.resizeInner');
ResizeHandler.removeElement(innerDiv)
}
var innerDiv = resizeDiv.querySelector('.resizeInner');
innerDiv.addEventListener('resize', function(event) {
console.log('resize event trigger');
});
</script>
</body>
</html>