【譯】8個你不知道的DOM的特性

原文:blog.logrocket.com/8-dom-featu…
做者:Louis Lazaris
翻譯:前端小白javascript


隨着最近對工具的關注愈來愈多,讓咱們從React和npm-install-everything發佈的全部文章中抽點時間來了解一下在現代瀏覽器中不須要依賴的純DOM和Web API的特性。

這篇文章將考慮八個不爲人知的DOM特性,這些特性具備強大的瀏覽器支持。 爲了幫助解釋每一個工做原理,我會提供大量的交互式演示,同時也便於你本身嘗試html

這些方法和屬性沒有一個陡峭的學習曲線,能夠很好地配合你的項目所使用的任何工具集。前端

你確定已經使用過 addEventListener() 將事件附加到web文檔中的元素上。一般,addEventListener() 調用看起來是這樣的:java

element.addEventListener('click', doSomething, false);
複製代碼

第一個參數是我正在監聽的事件。第二個參數是一個回調函數,它將在事件發生時執行。第三個參數是一個名爲 useCapture 的布爾值,用於指示是否要使用事件冒泡或捕獲jquery

這些都是衆所周知的(尤爲是前兩個)。可是,您可能不知道 addEventListener() 也接受將第三個布爾值類型的參數替換爲另外一個新參數。這個新參數是一個看起來像這樣的 options 對象:git

element.addEventListener('click', doSomething, {
  capture: false,
  once: true,
  passive: false
});
複製代碼

注意,語法容許定義三個不一樣的屬性。下面是每一個詞的意思:github

  • capture: 一個布爾值和 useCapture 參數同樣
  • once: 一個布爾值,若是設爲 true, 表示事件只在目標元素上運行一次,而後被刪除
  • passive: 一個布爾值,若是設爲 true, 表示函數不會調用 preventDefault(), 即便它包含在函數體中

這三個選項中最有趣的是 once。這在不少狀況下都很是有用,能夠避免使用 removeEventListener() 或其餘複雜的技術來強制觸發單個事件。若是您曾經使用過jQuery,那麼您可能熟悉該庫中的相似特性.one()方法。web

您能夠在下面的codepen中嘗試使用options對象:npm

codepen: codepen.io/impressivew…api

注意演示頁面上的按鈕只會追加文本一次。若是將once值更改成false,而後屢次單擊該按鈕,則每次單擊按鈕時都會附加文本。

瀏覽器對options對象的支持很是友好:除了IE11和更早版本以外,全部瀏覽器都支持它,因此若是你不在意微軟Edge以前的瀏覽器,那麼使用它是很是安全的。

scrollTo() 在窗口或元素中平滑滾動

平滑滾動一直是須要的。當本地頁面連接當即跳轉到指定位置時(若是你眨眼,你甚至可能會錯過跳轉),這會讓人很不舒服。 平滑滾動改進了用戶頁面體驗。

雖然過去使用jQuery插件能夠實現,但如今可使用 window.scrollTo() 方法,只須要使用一行JavaScript代碼就能夠實現。

scrollTo() 方法應用於 window 對象,以告訴瀏覽器滾動到頁面上指定的位置。下面是一個基本語法的示例:

window.scrollTo(0, 1000);
複製代碼

這將使窗口向右滾動0px(表示x座標,或水平滾動),向下滾動1000px(垂直方向,這一般是您想要的)。但在這種狀況下,滾動將不會是一個平滑的動畫效果;頁面將忽然滾動,就像使用本地連接到指定哈希URL同樣。

雖然有時候這就是你想要的。可是爲了得到平滑的滾動,必須使用不多有人知道的 ScrollToOptions 對象,就像這樣:

window.scrollTo({
  top: 0,
  left: 1000,
  behavior: 'smooth'
});
複製代碼

這段代碼與前面的示例相同,可是在 options 對象中添加了 behavior 屬性並將其值設爲 smooth

codepen: codepen.io/impressivew…

關於這個特性的一些注意事項:

  • 雖然幾乎全部的瀏覽器都支持 scrollTo(),但不是全部瀏覽器都支持 options 對象
  • 該方法一樣做用於DOM元素上,而不只僅 window
  • options 對象一樣適用於 scroll()scrollBy()

可選參數的setTimeout()和setInterval()

在許多狀況下,使用 window.setTimeout()window.setInterval() 執行基於時間的動畫如今已經被性能更友好的 window.requestAnimationFrame() 所取代。可是在某些狀況下,setTimeout()setInterval() 是更好的的選擇,所以有必要了解其中不多有人知道的一些特性。

一般,你會看到使用他們的語法是這樣的:

let timer = window.setInterval(doSomething, 3000);
function doSomething () {
  // Something happens here...
}
複製代碼

這裏 setInterval() 調用傳入兩個參數:回調函數和時間間隔。對於 setTimeout(),它將運行一次,而在本例中,它將無限期地運行,直到我傳入計時器ID,調用 window.clearTimeout() 清除定時器。

使用起來很簡單。 可是,若是我但願個人回調函數接受參數呢? 最近這些計時器方法容許添加如下內容:

let timer = window.setInterval(doSomething, 3000, 10, 20);
function doSomething (a, b) {
  // Something happens here…
}
複製代碼

注意,我在 setInterval() 調用中又添加了兩個參數。而後,個人 doSomething() 函數接受這些參數並根據須要操做它們。

如下的codepen演示瞭如何使用 setTimeout() 實現此功能:

codepen: codepen.io/impressivew…

當單擊該按鈕時,將執行兩個傳入值的計算。能夠經過頁面上的數字輸入更改值。

至於瀏覽器支持,還不是很肯定,但彷佛全部正在使用的瀏覽器都支持可選參數功能,包括IE10。

單選按鈕和複選框的defaultChecked屬性

您可能知道,對於單選按鈕和複選框,若是但願獲取或設置 checked 屬性,可使用 checked 屬性,以下所示(假設 radioButton 是對某個表單輸入的引用)

console.log(radioButton.checked); // true
radioButton.checked = false;
console.log(radioButton.checked); // false
複製代碼

但還有一個名爲 defaultChecked 的屬性,它能夠應用於單選按鈕或複選框組,來得到組中最初設置爲checked的元素是哪一個

<form id="form">
  <input type="radio" value="one" name="setOne"> One
  <input type="radio" value="two" name="setOne" checked> Two<br />
  <input type="radio" value="three" name="setOne"> Three
</form>
複製代碼

有了這個,即便在更改了選中的單選按鈕以後,我也能夠遍歷單選框並找出初始爲checked是哪個,以下所示:

for (i of myForm.setOne) {
  if (i.defaultChecked === true) {
    console.log(‘i.value’);
  }
}
複製代碼

下面是CodePen演示,它將顯示當前選中的單選框或默認選中的單選框,具體取決於你點擊哪一個按鈕:

codepen: codepen.io/impressivew…

該示例中的 defaultChecked 始終是單選框Two。如前所述,這也能夠經過複選框組來完成。嘗試更改HTML中的默認選中選項,而後再次嘗試點擊按鈕。

這是複選框組的演示: codepen: codepen.io/impressivew…

在本例中,你將注意到默認狀況下選中了兩個複選框,所以當使用 defaultChecked 查詢時,這兩個複選框都將返回 true

使用 normalize() and wholeText() 操做文本節點

HTML文檔中的文本節點可能比較複雜,特別是在動態插入或建立節點時。例如,若是我有如下HTML

<p id="el">This is the initial text.</p>
複製代碼

我能夠給p元素增長一個文本節點:

let el = document.getElementById('el');
el.appendChild(document.createTextNode(' Some more text.'));
console.log(el.childNodes.length); // 2
複製代碼

注意,在添加文本節點以後,我將記錄該段中子節點的長度,它表示有兩個節點。這些節點是一個文本字符串,可是由於文本是動態附加的,因此它們被視爲單獨的節點。

在某些狀況下,若是將文本做爲單個文本節點處理將會更有幫助,這使得文本更容易操做。這就是 normalize()wholeText() 發揮做用的地方。

normalize() 方法可用於合併單獨的文本節點

el.normalize();
console.log(el.childNodes.length); // 1
複製代碼

對元素調用 normalize() 將合併該元素內的任何相鄰文本節點。若是剛好有一些HTML穿插在相鄰的文本節點中,HTML將保持原樣,而全部相鄰的文本節點將被合併。

可是,若是因爲某種緣由我想保持文本節點分開,但我仍然但願可以將文本做爲單個單元抓取,那麼這就是 wholeText() 有用的地方。 所以,我能夠在相鄰的文本節點上執行此操做,而不是調用 normalize()

console.log(el.childNodes[0].wholeText);
// This is the initial text. Some more text.
console.log(el.childNodes.length); // 2
複製代碼

只要我沒有調用 normalize(),文本節點的長度將保持爲2,而且我可使用 wholeText() 記錄整個文本。可是請注意幾點:

  • 我必須在一個文本節點上調用 wholeText,而不是元素( el.childNodes[0] 或者 el.childNodes[1])
  • 文本節點必須相鄰,中間沒有HTML分隔它們

insertAdjacentElement() 和 insertAdjacentText()

許多人可能熟悉 insertAdjacentHTML() 方法,它容許您輕鬆地將一串文本或HTML添加到頁面中與其餘元素相關的特定位置。

可是,可能您沒有注意到該規範還包含兩個相關的方法,它們以相似的方式工做: insertAdjacentElement()insertAdjacentText()

