前端 那些冷門的 API

前言

說是冷門,無非於咱們而言,在實際項目中登場的機會少,或許有些api壓根沒據說過,本篇介紹幾個 api 算是冷門中的那幾爍極光,在咱們窮盡一切辦法時,它或許帶來那絲曙光。html

Element.classList

Element.classList 是一個只讀屬性,返回一個元素的類屬性的實時 DOMTokenList 集合。 相比將 element.className 做爲以空格分隔的字符串來使用,classList 是一種更方便的訪問元素的類列表的方法。經常使用相關的api以下:前端

  • add : 添加指定的類值。若是這些類已經存在於元素的屬性中,那麼它們將被忽略。
  • remove : 刪除指定的類值。
  • toggle :切換 class ; 即若是類存在,則刪除它,若是不存在,則添加它。
  • contain :檢查元素的類屬性中是否存在指定的類值
  • replace :用一個新類替換已有類。

Element.classList 沒用出現以前,咱們可能這樣操做DOMnode

/* 添加 class 類 方法*/
function add(element, className) {
  if(!new RegExp("(^|\\s)" + className + "(\\s|$)").test(element.className)) element.className += ' ' + className;
}
/* 移出 class 類 方法*/
function remove(element, className) {
  element.className = element.className.replace(new RegExp("(^|\\s)" + className + "(?=(\\s|$))", "g"), '');
}
/* 切換 class 類 方法*/
function toggle(element, className) {
    if(new RegExp("(^|\\s)" + className + "(\\s|$)").test(element.className)){
        element.className = element.className.replace(className,'')
    }else{
        element.className = element.className.trim() + ' ' + className;
    }
}
/* 判斷是否包含某個 class 類 方法*/
function contain(element, className) {
    return element.className.indexOf(className)>-1
}
/* 用一個新類替換已有類*/
function replace(element, oldClassName,newClassName) {
    element.className = element.className.replace(oldClassName,newClassName)
}
複製代碼

Element.classList 出現後,咱們這樣操做DOMapi

  • 給元素添加一個 class 類
element.classList.add( className )
複製代碼
  • 移除元素身上的一個 class 類
element.classList.remove( className )
複製代碼
  • 切換元素身上的一個 class 類
element.classList.toggle( className )
複製代碼
  • 判斷元素身上是否包含一個 class 類
element.classList.contain( className )
複製代碼
  • 用一個新類替換已有類
element.classList.replace( oldClass, newClass )
複製代碼
  • 返回索引對應的類
//html <button class="a b c"> 按鈕 </button>

document.querySelector('.a').classList.item(0) // a

document.querySelector('.a').classList.item(1) // b

document.querySelector('.a').classList.item(2) // c

document.querySelector('.a').classList.item(3) // null
複製代碼

Element.getBoundingClientRect

Element.getBoundingClientRect() 方法返回元素的大小及其相對於視口的位置安全

返回值是一個 DOMRect 對象,這個對象是由該元素的 getClientRects() 方法返回的一組矩形的集合,就是該元素的 CSS 邊框大小。返回的結果是包含完整元素的最小矩形,而且擁有left, top, right, bottom, x, y, width, 和 height這幾個以像素爲單位的只讀屬性用於描述整個邊框。除了width 和 height 之外的屬性是相對於視圖窗口的左上角來計算的。空邊框盒(譯者注:沒有內容的邊框)會被忽略。若是全部的元素邊框都是空邊框,那麼這個矩形給該元素返回的 width、height 值爲 0,left、top 值爲第一個 CSS 盒子(按內容順序)的 top-left 值。app

當計算邊界矩形時,會考慮視口區域(或其餘可滾動元素)內的滾動操做,也就是說,當滾動位置發生了改變,top和left屬性值就會隨之當即發生變化(所以,它們的值是相對於視口的,而不是絕對的)。若是你須要得到相對於整個網頁左上角定位的屬性值,那麼只要給top、left屬性值加上當前的滾動位置(經過 window.scrollX 和 window.scrollY),這樣就能夠獲取與當前的滾動位置無關的值 。源自 Element.getBoundingClientRect - MDN函數

MDN圖解

咱們能夠獲取這些數據進行咱們的邏輯工具

function getBoundingClientRect (element) {
    let rect = element.getBoundingClientRect();
    return {
        left: rect.left,//元素左邊到視窗左邊的距離
        top: rect.top,//元素上邊到視窗上邊的距離
        right: rect.right,//元素右邊到視窗左邊的距離
        bottom: rect.bottom,//元素下邊到視窗上邊的距離
        width: rect.width,//是元素自身的寬
        height: rect.height//是元素自身的高
    }
}
複製代碼

Element.insertAdjacentHTML

insertAdjacentHTML() 方法將指定的文本解析爲 Element 元素,並將結果節點插入到DOM樹中的指定位置。它不會從新解析它正在使用的元素,所以它不會破壞元素內的現有元素。這避免了額外的序列化步驟,使其比直接使用innerHTML操做更快。post

語法: element.insertAdjacentHTML(position, text)性能

  • position

    一個 DOMString,表示插入內容相對於元素的位置,而且必須是如下字符串之一:

    • beforebegin:元素自身的前面。
    • afterbegin:插入元素內部的第一個子節點以前。
    • beforeend:插入元素內部的最後一個子節點以後。
    • afterend:元素自身的後面。
  • text

    是要被解析爲HTML或XML元素,並插入到DOM樹中的 DOMString

位置名稱的可視化

<!-- beforebegin --> 
<p> 
<!-- afterbegin -->
foo
<!-- beforeend -->
</p>
<!-- afterend -->
複製代碼

注意: beforebegin和afterend位置,僅在節點在樹中且節點具備一個parent元素時工做。

有時候,咱們想要在頁面的一個DOM元素裏直接插入DOM字符串,想要jQuery的append的功能,可以往DOM裏面注入DOM字符串

document.createElement實現

// html <ul id="ul"></div> 
let ul = document.getElementById('ul'); 
for(let i=0;i<5;i++){
    let li = document.createElement('li')
    li.className = "item"
    li.innerHTML = `<p>${item}</p>`
    ul.appendChild(li)
}
複製代碼

從中能夠看出,頻繁操做DOM,對頁面性能極不友好

element.insertAdjacentHTML實現

// html <ul id="ul"></div> 
let ul = document.getElementById('ul'); 
let html = ''
for(let i=0;i<5;i++){
    html +=`<li class="item"><p>${item}</p></li>`
}
ul.insertAdjacentHTML('beforeend',html)
複製代碼

只操做了一次DOM

安全問題

使用 insertAdjacentHTML 插入用戶輸入的HTML內容的時候,須要轉義以後才能使用。

若是隻是爲了插入文本內容(而不是HTML節點),不建議使用這個方法,建議使用node.textContent 或者 node.insertAdjacentText()。由於這樣不須要通過HTML解釋器的轉換,性能會好一點。

CustomEvent

CustomEvent 事件是由程序建立的,能夠有任意自定義功能的事件。

CustomEvent是一個構造函數, 能夠建立一個自定義事件,能夠用 window.dispatchEvent去主動觸發這個自定義事件

使用示例:

實現localStorage 監聽

  • localStorage.setItem監聽:自定義事件 setItemEvent
  • localStorage.getItem監聽:自定義事件 getItemEvent
  • localStorage.removeItem監聽:自定義事件 removeItemEvent
//監聽自定義事件 setItemEvent
localStorage.setItem = (Orgin=>{
    return function(key,value){
        let setItemEvent = new CustomEvent('setItemEvent',{detail:{setKey:key,value}})
        window.dispatchEvent(setItemEvent)
        Orgin.call(this,key,typeof value == 'string'? value : JSON.stringify(value))
    }
})(localStorage.setItem)

