雖然現代化的 web 開發更多地依賴各類 MVC 框架,但開發者仍須要熟練掌握 HTML 與 DOM 方面的基礎知識。不過,即便是有着多年經驗的前端開發者,也會遇到一些不明因此的狀況。本文首先將爲初學者介紹 HTML 與 DOM 的基本常識,隨後爲你們介紹15個比較冷門的 HTML 元素的方法。html
初學者前端
首先讓咱們來討論一下 HTML 與 DOM 之間的區別。web
顯然,普通的 <table> 元素就是一段 HTML 代碼,它能夠應用在任何一個以 .html 爲擴展名的文件中。元素自帶一系列特性(attribute),以控制它的顯示方式與行爲。數組
這就是元素的所有內容,它與 JavaScript 沒有任何關聯。瀏覽器
而 DOM 的做用是將你的 JavaScript 代碼與文檔中的 HTML 元素關聯在一塊兒,讓你可以以對象的方式與這些元素進行交互。app
這就是所謂的文檔對象模型。框架
在 HTML 中的每一個元素都對應着一個 DOM ‘接口’,其中定義了若干屬性(property,一般會映射至 HTML 元素上的特性)與方法。舉例來講,一個 <table> 元素對應着一個 HTMLTableElement 接口。佈局
你能夠按如下方式獲取某個元素的引用:動畫
const searchBox = document.getElementById('search-box');
如今,你就能夠訪問該元素上定義的全部屬性與方法了。打個比方,你能夠經過 searchBox.value 訪問它的 value 屬性,也能夠調用 searchBox.focus() 方法讓光標移至輸入框上。網站
感謝你參加這個58秒的 DOM 入門培訓課程,哈哈。
如今的問題在於,大多數元素都沒有提供什麼有趣的方法。所以,除非你特地到官方文檔規範上去搜索那些可能永遠都用不到的東西,不然很容易忽略掉那些零散的小技巧。
幸運的是,瀏覽規範與整理小技巧正是我用於避免陷入困境的兩種最喜歡的方式。那麼,讓咱們開始吧……
若是你也但願嘗試一下這些技巧,又剛好有一些瀏覽器 DevTools 可使用的話,能夠在元素樹型結構中先選中某個元素,而後在控制檯中輸入 $0,它會返回給你一個所選中元素的引用。若是你須要返回該元素的對象,請輸入 dir($0)。
1 table 的方法
原始的 table 元素(時至今日仍然是網站佈局方法裏的第一名)自己自帶許多精巧的方法,使用這些方法建立表格就像搭建宜家裏的桌子同樣簡單。
如下是部分實用的方法。
const tableEl = document.querySelector('table'); const headRow = tableEl.createHead().insertRow(); headerRow.insertCell().textContent = 'Make'; headerRow.insertCell().textContent = 'Model'; headerRow.insertCell().textContent = 'Color'; const newRow = tableEl.insertRow(); newRow.insertCell().textContent = 'Yes'; newRow.insertCell().textContent = 'No'; newRow.insertCell().textContent = 'Thank you';
整段代碼裏徹底用不着使用 document.createElement() 方法。
若是你在一個 table 元素上直接調用 .insertRow() 方法,它甚至會自動爲你插入一個 <tbody> 元素,是否是很棒?
2 scrollIntoView()
你知道嗎?當頁面的 URL 中包含 #something 元素時,一旦頁面加載,瀏覽器就會自動滾動至具備這個 ID 的元素之處。
這確實是一項很貼心的功能,但若是你在頁面加載以後再渲染元素,這項功能就不起做用了。
不過,你也能夠經過如下方式,手動地讓這項功能從新生效:
document.querySelector(document.location.hash).scrollIntoView();
3 hidden
好吧,hidden 或許不是一個方法,但若是你提出抗議,那我也要爭論一下:在 hidden 的背後極可能對應着一個 setter,這但是一個貨真價實的方法,對不對?
無論怎樣,你是否曾經爲了隱藏某個元素而使用過 myElement.style.display = 'none' 這種方法呢?若是是的話,請別再這麼作了!
只需調用 myElement.hidden = true ,便可實現元素隱藏的功能。
4 toggle()
嗯,toggle 也不算是元素的方法,它其實是元素屬性上的一個方法。嚴格來講,這是一種爲元素添加或刪除某個 class 的方法,具體作法是 myElement.classList.toggle('some-class') 。
若是你曾經經過 if 條件語句爲元素添加 class,那就應該趕忙改用這種作法。
正確的方式是爲 toggle 方法傳入第二個參數,若是該參數返回 true ,則指定的 class 就會添加至元素上。
el.classList.toggle('some-orange-class', theme === 'orange');
我知道你在想些什麼:這種寫法違背了 ‘toggle’ 這個詞的本義(開關),那些從 IE 時代過來的開發者們都這麼想,他們斷言應當完全摒棄使用第二個參數的作法。
因此,我收回個人話。沒必要堅持這種寫法了,各位請隨意!
5 querySelector()
好吧,你固然知道這個方法,但據我推測,應該只有 17% 的開發者才知道,該方法可使用在任意元素上。
打個比方,myElement.querySelector('.my-class') 的做用是返回在 myElement 的子代中包含 my-class 這個 class 的全部元素。
6 closest
該方法可在任意元素上使用,它可以向上查找元素的樹型結構,能夠理解爲 與 querySelector() 相反的方法。所以,我能夠經過如下方法獲取當前內容的對應標頭:
myElement.closest('article').querySelector('h1');
這段方法首先向上找到最近的 <article> 元素,而後再向下找到最近的 <h1> 元素。
7 getBoundingClientRect()
在對 DOM 元素調用該方法時,將返回一個包含其空間結構詳細信息的簡單對象。
{ x: 604.875, y: 1312, width: 701.625, height: 31, top: 1312, right: 1306.5, bottom: 1343, left: 604.875 }
不過,在調用該方法時須要注意兩點:
調用該方法會致使元素的重繪,根據設備與頁面複雜程度的不一樣,重繪的時間可能會佔用幾毫秒。所以,若是你須要重複地調用該方法,例如在使用動畫的場景下,須要特別注意這一點。
並不是全部的瀏覽器都會返回這些值,他們有這個責任麼?
8 matches()
假設我須要檢查某個元素是否包括一個特定的 class。
這是最複雜的方式:
if (myElement.className.indexOf('some-class') > -1) { // do something }
比上面的好一點,但和本文沒什麼關係:
if (myElement.className.includes('some-class')) { // do something }
最佳方式:
if (myElement.matches('.some-class')) { // do something } 9 insertAdjacentElement()
我今天才剛學到這一條!它的做用相似於 appendChild()
,但可以更好地控制插入子元素的具體位置。
parentEl.insertAdjacentElement('beforeend', newEl) 與 parentEl.appendChild(newEl) 的做用是同樣的,但除此以外,你還能夠指定 beforebegin、afterbegin 或 afterend 這幾個參數值,元素將按這些值的名稱所示插入相應的位置。
多麼強大的控制能力!
多棒的點子。
10 contains()
你有沒有遇到過這樣的情形,須要知道某個元素是否被包含在另外一個元素中?至少我本人常常會遇到這樣的問題。
打個比方,假設我在處理一個鼠標點擊事件時,須要知道它是發生在一個模態窗口中仍是發生在外面(這樣我纔可以關閉這個窗口),我大概會這麼作:
const handleClick = e => { if (!modalEl.contains(e.target)) modalEl.hidden = true; };
代碼中的 modalEl 是模態窗口的引用,而 e.target 則表明各類發生點擊事件的元素。
有趣的是,每當遇到這種情形,在我第一遍寫代碼的時候,100%的機率會將其中的判斷邏輯寫反。
哪怕是我提升了警戒,並在保存代碼以前嘗試將邏輯顛倒過來寫,仍然仍是寫錯。
11 getAttribute()
這毫無疑問是全部元素方法中最沒用的一個,但有一個場景除外。
你是否記得,我在本文的開頭部分曾提到,對象的屬性 property 一般也會映射到它的特性 attribute 中(我在上文中特別用粗體強調了這一點,注意不是斜體)?
但在某一個場景中,這種假設並不成立,這就是某個元素的 href 特性,例如
<a href="/animals/cat">Cat</a> 。
調用 el.href 不會返回 /animals/cat,這可能與你的猜想不符。緣由在於 元素實現了 HTMLHyperlinkElementUtils接口,該接口提供了一系列輔助屬性,例如 prototol 與 hash 等等,以展示與連接的目標相關的值。
href 就是其中一個實用的屬性,它將返回完整的 URL,並去掉無用的空格,而不是返回在特性中所指定的相對 URL。
這樣一來,若是你須要獲取 href 特性中的字符串字面值,就只能使用 el.getAttribute('href') 方法了。
12 dialog 元素的三大法寶
<dialog> 是一個相對較新的元素,它帶來了兩個還算能用的方法,和一個很是棒的方法。其中show() 和 close() 方法的功能與你所想象的同樣,我感受還算能夠。
而 showModal() 方法可以將 <dialog> 元素顯示在頁面的頂層,居中對齊,這正是所指望的模態窗口行爲。你無需指定 z-index,或者手動添加一個灰色的背景,也不須要監聽 esc 按鍵以關閉此窗口。瀏覽器可以理解模態窗口的工做方式,並自動完成你所指望的行爲。
這真是太棒了。
13 forEach()
某些狀況下,當你獲取到一個元素列表的引用時,能夠經過 forEach() 方法進行迭代式調用。
用 for() 進行循環已是 2014 年代的老古董了。
假設你須要記錄頁面中全部連接的 URL,能夠輸入如下代碼,只要你不介意看到報錯。
document.getElementsByTagName('a').forEach(el ==> { console.log(el.href); });
也能夠這麼作:
document.querySelectorAll('a').forEach(el ==> { console.log(el.href); });
問題出在 getElementsByTagName 與其餘相似的 get… 方法返回的是一個 HTMLCollection 接口,而 querySelectorAll返回的是一個 NodeList 接口。
而 NodeList 接口爲咱們提供了 forEach() 方法(此外還包括 keys()、values(),和 entries() 等方法 )。
理想的狀況下,最好是每一個方法都只返回簡單的數組,而不是返回一些相似數組的對象。不過別擔憂,ECMA 大神爲咱們提供了 Array.from() 方法,它可以把全部這些類數組對象轉化爲一個真正的數組。
因此,這樣的代碼就可以正常工做:
Array.from(document.getElementsByTagName('a')).forEach(el ==> { console.log(el.href); });
獎勵關卡:建立了一個數組以後,你就可以對其使用 map() 、filter() 和 reduce() 以及其餘各類數組方法了。打個比方,先無論目的是什麼,總之你能夠按如下方式返回全部外部連接的數組:
Array.from(document.querySelectorAll('a')) .map(el => el.origin) .filter(origin => origin !== document.origin) .filter(Boolean);
我最喜歡的一個方法是 .filter(Boolean),它確定會給未來的我在調試問題時帶來無窮的煩惱,哈哈。
14 表單
或許你已經知道,<form> 有一個 submit() 方法。但或許你不知道表單還有一個 reset() 方法,並且當你須要對錶單元素進行驗證時,還能夠調用 reportValidity() 方法。
此外,你也能夠經過對錶單的 elements 屬性加上元素的 name 特性 的方式調用它的屬性。打個比方,myFormEl.elements.email 將返回屬於某個 <form> 中的 <input name="email" /> 元素(‘屬於’,並不表明它必定是一個‘子元素’)。
好吧,其實剛纔我是騙你的。elements 並不會返回一個元素列表,而是返回一個控件列表(顯然它不是一個數組,由於不必這麼作)。
舉例來講:假設你有三個單選按鈕,每一個都有相同的名稱 animal,那麼 formEl.elements.animal 將返回一個單選按鈕集的引用(一個控件,三個元素)。
而 formEl.elements.animal.value 將返回所選中的單選按鈕的值。
這種語法看起來很是古怪,讓咱們來分解一下看看:formEl 是一個元素,elements 則對應 HTMLFormControlsCollection 接口,這並不是一個真正的數組,其中的每一項內容也未必表明一個 HTML 元素。animal是多個單選按鈕的集合,只是由於他們具備相同的 name 特性才彙集在一塊兒(RadioNodeList 接口就是爲此而生的),而 value 則返回該集合中所選中的那個單選按鈕的 value 特性。
很是直觀,嗯……
15 select()