insertAdjacentHTML() 的一個缺點是插入的內容必須是字符串的形式。 所以,若是您包含HTML,則必須將其聲明爲:

el.insertAdjacentHTML('beforebegin', '<p><b>Some example</b> text goes here.</p>');
複製代碼

可是,使用 insertAdjacentElement(),第二個參數能夠是一個元素的引用,以下:

let el = document.getElementById('example'),
addEl = document.getElementById('other');
el.insertAdjacentElement('beforebegin', addEl);
複製代碼

這個方法的有趣之處在於,它不只將引用的元素添加到指定的位置,並且還將從文檔中的原始位置刪除元素。所以,這是將一個元素從DOM中的一個位置轉移到另外一個位置的簡單方法。

下面是一個使用 insertAdjacentElement() 的CodePen演示:

codepen: codepen.io/impressivew…

insertAdjacentText() 方法的工做原理相似,可是所提供的文本字符串將僅做爲文本插入,即便它包含HTML。demo以下:

codepen: codepen.io/impressivew…

您能夠將本身的文本添加到輸入字段,而後使用按鈕將其添加到文檔中。注意,任何特殊字符(如HTML標記)都將做爲HTML實體插入,這與 insertAdjacentHTML() 方法的行爲不一樣。

insertAdjacentHTML() , insertAdjacentElement(), 和 insertAdjacentText() 的第一個參數相同,可取值爲:

  • beforebegin
  • afterbegin
  • beforeend
  • afterend

event.detail 屬性

當使用 addEventListener() 時,您可能須要防止函數調用中的默認瀏覽器行爲。例如,您可能想攔截 <a> 元素上的單擊,並使用JavaScript處理這些單擊。你會這樣作:

btn.addEventListener('click', function (e) {
  // do something here...
  e.preventDefault();
}, false);
複製代碼

preventDefault(),和之前的 return false 效果是同樣的。這須要將事件對象傳遞到函數中,由於該對象會調用 preventDefault() 方法。

你還能夠利用那個事件對象作更多。實際上,當觸發某些事件(例如click、dbclick、mouseup、mousedown)時,他們會暴露出一個稱爲UIEvent接口。正如MDN所指出的,這個接口上的許多特性都被廢除了。但有用的是detail屬性,它是官方規範的一部分

他看起來像這樣:

btn.addEventListener('click', function (e) {
  // do something here...
  console.log(e.detail);
}, false);
複製代碼

cedepen: codepen.io/impressivew…

演示中的每一個按鈕都將以按鈕文本描述的方式響應,並顯示一條顯示當前單擊次數的消息。須要注意的一些事情:

  • WebKit內核瀏覽器容許無限制的點擊次數,dbclick除外,它老是兩次。 Firefox只容許最多三次點擊,而後計數再次開始
  • 我已經包括了 blurfocus ,來證實這些不符合條件,並將始終返回0(即沒有點擊)
  • IE11這類的舊瀏覽器可能會有問題

注意,演示中包含了一個很實用的技巧,能夠模擬三次單擊事件:

btnT.addEventListener('click', function (e) {
  if (e.detail === 3) {
    trpl.value = 'Triple Click Successful!';
  }
}, false);
複製代碼

若是全部瀏覽器都計算過三次點擊次數,那麼您還能夠檢測到更高的點擊次數,但我認爲在大多數狀況下,三次點擊事件就足夠了。

scrollHeight 和 scrollWidth 屬性

scrollHeightscrollWidth 屬性可能聽起來很熟悉,由於您可能會將它們與其餘與寬度和高度相關的DOM功能混淆。 例如,offsetWidthoffsetHeight 屬性將返回元素的高度或寬度,而不會考慮溢出。

參考下面demo:

codepen: codepen.io/impressivew…

演示中的列具備相同的內容。左邊列的 overflow 設置爲 auto,右邊列的 overflow 設置爲 hiddenoffsetHeight 屬性爲每一個屬性返回相同的值,由於它不考慮可滾動區域或隱藏區域;它只測量元素的實際高度,包括任何垂直填充和邊框。

另外一方面,名稱恰當的 scrollHeight 屬性將計算元素的所有高度,包括可滾動(或隱藏)區域:

codepen: codepen.io/impressivew…

上面的演示與前面的演示相同,只是它使用了 scrollHeightto 獲取每一個列的高度。再次注意,這兩列的值是相同的。但這一次,它是一個更高的數字,由於溢出面積也被算做高度的一部分。

上面的示例主要關注元素高度,這是最多見的用例,但您也可使用 offsetWidthscrollWidth,它們將以相同的方式應用於水平滾動。

最後

以上就是我列出的DOM特性,這些多是我在過去幾年遇到的最有趣的特性,因此我但願在不久的未來,您至少可以在項目中使用其中的一個特性。

相關文章
相關標籤/搜索