国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

如何優(yōu)雅監(jiān)聽容器高度變化

hizengzeng / 1327人閱讀

摘要:哈哈哈哈,以上純屬虛構(gòu),不過在最近項(xiàng)目中還真遇到過對容器監(jiān)聽高寬變化在使用或滾動插件,如果容器內(nèi)部元素有高度變化要去及時(shí)更新外部包裹容器,即調(diào)用方法。處理很簡單,只需在動畫停止事件觸發(fā)時(shí)監(jiān)聽高寬變化即可。

前言

老鳥:怎樣去監(jiān)聽 DOM 元素的高度變化呢?
菜鳥:哈哈哈哈哈,這都不知道哦,用 onresize 事件鴨!
老鳥扶了扶眼睛,空氣安靜幾秒鐘,菜鳥才晃過神來。對鴨,普通 DOM 元素沒有 onresize 事件,只有在 window 對象下有此事件,該死,又雙叒叕糗大了。

哈哈哈哈,以上純屬虛構(gòu),不過在最近項(xiàng)目中還真遇到過對容器監(jiān)聽高(寬)變化:在使用 iscrollbetter-scroll 滾動插件,如果容器內(nèi)部元素有高度變化要去及時(shí)更新外部包裹容器,即調(diào)用 refresh() 方法。不然就會造成滾動誤差(滾動不到底部或滾動脫離底部)。

可能我們一般處理思路:

在每次 DOM 節(jié)點(diǎn)有更新(刪除或插入)后就去調(diào)用 refresh(),更新外部容器。

對異步資源(如圖片)加載,使用onload 監(jiān)聽每次加載完成,再去調(diào)用 refresh(),更新外部容器。

這樣我們會發(fā)現(xiàn),如果容器內(nèi)部元素比較復(fù)雜,調(diào)用會越來越繁瑣,甚至還要考慮到用戶使用的每一個(gè)操作都可能導(dǎo)致內(nèi)部元素寬高變化,進(jìn)而要去調(diào)整外部容器,調(diào)用 refresh()

實(shí)際上,不管是對元素的哪種操作,都會造成它的屬性、子孫節(jié)點(diǎn)、文本節(jié)點(diǎn)發(fā)生了變化,如果能能監(jiān)聽得到這種變化,這時(shí)只需比較容器寬高變化,即可實(shí)現(xiàn)對容器寬高的監(jiān)聽,而無需關(guān)系它外部行為。DOM3 Events 規(guī)范為我們提供了 MutationObserver 接口監(jiān)視對 DOM 樹所做更改的能力。

MutationObserver

Mutation Observer API 用來監(jiān)視 DOM 變動。DOM 的任何變動,比如節(jié)點(diǎn)的增減、屬性的變動、文本內(nèi)容的變動,這個(gè) API 都可以得到通知。

PS Mutation Observer API 已經(jīng)有很不多的瀏覽器兼容性,如果對IE10及以下沒有要求的話。

MutationObserver 特點(diǎn)

DOM 發(fā)生變動都會觸發(fā) Mutation Observer 事件。但是,它跟事件還是有不用點(diǎn):事件是同步觸發(fā),DOM 變化立即觸發(fā)相應(yīng)事件;Mutation Observer 是異步觸發(fā),DOM 變化不會馬上觸發(fā),而是等當(dāng)前所有 DOM 操作都結(jié)束后才觸發(fā)??偟膩碚f,特點(diǎn)如下:

它等待所有腳本任務(wù)完成后,才會運(yùn)行(即異步觸發(fā)方式)。

它把 DOM 變動記錄封裝成一個(gè)數(shù)組進(jìn)行處理,而不是一條條個(gè)別處理 DOM 變動。

它既可以觀察 DOM 的所有類型變動,也可以指定只觀察某一類變動。

MutationObserver 構(gòu)造函數(shù)

MutationObserver 構(gòu)造函數(shù)的實(shí)例傳的是一個(gè)回調(diào)函數(shù),該函數(shù)接受兩個(gè)參數(shù),第一個(gè)是變動的數(shù)組,第二個(gè)是觀察器是實(shí)例。

var observer = new MutationObserver(function (mutations, observer){
  mutations.forEach(function (mutaion) {
    console.log(mutation);
  })
})
MutationObserver 實(shí)例的 observe() 方法

observe 方法用來執(zhí)行監(jiān)聽,接受兩個(gè)參數(shù):

第一個(gè)參數(shù),被觀察的 DOM 節(jié)點(diǎn);

