首頁  >  文章  >  web前端  >  JS用scroll監聽resize的圖文實例

JS用scroll監聽resize的圖文實例

零下一度
零下一度原創
2017-06-17 10:34:441172瀏覽

這篇文章主要為大家介紹了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 + &#39;px&#39;; // 保持宽度多1px
 expandChild.style.height = expand.offsetHeight + 1 + &#39;px&#39;; // 保持高度多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(&#39;onresize&#39;, fn);
 } else {
if (!element.resizeTrigger) { // 如果没有触发器
  if (getComputedStyle(element).position === &#39;static&#39;) {
  element.style.position = &#39;relative&#39;; // 将static改为relative
  }
createStyles();
  element.resizeLast = {}; // 初始化触发器最后的状态
  element.resizeListeners = []; // 初始化触发器的监听器

  const resizeTrigger = element.resizeTrigger = document.createElement(&#39;p&#39;); // 创建触发器
  resizeTrigger.className = &#39;resize-triggers&#39;;
  resizeTrigger.innerHTML = &#39;<p class="expand-trigger"><p></p></p><p class="contract-trigger"></p>&#39;;
  element.appendChild(resizeTrigger); // 添加触发器

  resetTrigger(element); // 重置触发器
  element.addEventListener(&#39;scroll&#39;, 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(&#39;onresize&#39;, fn);
 } else {
 element.resizeListeners.splice(element.resizeListeners.indexOf(fn), 1); // 移除对应的回调函数
 if (!element.resizeListeners.length) { // 如果全部时间被移除
  element.removeEventListener(&#39;scroll&#39;, scrollListener); // 移除滚动监听
  element.resizeTrigger = !element.removeChild(element.resizeTrigger); // 移除对应的触发器,但保存下来
 }
 }
};

其他

其中有部分內容是用來最佳化的,並不影響基礎功能,如對伺服器渲染、客戶端渲染的區分,對IE 的特殊處理,以及透過opacity 的動畫來解決chrome 上的bug。

以上是JS用scroll監聽resize的圖文實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn