前言
大家都知道知道原生的 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 结构:
<div class="resize-triggers"> <div class="expand-trigger"> <div></div> </div> <div class="contract-trigger"></div> </div>
他们对应的 CSS 如下:
.resize-triggers { visibility: hidden; opacity: 0; } .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%; }
其中 .expand-triggers
的子元素宽高应当保持大于父元素 1px,且两个触发器都应当保持在最右下角的状态,因此我们可以实现如下的状态重置函数,并在初始化和每次滚动事件的时候调用:
/** * 重置触发器 * @param element 要处理的元素 */ const resetTrigger = function(element) { const trigger = element.__resizeTrigger__; // 要重置的触发器 const expand = trigger.firstElementChild; // 第一个子元素,用来监听变大 const contract = trigger.lastElementChild; // 最后一个子元素,用来监听变小 const expandChild = expand.firstElementChild; // 第一个子元素的第一个子元素,用来监听变大 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; // 滚动到最右 };
我们可以用如下函数检测元素大小是否发生了改变:
/** * 检测触发器状态 * @param element 要检查的元素 * @returns {boolean} 是否改变了大小 */ const checkTriggers = function(element) { // 宽度或高度不一致就返回true return element.offsetWidth !== element.__resizeLast__.width || element.offsetHeight !== element.__resizeLast__.height; };
最终,我们可以实现简单的事件监听的添加:
/** * 添加大小更改监听 * @param element 要监听的元素 * @param fn 回调函数 */ export const addResizeListener = function(element, fn) { if (isServer) return; // 服务器端直接返回 if (attachEvent) { // 处理低版本ie element.attachEvent('onresize', fn); } else { if (!element.__resizeTrigger__) { // 如果没有触发器 if (getComputedStyle(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"><div></div></div><div class="contract-trigger"></div>'; element.appendChild(resizeTrigger); // 添加触发器 resetTrigger(element); // 重置触发器 element.addEventListener('scroll', scrollListener, true); // 监听滚动事件 /* Listen for a css animation to detect element display/re-attach */ // 监听CSS动画来检测元素显示或者重新添加 if (animationStartEvent) { // 动画开始 resizeTrigger.addEventListener(animationStartEvent, function(event) { // 增加动画开始的事件监听 if (event.animationName === RESIZE_ANIMATION_NAME) { // 如果是大小改变事件 resetTrigger(element); // 重置触发器 } }); } } element.__resizeListeners__.push(fn); // 加入该回调 } };
以及如下的函数来移除事件监听:
/** * 移除大小改变的监听 * @param element 被监听的元素 * @param fn 对应的回调函数 */ export const removeResizeListener = function(element, fn) { if (attachEvent) { // 处理ie element.detachEvent('onresize', fn); } else { element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); // 移除对应的回调函数 if (!element.__resizeListeners__.length) { // 如果全部时间被移除 element.removeEventListener('scroll', scrollListener); // 移除滚动监听 element.__resizeTrigger__ = !element.removeChild(element.__resizeTrigger__); // 移除对应的触发器,但保存下来 } } };
其他
其中有部分内容是用来优化的,并不影响基础功能,如对服务器渲染、客户端渲染的区分,对 IE 的特殊处理,以及通过 opacity 的动画来解决 chrome 上的bug。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如有疑问大家可以留言交流,谢谢大家对的支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新日志
- 房东的猫2017-房东的猫[科文音像][WAV+CUE]
- 杨乃文.2016-离心力(引进版)【亚神音乐】【WAV+CUE】
- 群星.2024-珠帘玉幕影视原声带【TME】【FLAC分轨】
- 芝麻龙眼.2008-光阴隧道民歌记录3CD【乡城】【WAV+CUE】
- 谭艳《再度重相逢HQII》头版限量[低速原抓WAV+CUE][549M]
- ABC唱片《蔡琴三十周年纪念版》6N纯银镀膜 [WAV+CUE][1.1G]
- 海来阿木《西楼情歌》开盘母带[WAV+CUE][1.1G]
- TheGesualdoSix-QueenofHeartsLamentsandSongsofRegretforQueensTerrestrialandCele
- 王建杰2011-荣华富贵[喜玛拉雅][WAV+CUE]
- 孙悦2024-时光音乐会[金蜂][WAV+CUE]
- 秦宇子.2020-#YUZI【海蝶】【FLAC分轨】
- 苏有朋.1994-这般发生【华纳】【WAV+CUE】
- 小虎队.1990-红蜻蜓【飞碟】【WAV+CUE】
- 雷婷《寂寞烟火HQⅡ》头版限量[低速原抓WAV+CUE][1G]
- 赵传1996《黑暗英雄》台湾首版[WAV+CUE][1G]