第二個(gè)參數(shù),一個(gè)配置對象,指定所要觀察特征。

var $tar = document.getElementById("tar");
var option = {
  childList: true, // 子節(jié)點(diǎn)的變動(新增、刪除或者更改)
  attributes: true, // 屬性的變動
  characterData: true, // 節(jié)點(diǎn)內(nèi)容或節(jié)點(diǎn)文本的變動

  subtree: true, // 是否將觀察器應(yīng)用于該節(jié)點(diǎn)的所有后代節(jié)點(diǎn)
  attributeFilter: ["class", "style"], // 觀察特定屬性
  attributeOldValue: true, // 觀察 attributes 變動時(shí),是否需要記錄變動前的屬性值
  characterDataOldValue: true // 觀察 characterData 變動,是否需要記錄變動前的值
}
mutationObserver.observe($tar, option);

option 中,必須有 childList、attributescharacterData中一種或多種,否則會報(bào)錯(cuò)。其中各個(gè)屬性意思如下:

childList 布爾值,表示是否應(yīng)用到子節(jié)點(diǎn)的變動(新增、刪除或者更改);

attributes 布爾值,表示是否應(yīng)用到屬性的變動;

characterData 布爾值,表示是否應(yīng)用到節(jié)點(diǎn)內(nèi)容或節(jié)點(diǎn)文本的變動;

subtree 布爾值,表示是否應(yīng)用到是否將觀察器應(yīng)用于該節(jié)點(diǎn)的所有后代節(jié)點(diǎn);

attributeFilter 數(shù)組,表示觀察特定屬性;

attributeOldValue 布爾值,表示觀察 attributes 變動時(shí),是否需要記錄變動前的屬性值;

characterDataOldValue 布爾值,表示觀察 characterData 變動,是否需要記錄變動前的值;

childList 和 subtree 屬性

childList 屬性表示是否應(yīng)用到子節(jié)點(diǎn)的變動(新增、刪除或者更改),監(jiān)聽不到子節(jié)點(diǎn)后代節(jié)點(diǎn)變動。

var mutationObserver = new MutationObserver(function (mutations) {
  console.log(mutations);
})

mutationObserver.observe($tar, {
  childList: true, // 子節(jié)點(diǎn)的變動(新增、刪除或者更改)
})

var $div1 = document.createElement("div");
$div1.innerText = "div1";

// 新增子節(jié)點(diǎn)
$tar.appendChild($div1); // 能監(jiān)聽到

// 刪除子節(jié)點(diǎn)
$tar.childNodes[0].remove(); // 能監(jiān)聽到

var $div2 = document.createElement("div");
$div2.innerText = "div2";

var $div3 = document.createElement("div");
$div3.innerText = "div3";

// 新增子節(jié)點(diǎn)
$tar.appendChild($div2); // 能監(jiān)聽到

// 替換子節(jié)點(diǎn)
$tar.replaceChild($div3, $div2); // 能監(jiān)聽到

// 新增孫節(jié)點(diǎn)
$tar.childNodes[0].appendChild(document.createTextNode("新增孫文本節(jié)點(diǎn)")); // 監(jiān)聽不到
attributes 和 attributeFilter 屬性

attributes 屬性表示是否應(yīng)用到 DOM 節(jié)點(diǎn)屬性的值變動的監(jiān)聽。而 attributeFilter 屬性是用來過濾要監(jiān)聽的屬性 key

// ...
mutationObserver.observe($tar, {
  attributes: true, // 屬性的變動
  attributeFilter: ["class", "style"], // 觀察特定屬性
})
// ...
// 改變 style 屬性
$tar.style.height = "100px"; // 能監(jiān)聽到
// 改變 className
$tar.className = "tar"; // 能監(jiān)聽到
// 改變 dataset
$tar.dataset = "abc"; // 監(jiān)聽不到
characterData 和 subtree 屬性

characterData 屬性表示是否應(yīng)用到節(jié)點(diǎn)內(nèi)容或節(jié)點(diǎn)文本的變動。subtree 是否將觀察器應(yīng)用于該節(jié)點(diǎn)的所有后代節(jié)點(diǎn)。為了更好觀察節(jié)點(diǎn)文本變化,將兩者結(jié)合應(yīng)用到富文本監(jiān)聽上是不錯(cuò)的選擇。

簡單的富文本,比如