//監聽自定義事件 getItemEvent
localStorage.getItem = (Orgin=>{
    return function(key){
        let result = JSON.parse(Orgin.call(this,key))
        let getItemEvent = new CustomEvent('getItemEvent',{detail:{getKey:key,value:result}})
        window.dispatchEvent(getItemEvent)
        return result 
    }
})(localStorage.getItem)


//監聽自定義事件 removeItemEvent
localStorage.removeItem = (Orgin=>{
    return function(key){
        let removeItemEvent = new CustomEvent('removeItemEvent',{detail:{removeKey:key}})
        window.dispatchEvent(removeItemEvent)
        Orgin.call(this,key)
    }
})(localStorage.removeItem)

複製代碼

以上示例,咱們對localStorage的 setItemgetItemremoveItem在不影響自己的功能前提下進行了重寫,讓咱們有了對這localStorage的這三個操做進行了監聽的功能。

監聽

//localStorage.setItem監聽
window.addEventListener('setItemEvent',function(e){
    console.log(e.detail)
})

//localStorage.getItem監聽
window.addEventListener('getItemEvent',function(e){
    console.log(e.detail)
}) 

//localStorage.removeItem監聽
window.addEventListener('removeItemEvent',function(e){
    console.log(e.detail)
})
複製代碼

該示例在混合app開發中有實際應用,原生安卓或原生IOS 與 咱們的JS交互,有很大的幫助

ParentNode.append

ParentNode.append 方法在 ParentNode的最後一個子節點以後插入一組 Node 對象或 DOMString 對象。 被插入的 DOMString 對象等價爲 Text 節點。

與 Node.appendChild() 的差別:

  • ParentNode.append()容許追加 DOMString 對象,而 Node.appendChild() 只接受 Node 對象。
  • ParentNode.append() 沒有返回值,而 Node.appendChild() 返回追加的 Node 對象。
  • ParentNode.append() 能夠追加多個節點和字符串,而 Node.appendChild() 只能追加一個節點。

若是想要 DOM 插入 DOMString,能夠選用 element.insertAdjacentHTML(position, DOMString),若是想要便可以插入NODE節點也能夠插入字符串,能夠選用 ParentNode.append()

var parent = document.createElement("div");
var p = document.createElement("p");
parent.append("Some text", p);
console.log(parent);
// <div>"Some text"<p></p></div>
複製代碼

Document.createDocumentFragment

建立一個新的空白的文檔片斷( DocumentFragment)。

語法:let fragment = document.createDocumentFragment()

描述: fragment 是一個指向空DocumentFragment對象的引用。

DocumentFragments 是DOM節點。它們不是主DOM樹的一部分。一般的用例是建立文檔片斷,將元素附加到文檔片斷,而後將文檔片斷附加到DOM樹。在DOM樹中,文檔片斷被其全部的子元素所代替。

由於文檔片斷存在於內存中,並不在DOM樹中,因此將子元素插入到文檔片斷時不會引發頁面迴流(對元素位置和幾何上的計算)。所以,使用文檔片斷一般會帶來更好的性能。

示例:

HTML

<ul id="ul"></ul>
複製代碼

JavaScript

var element  = document.getElementById('ul'); // assuming ul exists
var fragment = document.createDocumentFragment();
var browsers = ['Firefox', 'Chrome', 'Opera', 
    'Safari', 'Internet Explorer'];

browsers.forEach(function(browser) {
    var li = document.createElement('li');
    li.textContent = browser;
    fragment.appendChild(li);
});

element.appendChild(fragment);
複製代碼

結果:

Firefox
Chrome
Opera
Safari
Internet Explorer
複製代碼

參考文獻

中文 Web API 接口參考 | MDN

結語

若是你有更好的點子,或者沒有找到你想要的工具函數,歡迎留言

文中如有不許確或錯誤的地方,歡迎指出

往期文章 :

前端代碼優化實用篇

前端開發中實用的工具方法

前端 Promise 常見的應用場景

相關文章
相關標籤/搜索