中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JS奇技之利用scroll來監聽resize詳解

發布時間:2020-09-19 12:35:09 來源:腳本之家 閱讀:185 作者:Liril 欄目:web開發

前言

大家都知道知道原生的 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 來判斷,其圖示如下:

JS奇技之利用scroll來監聽resize詳解

監聽元素變小

當元素變小的時候,可滾動區域會變大,滾動條的位置其實并不會進行改變,這里采取的做法是,讓可滾動區域和父元素成一定的比例一起縮小,讓父元素來擠壓滾動區域,從而間接改變滾動條 scrollLeft 或者 scrollTop 的大小,文字描述可能不是很清楚,我們看下圖:

JS奇技之利用scroll來監聽resize詳解

通過以上兩種方式,我們可以就可以獲得 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。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如有疑問大家可以留言交流,謝謝大家對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武宁县| 青冈县| 视频| 南丹县| 木兰县| 樟树市| 札达县| 修文县| 开平市| 泸溪县| 乌兰县| 城固县| 二连浩特市| 门源| 株洲市| 丰都县| 钟祥市| 汉沽区| 天等县| 和静县| 海盐县| 张家川| 扶风县| 阜城县| 盱眙县| 黄石市| 道孚县| 泾阳县| 开远市| 罗甸县| 禄丰县| 南漳县| 绍兴市| 鄂伦春自治旗| 合山市| 青田县| 顺义区| 绥芬河市| 青河县| 和平区| 怀化市|