A simple editor
var $tar = document.getElementById("tar");
var MutationObserver = window.MutationObserver || window.webkitMutationObserver || window.MozMutationObserver;
var mutationObserver = new MutationObserver(function (mutations) {
  console.log(mutations);
})
mutationObserver.observe($tar, {
  characterData: true, // 節(jié)點(diǎn)內(nèi)容或節(jié)點(diǎn)文本的變動
  subtree: true, // 是否將觀察器應(yīng)用于該節(jié)點(diǎn)的所有后代節(jié)點(diǎn)
})

takeRecords()、disconnect() 方法

MutationObserver 實(shí)例上還有兩個(gè)方法,takeRecords() 用來清空記錄隊(duì)列并返回變動記錄的數(shù)組。disconnect() 用來停止觀察。調(diào)用該方法后,DOM 再發(fā)生變動,也不會觸發(fā)觀察器。

var $text5 = document.createTextNode("新增文本節(jié)點(diǎn)5");
var $text6 = document.createTextNode("新增文本節(jié)點(diǎn)6");

// 新增文本節(jié)點(diǎn)
$tar.appendChild($text5);
var record = mutationObserver.takeRecords();

console.log("record: ", record); // 返回 記錄新增文本節(jié)點(diǎn)操作,并清空監(jiān)聽隊(duì)列

// 替換文本節(jié)點(diǎn)
$tar.replaceChild($text6, $text5);

mutationObserver.disconnect(); // 此處以后的不再監(jiān)聽

// 刪除文本節(jié)點(diǎn)
$tar.removeChild($text6); // 監(jiān)聽不到

前面還有兩個(gè)屬性 attributeOldValuecharacterDataOldValue 沒有說,其實(shí)是影響 takeRecords() 方法返回 MutationRecord 實(shí)例。如果設(shè)置了這兩個(gè)屬性,就會對應(yīng)返回對象中 oldValue 為記錄之前舊的 attributedata值。

比如將原來的 className 的值 aaa 替換成 tar,oldValue 記錄為 aaa

record: [{
  addedNodes: NodeList []
  attributeName: "class"
  attributeNamespace: null
  nextSibling: null
  oldValue: "aaa"
  previousSibling: null
  removedNodes: NodeList []
  target: div#tar.tar
  type: "attributes"
}]
MutationObserver 的應(yīng)用

一個(gè)容器本身以及內(nèi)部元素的屬性變化,節(jié)點(diǎn)變化和文本變化是影響該容器高寬的重要因素(當(dāng)然還有其他因素),以上了解了 MutationObserver API 的一些細(xì)節(jié),可以實(shí)現(xiàn)監(jiān)聽容器寬高的變化。

var $tar = document.getElementById("tar");
var MutationObserver = window.MutationObserver || window.webkitMutationObserver || window.MozMutationObserver;

var recordHeight = 0;
var mutationObserver = new MutationObserver(function (mutations) {
  console.log(mutations);

  let height = window.getComputedStyle($tar).getPropertyValue("height");
  if (height === recordHeight) {
    return;
  }
  recordHeight = height;
  console.log("高度變化了");
  // 之后更新外部容器等操作
})

mutationObserver.observe($tar, {
  childList: true, // 子節(jié)點(diǎn)的變動(新增、刪除或者更改)
  attributes: true, // 屬性的變動
  characterData: true, // 節(jié)點(diǎn)內(nèi)容或節(jié)點(diǎn)文本的變動
  subtree: true // 是否將觀察器應(yīng)用于該節(jié)點(diǎn)的所有后代節(jié)點(diǎn)
})
漏網(wǎng)之魚:動畫(animation、transform)改變?nèi)萜鞲撸▽挘?/b>

除了容器內(nèi)部元素節(jié)點(diǎn)、屬性變化,還有 css3 動畫會影響容器高寬,由于動畫并不會造成元素屬性的變化,所以 MutationObserver API 是監(jiān)聽不到的。

#tar 容器加入以下 css 動畫

@keyframes changeHeight {
  to {
    height: 300px;
  }
}

#tar {
  background-color: aqua;
  border: 1px solid #ccc;
  animation: changeHeight 2s ease-in 1s;
}

可以看出,沒有打印輸出,是監(jiān)聽不到動畫改變高寬的。所以,在這還需對這條“漏網(wǎng)之魚”進(jìn)行處理。處理很簡單,只需在動畫(transitionend、animationend)停止事件觸發(fā)時(shí)監(jiān)聽高寬變化即可。在這里用 Vue 自定義指令處理如下:

