• 技术文章 >web前端 >js教程

    JS用scroll监听resize的图文实例

    零下一度零下一度2017-06-17 10:34:44原创664
    这篇文章主要给大家介绍了JS奇技之利用scroll来监听resize的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。

    前言

    大家都知道知道原生的 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 结构:


    <p class="resize-triggers">
     <p class="expand-trigger">
     <p></p>
     </p>
     <p class="contract-trigger"></p>
    </p>

    他们对应的 CSS 如下:


    .resize-triggers {
     visibility: hidden;
     opacity: 0;
    }
    
    .resize-triggers,
    .resize-triggers > p,
    .contract-trigger:before {
     content: " ";
     display: block;
     position: absolute;
     top: 0;
     left: 0;
     height: 100%;
     width: 100%;
     overflow: hidden;
    }
    
    .resize-triggers > p {
     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('p'); // 创建触发器
      resizeTrigger.className = 'resize-triggers';
      resizeTrigger.innerHTML = '<p class="expand-trigger"><p></p></p><p class="contract-trigger"></p>';
      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。

    以上就是JS用scroll监听resize的图文实例的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    上一篇:总结一篇react-router JS 控制路由跳转的实例教程 下一篇:详细介绍package.json文件配置
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【活动】充值PHP中文网VIP即送云服务器• 如何上手Angular,先从 8 个开源项目开始!• Node.js怎么实现分片上传?方法介绍• 一文了解JavaScript栈• JavaScript字典与集合(总结分享)• 怎么利用node生成word文档?使用库分享
    1/1

    PHP中文网