前言

大家都知道知道原生的 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。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如有疑问大家可以留言交流,谢谢大家对的支持。
# scroll
# resize
# js监听resize
# js监听scroll事件
# JS如何监听div的resize事件详解
# 移除
# 变大
# 回调
# 滚动条
# 可以实现
# 这是
# 发生了
# 好了
# 大家都
# 如有
# 什么时候
# 两种
# 可以用
# 不是很
# 并在
# 我们可以
# 对其
# 如果没有
# 会让
# 可以看到
相关文章:
如何零基础开发自助建站系统?完整教程解析
如何自定义建站之星模板颜色并下载新样式?
Thinkphp 中 distinct 的用法解析
高防服务器租用首荐平台,企业级优惠套餐快速部署
如何快速启动建站代理加盟业务?
如何在IIS管理器中快速创建并配置网站?
高端网站建设与定制开发一站式解决方案 中企动力
如何在宝塔面板创建新站点?
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
已有域名和空间,如何快速搭建网站?
如何选择CMS系统实现快速建站与SEO优化?
黑客如何利用漏洞与弱口令入侵网站服务器?
建站之星上传入口如何快速找到?
建站之星后台管理如何实现高效配置?
如何配置WinSCP新建站点的密钥验证步骤?
在线ppt制作网站有哪些,请推荐几个好的课件下载的网站?
C++ static_cast和dynamic_cast区别_C++静态转换与动态类型安全转换
,有什么在线背英语单词效率比较高的网站?
,石家庄四十八中学官网?
如何在阿里云高效完成企业建站全流程?
如何配置支付宝与微信支付功能?
建站主机如何选?高性价比方案全解析
公司门户网站制作流程,华为官网怎么做?
,sp开头的版面叫什么?
定制建站流程解析:需求评估与SEO优化功能开发指南
安徽网站建设与外贸建站服务专业定制方案
建站之星安装路径如何正确选择及配置?
建站ABC备案流程中有哪些关键注意事项?
模具网站制作流程,如何找模具客户?
深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?
如何通过FTP空间快速搭建安全高效网站?
如何在阿里云域名上完成建站全流程?
5种Android数据存储方式汇总
无锡营销型网站制作公司,无锡网选车牌流程?
营销式网站制作方案,销售哪个网站招聘效果最好?
北京的网站制作公司有哪些,哪个视频网站最好?
如何通过WDCP绑定主域名及创建子域名站点?
如何选择服务器才能高效搭建专属网站?
电脑免费海报制作网站推荐,招聘海报哪个网站多?
网站制作壁纸教程视频,电脑壁纸网站?
攀枝花网站建设,攀枝花营业执照网上怎么年审?
清除minerd进程的简单方法
商务网站制作工程师,从哪几个方面把握电子商务网站主页和页面的特色设计?
如何批量查询域名的建站时间记录?
代购小票制作网站有哪些,购物小票的简要说明?
网站制作哪家好,cc、.co、.cm哪个域名更适合做网站?
宝塔面板创建网站无法访问?如何快速排查修复?
GML (Geography Markup Language)是什么,它如何用XML来表示地理空间信息?
上海网站制作网页,上海本地的生活网站有哪些?最好包括生活的各个方面的?
定制建站模板如何实现SEO优化与智能系统配置?18字教程
*请认真填写需求信息,我们会在24小时内与您取得联系。