/**
 * 監(jiān)聽元素高度變化,更新滾動容器
 */
Vue.directive("observe-element-height", {
  insert (el, binding) {
    const MutationObserver = window.MutationObserver || window.webkitMutationObserver || window.MozMutationObserver
    let recordHeight = 0
    const onHeightChange = _.throttle(function () { // _.throttle 節(jié)流函數(shù)
      let height = window.getComputedStyle(el).getPropertyValue("height");
      if (height === recordHeight) {
        return
      }
      recordHeight = height
      console.log("高度變化了")
      // 之后更新外部容器等操作
    }, 500)

    el.__onHeightChange__ = onHeightChange

    el.addEventListener("animationend", onHeightChange)

    el.addEventListener("transitionend", onHeightChange)

    el.__observer__ = new MutationObserver((mutations) => {
      onHeightChange()
    });

    el.__observer__.observe(el, {
      childList: true,
      subtree: true,
      characterData: true,
      attributes: true
    })
  },
  unbind (el) {
    if (el.__observer__) {
      el.__observer__.disconnect()
      el.__observer__ = null
    }
    el.removeEventListener("animationend", el.__onHeightChange__)
    el.removeEventListener("transitionend", el.__onHeightChange__)
    el.__onHeightChange__ = null
  }
})

ResizeObserver

既然對容器區(qū)域?qū)捀弑O(jiān)聽有硬性需求,那么是否有相關(guān)規(guī)范呢?答案是有的,ResizeObserver 接口可以監(jiān)聽到 Element 的內(nèi)容區(qū)域或 SVGElement 的邊界框改變。內(nèi)容區(qū)域則需要減去內(nèi)邊距 padding。目前還是實(shí)驗(yàn)性的一個(gè)接口,各大瀏覽器對ResizeObserver兼容性不夠,實(shí)際應(yīng)用需謹(jǐn)慎。

ResizeObserver Polyfill

實(shí)驗(yàn)性的 API 不足,總有 Polyfill 來彌補(bǔ)。

ResizeObserver Polyfill 利用事件冒泡,在頂層 document 上監(jiān)聽動畫 transitionend;

簡體 windowresize 事件;

其次用 MutationObserver 監(jiān)聽 document 元素;

兼容IE11以下 通過 DOMSubtreeModified 監(jiān)聽 document 元素。

利用MapShim (類似ES6中 Map) 數(shù)據(jù)結(jié)構(gòu),key 為被監(jiān)聽元素,valueResizeObserver 實(shí)例,映射監(jiān)聽關(guān)系,頂層 documentwindow 監(jiān)聽到觸發(fā)事件,通過綁定元素即可監(jiān)聽元素尺寸變化。部分源碼如下:

/**
 * Initializes DOM listeners.
 *
 * @private
 * @returns {void}
 */
ResizeObserverController.prototype.connect_ = function () {
    // Do nothing if running in a non-browser environment or if listeners
    // have been already added.
    if (!isBrowser || this.connected_) {
        return;
    }
    // Subscription to the "Transitionend" event is used as a workaround for
    // delayed transitions. This way it"s possible to capture at least the
    // final state of an element.
    document.addEventListener("transitionend", this.onTransitionEnd_);
    window.addEventListener("resize", this.refresh);
    if (mutationObserverSupported) {
        this.mutationsObserver_ = new MutationObserver(this.refresh);
        this.mutationsObserver_.observe(document, {
            attributes: true,
            childList: true,
            characterData: true,
            subtree: true
        });
    }
    else {
        document.addEventListener("DOMSubtreeModified", this.refresh);
        this.mutationEventsAdded_ = true;
    }
    this.connected_ = true;
};

PS:不過,這里貌似作者沒有對 animation 做處理,也就是 animation 改變元素尺寸還是監(jiān)聽不到。不知道是不是我沒有全面的考慮,這點(diǎn)已向作者提了issue。

用 iframe 模擬 window 的 resize

windowresize 沒有兼容性問題,按照這個(gè)思路,可以用隱藏的 iframe 模擬 window 撐滿要監(jiān)聽得容器元素,當(dāng)容器尺寸變化時(shí),自然會 iframe 尺寸也會改變,通過contentWindow.onresize() 就能監(jiān)聽得到。

function observeResize(element, handler) {
  let frame = document.createElement("iframe");
  const CSS = "position:absolute;left:0;top:-100%;width:100%;height:100%;margin:1px 0 0;border:none;opacity:0;visibility:hidden;pointer-events:none;";
  frame.style.cssText = CSS;
  frame.onload = () => {
    frame.contentWindow.onresize = () => {
      handler(element);
    };
  };
  element.appendChild(frame);
  return frame;
}

