前言
大家都知道知道原生的 resize 事件只能作用于 defaultview 即 window 上,那么我们应该通过什么样的方式来监听其他元素的大小改变呢?笔者最近学习发现了一种神奇的方法,通过 scroll 事件来间接实现 resize 事件的监听,本文将对这种方式进行原理的剖析与代码实现。
原理
首先,我们先来看一下 scroll 事件是干嘛的。
The scroll event is fired when the document view or an element has been scrolled.
当文档视图或者元素滚动的时候会触发 scroll 事件。
也就是说元素滚动的时候会触发这个事件,那么什么时候元素会滚动?当元素大于其父级元素,且父级元素允许其滚动的时候,该元素可以进行滚动。换句话说,元素可以滚动意味着父子元素大小不一致,这是这个方法的核心。
那么我们需要让元素大小发生改变时,使得 scrollLeft 或者 scrollTop 发生改变,从而触发 scroll 事件,进一步得知其大小发生了改变。
监听元素变大
元素变大的时候,我们可以看到更多,其内部可滚动区域将慢慢减小,但这并不会造成滚动条位置的改变,但当元素大到让滚动条消失的时候会让 scrollLeft 或者 scrollTop 变成 0,这样我们就知道了元素变大了,因此我们其实只需要 1px 来判断,其图示如下:
监听元素变小
当元素变小的时候,可滚动区域会变大,滚动条的位置其实并不会进行改变,这里采取的做法是,让可滚动区域和父元素成一定的比例一起缩小,让父元素来挤压滚动区域,从而间接改变滚动条 scrollLeft 或者 scrollTop 的大小,文字描述可能不是很清楚,我们看下图:
通过以上两种方式,我们可以就可以获得 resize 事件。
实现
首先,为了不影响原有的元素,我们应当创建一个和要监听元素等大的元素,并对其进行相关操作,然后我们需要两个子元素来分别监听元素变大和元素变小两个情况。因此构造如下的 HTML 结构:
.resize-triggers,.resize-triggers > div,.contract-trigger:before {
content: " ";
display: block;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
.resize-triggers > div {
overflow: auto;
}
.contract-triggers:before {
width: 200%;
height: 200%;
}
rush:js;">
/**
* 重置触发器
* @p
aram element 要处理的元素
*/
const resetTrigger = function(element) {
const trigger = element.__resizeTrigger__; // 要重置的触发器
const expand = trigger.f
irstElementChild; // 第
一个子元素,用来监听变大
const contract = trigger.lastElementChild; // 最后
一个子元素,用来监听变小
const expandChild = expand.f
irstElementChild; // 第
一个子元素的第
一个子元素,用来监听变大
contract.scrollLeft = contract.scrollWidth; // 滚动到最右
contract.scrollTop = contract.scrollHeight; // 滚动到最下
expandChild.style.width = expand.offsetWidth + 1 + 'px'; // 保持宽度多1px
expandChild.style.height = expand.offsetHeight + 1 + 'px'; // 保持高度多1px
expand.scrollLeft = expand.scrollWidth; // 滚动到最右
expand.scrollTop = expand.scrollHeight; // 滚动到最右
};
rush:js;">
/**
* 检测触发器状态
* @p
aram element 要检查的元素
* @returns {boolean} 是否改变了大小
*/
const checkTriggers = function(element) {
// 宽度或高度不一致就返回true
return element.offsetWidth !== element.__resizeLast__.width || element.offsetHeight !== element.__resizeLast__.height;
};
rush:js;">
/**
*
添加大小更改监听
* @p
aram element 要监听的元素
* @p
aram fn 回调
函数
*/
export const addResizeListener = function(element,fn) {
if (isServer) return; // 服务器端直接返回
if (attachEvent) { // 处理低版本ie
element.attachEvent('onresize',fn);
} else {
if (!element.__resizeTrigger__) { // 如果没有触发器
if (getCo
mputedStyle(element).position === 'static') {
element.style.position = 'relative'; // 将static改为relative
}
createStyles();
element.__resizeLast__ = {}; // 初始化触发器最后的状态
element.__resizeListeners__ = []; // 初始化触发器的监听器
const resizeTrigger = element.resizeTrigger = document.createElement('div'); // 创建触发器
resizeTrigger.className = 'resize-triggers';
resizeTrigger.innerHTML = '<div class="expand-trigger">