let element = document.getElementById("main");
// listen for resize
observeResize(element, () => {
  console.log("new size: ", {
    width: element.clientWidth,
    height: element.clientHeight
  });
});

采用這種方案常用插件有 iframe-resizer、resize-sensor等。不過這種方案不是特別優(yōu)雅,需要插入 iframe 元素,還需將父元素定位,可能在頁面上會有其他意想不到的問題,僅作為供參考方案吧。

總結(jié)

最后,要優(yōu)雅地監(jiān)聽元素的寬高變化,不要去根據(jù)交互行為而是從元素本身去監(jiān)聽,了解 MutationObserver 接口是重點(diǎn),其次要考慮到元素動畫可能造成寬高變化,兼容IE11以下,通過 DOMSubtreeModified 監(jiān)聽。用 iframe 模擬 window 的 resize 屬于一種供參考方案。做的功課有點(diǎn)少,歡迎指正,完~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/106021.html

相關(guān)文章

  • 如何優(yōu)雅監(jiān)聽容器高度變化

    摘要:哈哈哈哈,以上純屬虛構(gòu),不過在最近項(xiàng)目中還真遇到過對容器監(jiān)聽高寬變化在使用或滾動插件,如果容器內(nèi)部元素有高度變化要去及時(shí)更新外部包裹容器,即調(diào)用方法。處理很簡單,只需在動畫停止事件觸發(fā)時(shí)監(jiān)聽高寬變化即可。 前言 老鳥:怎樣去監(jiān)聽 DOM 元素的高度變化呢?菜鳥:哈哈哈哈哈,這都不知道哦,用 onresize 事件鴨!老鳥扶了扶眼睛,空氣安靜幾秒鐘,菜鳥才晃過神來。對鴨,普通 DOM 元...

    jackwang 評論0 收藏0
  • 單頁應(yīng)用SPA開發(fā)最佳實(shí)踐

    摘要:最近用做了個(gè)單頁應(yīng)用的項(xiàng)目,頁面大概有個(gè)左右。詳見鏈接使用自定義事件的表單輸入組件優(yōu)雅解決的問題的問題由來已久,在單頁應(yīng)用中我們免不了需要處理這樣的。 最近用vue+vue-router做了個(gè)單頁應(yīng)用的項(xiàng)目,頁面大概有15個(gè)左右。積累了一些開發(fā)經(jīng)驗(yàn)在此做一些記錄.本文主要從可維護(hù)性方面來考慮SPA的開發(fā)實(shí)踐 全站的顏色定義放在一個(gè)less或者scss的文件里,其他組件和頁面import...

    mingzhong 評論0 收藏0
  • 單頁應(yīng)用SPA開發(fā)最佳實(shí)踐

    摘要:最近用做了個(gè)單頁應(yīng)用的項(xiàng)目,頁面大概有個(gè)左右。詳見鏈接使用自定義事件的表單輸入組件優(yōu)雅解決的問題的問題由來已久,在單頁應(yīng)用中我們免不了需要處理這樣的。 最近用vue+vue-router做了個(gè)單頁應(yīng)用的項(xiàng)目,頁面大概有15個(gè)左右。積累了一些開發(fā)經(jīng)驗(yàn)在此做一些記錄.本文主要從可維護(hù)性方面來考慮SPA的開發(fā)實(shí)踐 全站的顏色定義放在一個(gè)less或者scss的文件里,其他組件和頁面import...

    不知名網(wǎng)友 評論0 收藏0
  • 單頁應(yīng)用SPA開發(fā)最佳實(shí)踐

    摘要:最近用做了個(gè)單頁應(yīng)用的項(xiàng)目,頁面大概有個(gè)左右。詳見鏈接使用自定義事件的表單輸入組件優(yōu)雅解決的問題的問題由來已久,在單頁應(yīng)用中我們免不了需要處理這樣的。 最近用vue+vue-router做了個(gè)單頁應(yīng)用的項(xiàng)目,頁面大概有15個(gè)左右。積累了一些開發(fā)經(jīng)驗(yàn)在此做一些記錄.本文主要從可維護(hù)性方面來考慮SPA的開發(fā)實(shí)踐 全站的顏色定義放在一個(gè)less或者scss的文件里,其他組件和頁面import...

    williamwen1986 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<