JavaScript中的瀏覽器檢測和DOM基礎

魏曉蕾發表於2020-10-01

1、navigator 物件

由於每個瀏覽器都具有自己獨到的擴充套件,所以在開發階段來判斷瀏覽器是一個非常重要的步驟。雖然瀏覽器開發商在公共介面方面投入了很多精力,努力的去支援最常用的公共功能;但在現實中,瀏覽器之間的差異,以及不同瀏覽器的“怪癖”卻是非常多的,因此客戶端檢測除了是一種補救措施,更是一種行之有效的開發策略。
navigator 物件最早由 Netscape Navigator2.0 引入的 navigator 物件,現在已經成為識別客戶端瀏覽器的事實標準。與之前的 BOM 物件一樣,每個瀏覽器中 的 navigator 物件也都有一套自己的屬性。
navigator 物件的屬性或方法:

屬性或方法說明IEFirefoxSafari/ChromeOpera
appCodeName瀏覽器的名稱。通常是Mozilla,即使在非 Mozilla 瀏覽器中也是如此3.0+1.0+1.0+7.0+
appName完整的瀏覽器名稱3.0+1.0+1.0+7.0+
appMinorVersion次版本資訊4.0+--9.5+
appVersion瀏覽器的版本。一般不與實際的瀏覽器版本對應3.0+1.0+1.0+7.0+
buildID瀏覽器編譯版本-2.0+--
cookieEnabled表示 cookie 是否啟用4.0+1.0+1.0+7.0+
cpuClass客戶端計算機中使用的 CPU 型別(x86、68K、Alpha、PPC、other)4.0+---
javaEnabled()表示當前瀏覽器中是否啟用了Java4.0+1.0+1.0+7.0+
language瀏覽器的主語言-1.0+1.0+7.0+
mimeTypes在瀏覽器中註冊的MIME型別陣列4.0+1.0+1.0+7.0+
onLine表示瀏覽器是否連線到了因特網4.0+1.0+-9.5+
opsProfile似乎早就不用了。無法查詢4.0+---
oscpu客戶端計算機的作業系統或使用的 CPU-1.0+--
platform瀏覽器所在的系統平臺4.0+1.0+1.0+7.0+
plugins瀏覽器中安裝的外掛資訊的陣列4.0+1.0+1.0+7.0+
preference()設定使用者的首選項-1.5+--
product產品名稱(如 Gecko)-1.0+1.0+-
productSub關於產品的次要資訊(如 Gecko 的版本)-1.0+1.0+-
registerContentHandler()針對特定的MIME型別講一個站點註冊為處理程式-2.0+--
registerProtocolHandler()針對特定的協議將一個站點註冊為處理程式-2.0--
securityPolicy已經廢棄。安全策略的名稱-1.0+--
systemLanguage作業系統的語言4.0+---
taintEnabled()已經廢棄。表示是否執行變數被修改4.0+1.0+-7.0+
userAgent瀏覽器的使用者代理字串3.0+1.0+1.0+7.0+
userLanguage作業系統的預設語4.0+--7.0+
userProfile藉以訪問使用者個人資訊的物件4.0+---
vendor瀏覽器的品牌-1.0+1.0+-
verdorSub有關供應商的次要資訊-1.0+1.0+-

1. 瀏覽器及版本號

不同的瀏覽器支援的功能、屬性和方法各有不同。比如 IE 和 Firefox 顯示的頁面可能就會有所略微不同。

alert('瀏覽器名稱:' + navigator.appName);
alert('瀏覽器版本:' + navigator.appVersion);
alert('瀏覽器使用者代理字串:' + navigator.userAgent);
alert('瀏覽器所在的系統:' + navigator.platform);

2. 瀏覽器嗅探器

瀏覽器嗅探器是一段程式,有了它,瀏覽器檢測就變得簡單了。我們這裡提供了一個 browserdetect.js 檔案,用於判斷瀏覽器的名稱、版本號及作業系統。

呼叫方式說明
BrowserDetect.browser瀏覽器的名稱,例如 Firefox,IE
BrowserDetect.version瀏覽器的版本,比如,7、11
BrowserDetect.OS瀏覽器所宿主的作業系統,比如 Windows、Linux
alert(BrowserDetect.browser); 		// 名稱
alert(BrowserDetect.version); 		// 版本
alert(BrowserDetect.OS) 			// 系統

3. 檢測外掛

外掛是一類特殊的程式。他可以擴充套件瀏覽器的功能,通過下載安裝完成。比如,線上音樂、視訊動畫等等外掛。
navigator 物件的 plugins 屬性,這個一個陣列。儲存在瀏覽器已安裝外掛的完整列表。

屬性含義
name外掛名
filename外掛的磁碟檔名
lengthplugins 陣列的元素個數
description外掛的描述資訊
// 列出所有的外掛名
for (var i = 0; i < navigator.plugins.length; i ++) {
	document.write(navigator.plugins[i].name + '<br />');
}

// 檢測非 IE 瀏覽器外掛是否存在
function hasPlugin(name) {
	var name = name.toLowerCase();
	for (var i = 0; i < navigator.plugins.length; i ++) {
		if (navigator.plugins[i].name.toLowerCase().indexOf(name) > -1) {
			return true;
		}
	}
	return false;
}
alert(hasPlugin('Flash')); 		// 檢測 Flash 是否存在
alert(hasPlugin('java')) 		// 檢測 Java 是否存在

4. ActiveX

IE 瀏覽器沒有外掛,但提供了 ActiveX 控制元件。ActiveX 控制元件一種在 Web 頁面中嵌入物件或元件的方法。
由於在 JS 中,我們無法把所有已安裝的 ActiveX 控制元件遍歷出來,但我們還是可以去驗證是否安裝了此控制元件。

// 檢測 IE 中的控制元件
function hasIEPlugin(name) {
	try {
		newActiveXObject(name);
		return true;
	} catch (e) {
		return false;
	}
}

// 檢測 Flash
alert(hasIEPlugin('ShockwaveFlash.ShockwaveFlash'));

ShockwaveFlash.ShockwaveFlash 是 IE 中代表 FLASH 的識別符號,你需要檢查哪種控制元件,必須先獲取它的識別符號。

// 跨瀏覽器檢測是否支援 Flash
function hasFlash() {
	var result = hasPlugin('Flash');
	if (!result) {
		result = hasIEPlugin('ShockwaveFlash.ShockwaveFlash');
	}
	return result;
}

// 檢測 Flash
alert(hasFlash());

5. MIME 型別

MIME 是指多用途因特網郵件擴充套件。它是通過因特網傳送郵件訊息的標準格式。現在也被用於在因特網中交換各種型別的檔案。mimeType[]陣列在 IE 中不產生輸出。
mimeType 物件的屬性:

屬性含義
typeMIME 型別名
descriptionMIME 型別的描述資訊
enabledPlugin指定 MIME 型別配置好的 plugin 物件引用
suffixesMIME 型別所有可能的副檔名
// 遍歷非 IE 下所有 MIME 型別資訊
for (var i = 0; i < navigator.mimeTypes.length; i++) {
	if (navigator.mimeTypes[i].enabledPlugin != null) {
		document.write('<dl>');
		document.write('<dd>型別名稱:' + navigator.mimeTypes[i].type + '</dd>');
		document.write('<dd>型別引用:' + navigator.mimeTypes[i].enabledPlugin.name + '</dd>');
		document.write('<dd>型別描述:' + navigator.mimeTypes[i].description + '</dd>');
		document.write('<dd>型別字尾:' + navigator.mimeTypes[i].suffixes + '</dd>');
		document.write('</dl>')
	}
}

2、客戶端檢測

客戶端檢測一共分為三種,分別為:能力檢測、怪癖檢測和使用者代理檢測,通過這三種檢測方案,我們可以充分的瞭解當前瀏覽器所處系統、所支援的語法、所具有的特殊效能。

1. 能力檢測

能力檢測又稱作為特性檢測,檢測的目標不是識別特定的瀏覽器,而是識別瀏覽器的能力。能力檢測不必估計特定的瀏覽器,只需要確定當前的瀏覽器是否支援特定的能力,就可以給出可行的解決方案。

// BOM 章節的一段程式
var width = window.innerWidth; 							// 如果是非 IE 瀏覽器
if (typeof width != 'number') { 						// 如果是 IE,就使用 document
	if (document.compatMode == 'CSS1Compat') {
		width = document.documentElement.clientWidth;
	} else {
		width = document.body.clientWidth; 				// 非標準模式使用 body
	}
}

上面其實有兩塊地方使用了能力檢測,第一個就是是否支援 innerWidth 的檢測,第二個就是是否是標準模式的檢測,這兩個都是能力檢測。

2. 怪癖檢測(bug 檢測)

與能力檢測類似,怪癖檢測的目標是識別瀏覽器的特殊行為。但與能力檢測確認瀏覽器支援什麼能力不同,怪癖檢測是想要知道瀏覽器存在什麼缺陷(bug)。bug 一般屬於個別瀏覽器獨有,在大多數新版本的瀏覽器被修復。在後續的開發過程中 ,如果遇到瀏覽器 bug 我們再詳細探討。

var box = {
	toString : function () {} 		// 建立一個 toString(),和原型中重名了
};
for (var o in box) {
	alert(o); 						// IE 瀏覽器的一個 bug,不識別了
}

3. 使用者代理檢測

使用者代理檢測通過檢測使用者代理字串來確定實際使用的瀏覽器。在每一次 HTTP 請求過程中,使用者代理字串是作為響應首部傳送的,而且該字串可以通過 JavaScript 的 navigator.userAgent 屬性訪問。
使用者代理代理檢測,主要通過 navigator.userAgent 來獲取使用者代理字串的,通過這組字串,我們來獲取當前瀏覽器的版本號、瀏覽器名稱、系統名稱。
在伺服器端,通過檢測使用者代理字串確定使用者使用的瀏覽器是一種比較廣為接受的做法。但在客戶端,這種測試被當作是一種萬不得已的做法,且飽受爭議,其優先順序排在能力檢測或怪癖檢測之後。飽受爭議的原因,是因為它具有一定的欺騙性。

// 得到使用者代理字串
document.write(navigator.userAgent);

// Firefox14.0.1
Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1

// Firefox3.6.28
Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.28) Gecko/20120306 Firefox/3.6.28

// Chrome20.0.1132.57 m
Mozilla/5.0 (Windows NT 5.1)AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11

// Safari5.1.7
Mozilla/5.0 (Windows NT 5.1)AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2

// IE7.0
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)

// IE8.0
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)

// IE6.0
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)

// Opera12.0
Opera/9.80 (Windows NT 5.1; U; zh-cn) Presto/2.10.289 Version/12.00

// Opera7.54
Opera/7.54 (Windows NT 5.1; U) [en]

// Opera8
Opera/8.0 (Window NT 5.1; U; en)

// Konqueror(Linux 整合,基於 KHTML 呈現引擎的瀏覽器)
Mozilla/5.0 (compatible; Konqueror/3.5; SunOS) KHTML/3.5.0 (like Gecko)

只要仔細的閱讀這些字串,我們可以發現,這些字串包含了瀏覽器的名稱、版本和所宿主的作業系統。
每個瀏覽器有它自己的呈現引擎,所謂呈現引擎,就是用來排版網頁和解釋瀏覽器的引擎。通過代理字串發現,我們歸納出瀏覽器對應的引擎:

  • IE:Trident, IE8 體現出來了,之前的未體現
  • Firefox:Gecko
  • Opera:Presto, 舊版本根本無法體現呈現引擎
  • Chrome:WebKit WebKit 是 KHTML 呈現引擎的一個分支,後獨立開來
  • Safari:WebKit
  • Konqueror:KHTML

由上面的情況,我們需要檢測呈現引擎可以分為五大類:IE、Gecko、WebKit、KHTML 和 Opera。

var client = function () { 		// 建立一個物件
	var engine = { 				// 呈現引擎
		ie : false,
		gecko : false,
		webkit : false,
		khtml : false,
		opera : false,
		ver : 0 				// 具體的版本號
	};
	return {
		engine : engine 		// 返回呈現引擎物件
	};
}(); 							// 自我執行
alert(client.engine.ie); 		// 獲取 ie

以上的程式碼實現了五大引擎的初始化工作,分別給予 true 的初值,並且設定版本號為 0。
下面我們首先要做的是判斷 Opera,因為 Opera 瀏覽器支援 window.opera 物件,通過這個物件,我們可以很容易獲取到 Opera 的資訊。

for (var p in window.opera) { 				// 獲取 window.opera 物件資訊
	document.write(p + "<br />");
}
if (window.opera) { 						// 判斷 opera 瀏覽器
	engine.ver = window.opera.version(); 	// 獲取 opera 呈現引擎版本
	engine.opera = true; 					// 設定真
}

接下來,我們通過正規表示式來獲取 WebKit 引擎和它的版本號。

else if (/AppleWebKit\/(\S+)/.test(ua)) { 	// 正則 WebKit
	engine.ver = RegExp['$1']; 				// 獲取 WebKit 版本號
	engine.webkit = true;

然後,我們通過正規表示式來獲取 KHTML 引擎和它的版本號。由於這款瀏覽器基於 Linux,我們無法測試。

// 獲取 KHTML 和它的版本號
else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) {
	engine.ver = RegExp['$1'];
	engine.khtml = true;
}

下面,我們通過正規表示式來獲取 Gecko 引擎和它的版本號。

else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) { 	// 獲取 Gecko 和它的版本號
	engine.ver = RegExp['$1'];
	engine.gecko = true;
}

最後,我們通過正規表示式來獲取 IE 的引擎和它的版本號。因為 IE8 之前沒有呈現引擎,所以,我們只有通過"MSIE"這個共有的字串來獲取。

else if (/MSIE ([^;]+)/.test(ua)) { 				// 獲取 IE 和它的版本號
	engine.ver = RegExp['$1'];
	engine.ie = true;
}

上面獲取各個瀏覽器的引擎和引擎的版本號,但大家也發現了,其實有些確實是瀏覽器的版本號。所以,下面,我們需要進行瀏覽器名稱的獲取和瀏覽器版本號的獲取。
根據目前的瀏覽器市場份額,我們可以給一下瀏覽器做檢測:IE、Firefox、konq、opera 、chrome、safari。

var browser = { 			// 瀏覽器物件
	ie : false,
	firefox : false,
	konq : false,
	opera : false,
	chrome : false,
	safari : false,
	ver : 0, 				// 具體版本
	name : '' 				// 具體的瀏覽器名稱
};

對於獲取 IE 瀏覽器的名稱和版本,可以直接如下:

else if (/MSIE ([^;]+)/.test(ua)) {
	engine.ver = browser.ver = RegExp['$1']; 	// 設定版本
	engine.ie = browser.ie = true; 				// 填充保證為 true
	browser.name = 'Internet Explorer'; 		// 設定名稱
}

對於獲取 Firefox 瀏覽器的名稱和版本,可以如下:

else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {
	engine.ver = RegExp['$1'];
	engine.gecko = true;
	if (/Firefox\/(\S+)/.test(ua)) {
		browser.ver = RegExp['$1']; 			// 設定版本
		browser.firefox = true; 				// 填充保證為 true
		browser.name = 'Firefox'; 				// 設定名稱
	}
}

對於獲取 Chrome 和 safari 瀏覽器的名稱和版本,可以如下:

else if (/AppleWebKit\/(\S+)/.test(ua)) {
	engine.ver = RegExp['$1'];
	engine.webkit = parseFloat(engine.ver);
	if (/Chrome\/(\S+)/.test(ua)) {
		browser.ver = RegExp['$1'];
		browser.chrome = true;
		browser.name = 'Chrome';
	} else if (/Version\/(\S+)/.test(ua)) {
		browser.ver = RegExp['$1'];
		browser.chrome = true;
		browser.name = 'Safari';
	}
}

對於 Safari3 之前的低版本,需要做 WebKit 的版本號近似對映。
瀏覽器的名稱和版本號,我們已經準確的獲取到,最後,我們想要去獲取瀏覽器所宿主的作業系統。

var system = { 												// 作業系統
	win : false, 											// windows
	mac : false, 											// Mac
	x11 : false 											// Unix、Linux
};
var p = navigator.platform; 								// 獲取系統
system.win = p.indexOf('Win') == 0; 						// 判斷是否是 windows
system.mac = p.indexOf('Mac') == 0; 						// 判斷是否是 mac
system.x11 = (p == 'X11') || (p.indexOf('Linux') == 0) 		// 判斷是否是 Unix、Linux

這裡我們也可以通過使用者代理字串獲取到 windows 相關的版本。

Windows版本IE4+GeckoOpera < 7Opera 7+WebKit
95“Windows 95”“Win95”“Windows 95”“Windows 95”n/a
98“Windows 98”“Win98”“Windows 98”“Windows 98”n/a
NT4.0“Windows NT”“WinNT4.0”“Windows NT 4.0”“Windows NT 4.0”n/a
2000“Windows NT 5.0”“Windows NT5.0”“Windows 2000”“Windows NT 5.0”n/a
ME“Win 9X 4.90”“Win 9x 4.90”“Windows ME”“Win 9X 4.90”n/a
XP“Windows NT 5.1”“Windows NT 5.1”“Windows XP”“Windows NT 5.1”“Windows NT 5.1”
Vista“Windows NT 6.0”“Windows NT 6.0”n/a“Windows NT 6.0”“Windows NT 6.0”
7“Windows NT 6.1”“Windows NT 6.1”n/a“Windows NT 6.1”“Windows NT 6.1”

3、DOM 介紹

DOM(Document Object Model)即文件物件模型,針對 HTML 和 XML 文件的 API(應用程式介面)。DOM 描繪了一個層次化的節點樹,執行開發人員新增、移除和修改頁面的某一部分。DOM 脫胎於 Netscape 及微軟公司創始的 DHTML(動態 HTML),但現在它已經成為表現和操作頁面標記的真正跨平臺、語言中立的方式。
DOM 中的三個字母,D(文件)可以理解為整個 Web 載入的網頁文件;O(物件)可以理解為類似 window 物件之類的東西,可以呼叫屬性和方法,這裡我們說的是 document 物件;M(模型)可以理解為網頁文件的樹型結構。
DOM 有三個等級,分別是 DOM1、DOM2、DOM3,並且 DOM1 在 1998 年 10 月成為 W3C 標準。DOM1 所支援的瀏覽器包括 IE6+、Firefox、Safari、Chrome 和 Opera1.7+。
IE 中的所有 DOM 物件都是以 COM 物件的形式實現的,這意味著 IE 中的 DOM 可能會和其他瀏覽器有一定的差異。

1. 節點

載入 HTML 頁面時,Web 瀏覽器生成一個樹型結構,用來表示頁面內部結構。DOM 將這種樹型結構理解為由節點組成。

從上圖的樹型結構,我們理解幾個概念,html 標籤沒有父輩,沒有兄弟,所以 html 標籤為根標籤。head 標籤是 html 子標籤,meta 和 title 標籤之間是兄弟關係。如果把每個標籤當作一個節點的話,那麼這些節點組合成了一棵節點樹。
後面我們經常把標籤稱作為元素,是一個意思。

2. 節點種類

節點種類分為元素節點、文字節點、屬性節點。

4、查詢元素

W3C 提供了比較方便簡單的定位節點的方法和屬性,以便我們快速的對節點進行操作 。分別為:getElementById()、getElementsByTagName()、getElementsByName()、getAttribute() 、setAttribute()和 removeAttribute()。
元素節點方法:

方法說明
getElementById()獲取特定 ID 元素的節點
getElementsByTagName()獲取相同元素的節點列表
getElementsByName()獲取相同名稱的節點列表
getAttribute()獲取特定元素節點屬性的值
setAttribute()設定特定元素節點屬性的值
removeAttribute()移除特定元素節點屬性

1. getElementById()方法

getElementById()方法,接受一個引數:獲取元素的 ID。如果找到相應的元素則返回該元素的 HTMLDivElement 物件,如果不存在,則返回 null。

document.getElementById('box'); 			// 獲取 id 為 box 的元素節點

上面的例子,預設情況返回 null,這無關是否存在 id="box"的標籤,而是執行順序問題。解決方法,(1)把 script 呼叫標籤移到 html 末尾即可;(2)使用 onload 事件來處理 JS,等待 html 載入完畢再載入 onload 事件裡的 JS。

window.onload = function () { 				// 預載入 html 後執行
	document.getElementById('box');
};

id 表示一個元素節點的唯一性,不能同時給兩個或以上的元素節點建立同一個命名的 id。某些低版本的瀏覽器會無法識別 getElementById()方法,比如 IE5.0-,這時需要做一些判斷,可以結合上章的瀏覽器檢測來操作。

if (document.getElementById) { 				// 判斷是否支援 getElementById
	alert('當前瀏覽器支援 getElementById');
}

當我們通過 getElementById()獲取到特定元素節點時,這個節點物件就被我們獲取到了 ,而通過這個節點物件,我們可以訪問它的一系列屬性。
元素節點屬性:

屬性說明
tagName獲取元素節點的標籤名
innerHTML獲取元素節點裡的內容,非 W3C DOM 規範
document.getElementById('box').tagName; 		// DIV
document.getElementById('box').innerHTML; 		// 測試 Div

HTML 屬性:

屬性說明
id元素節點的 id 名稱
title元素節點的 title 屬性值
styleCSS 內聯樣式屬性值
classNameCSS 元素的類
document.getElementById('box').id; 						// 獲取 id
document.getElementById('box').id = 'person'; 			// 設定 id
document.getElementById('box').title; 					// 獲取 title
document.getElementById('box').title = '標題' 			// 設定 title
document.getElementById('box').style; 					// 獲取 CSSStyleDeclaration 物件
document.getElementById('box').style.color; 			// 獲取 style 物件中 color 的值
document.getElementById('box').style.color = 'red'; 	// 設定 style 物件中 color 的值
document.getElementById('box').className; 				// 獲取 class
document.getElementById('box').className = 'box'; 		// 設定 class
alert(document.getElementById('box').bbb); 				// 獲取自定義屬性的值,非 IE 不支援

2. getElementsByTagName()方法

getElementsByTagName()方法將返回一個物件陣列HTMLCollection(NodeList),這個陣列儲存著所有相同元素名的節點列表。

document.getElementsByTagName('*'); 				// 獲取所有元素

IE 瀏覽器在使用萬用字元的時候,會把文件最開始的 html 的規範宣告當作第一個元素節點。

document.getElementsByTagName('li'); 				// 獲取所有 li 元素,返回陣列
document.getElementsByTagName('li')[0]; 			// 獲取第一個 li 元素,HTMLLIElement
document.getElementsByTagName('li').item(0) 		// 獲取第一個 li 元素,HTMLLIElement
document.getElementsByTagName('li').length; 		// 獲取所有 li 元素的數目

不管是 getElementById 還是 getElementsByTagName,在傳遞引數的時候,並不是所有瀏覽器都必須區分大小寫,為了防止不必要的錯誤和麻煩,我們必須堅持養成區分大小寫的習慣。

3. getElementsByName()方法

getElementsByName()方法可以獲取相同名稱(name)的元素,返回一個物件陣列 HTMLCollection(NodeList)。

document.getElementsByName('add') 					// 獲取 input 元素
document.getElementsByName('add')[0].value 			// 獲取 input 元素的 value 值
document.getElementsByName('add')[0].checked		// 獲取 input 元素的 checked 值

對於並不是 HTML 合法的屬性,那麼在 JS 獲取的相容性上也會存在差異,IE 瀏覽器支援本身合法的 name 屬性,而不合法的就會出現不相容的問題。

4. getAttribute()方法

getAttribute()方法將獲取元素中某個屬性的值。它和直接使用.屬性獲取屬性值的方法有一定區別。

document.getElementById('box').getAttribute('id');			// 獲取元素的 id 值
document.getElementById('box').id; 							// 獲取元素的 id 值
document.getElementById('box').getAttribute('mydiv');		// 獲取元素的自定義屬性值
document.getElementById('box').mydiv 						// 獲取元素的自定義屬性值,非 IE 不支援
document.getElementById('box').getAttribute('class');		// 獲取元素的 class 值,IE 不支援
document.getElementById('box').getAttribute('className');	// 非 IE 不支援

HTML 通用屬性 style 和 onclick,IE7 更低的版本 style 返回一個物件,onclick 返回一個函式式。雖然 IE8 已經修復這個 bug,但為了更好的相容,開發人員只有儘可能避免使用 getAttribute()訪問 HTML 屬性了,或者碰到特殊的屬性獲取做特殊的相容處理。

5. setAttribute()方法

setAttribute()方法將設定元素中某個屬性和值。它需要接受兩個引數:屬性名和值。如果屬性本身已存在,那麼就會被覆蓋。

document.getElementById('box').setAttribute('align','center');		// 設定屬性和值
document.getElementById('box').setAttribute('bbb','ccc');			// 設定自定義的屬性和值

在 IE7 及更低的版本中,使用 setAttribute()方法設定 class 和 style 屬性是沒有效果的,雖然 IE8 解決了這個 bug,但還是不建議使用。

6. removeAttribute()方法

removeAttribute()可以移除 HTML 屬性。

document.getElementById('box').removeAttribute('style');		// 移除屬性

IE6 及更低版本不支援 removeAttribute()方法。

5、DOM 節點

1. node 節點屬性

節點可以分為元素節點、屬性節點和文字節點,而這些節點又有三個非常有用的屬性 ,分別為:nodeName、nodeType 和 nodeValue。
資訊節點屬性:

節點型別nodeNamenodeTypenodeValue
元素元素名稱1null
屬性屬性名稱2屬性值
文字#text3文字內容(不包含 html)
document.getElementById('box').nodeType; 		// 1,元素節點

2. 層次節點屬性

節點的層次結構可以劃分為:父節點與子節點、兄弟節點這兩種。當我們獲取其中一個元素節點的時候,就可以使用層次節點屬性來獲取它相關層次的節點。
層次節點屬性:

屬性說明
childNodes獲取當前元素節點的所有子節點
firstChild獲取當前元素節點的第一個子節點
lastChild獲取當前元素節點的最後一個子節點
ownerDocument獲取該節點的文件根節點,相當於 document
parentNode獲取當前節點的父節點
previousSibling獲取當前節點的前一個同級節點
nextSibling獲取當前節點的後一個同級節點
attributes獲取當前元素節點的所有屬性節點集合
  1. childNodes 屬性

childeNodes 屬性可以獲取某一個元素節點的所有子節點,這些子節點包含元素子節點和文字子節點。

var box = document.getElementById('box'); 	// 獲取一個元素節點
alert(box.childNodes.length); 				// 獲取這個元素節點的所有子節點
alert(box.childNodes[0]); 					// 獲取第一個子節點物件

使用 childNodes[n]返回子節點物件的時候,有可能返回的是元素子節點,比如HTMLElement;也有可能返回的是文字子節點,比如 Text。元素子節點可以使用 nodeName或者 tagName 獲取標籤名稱,而文字子節點可以使用 nodeValue 獲取。

for (var i = 0; i < box.childNodes.length; i ++) {
	// 判斷是元素節點,輸出元素標籤名
	if (box.childNodes[i].nodeType === 1) {
		alert('元素節點:' + box.childNodes[i].nodeName);
	// 判斷是文字節點,輸出文字內容
	} else if (box.childNodes[i].nodeType === 3) {
		alert('文字節點:' + box.childNodes[i].nodeValue);
	}
}

在獲取到文字節點的時候,是無法使用 innerHTML 這個屬性輸出文字內容的。這個非標準的屬性必須在獲取元素節點的時候,才能輸出裡面包含的文字。

alert(box.innerHTML); 										// innerHTML 和 nodeValue 第一個區別

innerHTML 和 nodeValue 第一個區別,就是取值的。那麼第二個區別就是賦值的時候,nodeValue 會把包含在文字里的 HTML 轉義成特殊字元,從而達到形成單純文字的效果。

box.childNodes[0].nodeValue = '<strong>abc</strong>';		// 結果為:<strong>abc</strong>
box.innerHTML = '<strong>abc</strong>'; 					// 結果為: abc
  1. firstChild 和 lastChild 屬性

firstChild 用於獲取當前元素節點的第一個子節點,相當於 childNodes[0];lastChild 用於獲取當前元素節點的最後一個子節點,相當於 childNodes[box.childNodes.length - 1]。

alert(box.firstChild.nodeValue); 							// 獲取第一個子節點的文字內容
alert(box.lastChild.nodeValue); 							// 獲取最後一個子節點的文字內容
  1. ownerDocument 屬性

ownerDocument 屬性返回該節點的文件物件根節點,返回的物件相當於 document。

alert(box.ownerDocument === document); 						// true,根節點
  1. parentNode、previousSibling、nextSibling 屬性

parentNode 屬性返回該節點的父節點,previousSibling 屬性返回該節點的前一個同級節點,nextSibling 屬性返回該節點的後一個同級節點。

alert(box.parentNode.nodeName); 							// 獲取父節點的標籤名
alert(box.lastChild.previousSibling); 						// 獲取前一個同級節點
alert(box.firstChild.nextSibling); 							// 獲取後一個同級節點
  1. attributes 屬性

attributes 屬性返回該節點的屬性節點集合。

document.getElementById('box').attributes 						// NamedNodeMap
document.getElementById('box').attributes.length;				// 返回屬性節點個數
document.getElementById('box').attributes[0]; 					// Attr,返回最後一個屬性節點
document.getElementById('box').attributes[0].nodeType; 			// 2,節點型別
document.getElementById('box').attributes[0].nodeValue; 		// 屬性值
document.getElementById('box').attributes['id']; 				// Attr,返回屬性為 id 的節點
document.getElementById('box').attributes.getNamedItem('id'); 	// Attr
  1. 忽略空白文字節點
var body = document.getElementsByTagName('body')[0];	// 獲取 body 元素節點
alert(body.childNodes.length); 							// 得到子節點個數,IE3 個,非 IE7 個

在非 IE 中,標準的 DOM 具有識別空白文字節點的功能,所以在火狐瀏覽器是 7 個,而 IE 自動忽略了,如果要保持一致的子元素節點,需要手工忽略掉它。

function filterSpaceNode(nodes) {
	var ret = []; 		// 新陣列
	for (var i = 0; i < nodes.length; i ++) {
		// 如果識別到空白文字節點,就不新增陣列
		if (nodes[i].nodeType == 3 && /^\s+$/.test(nodes[i].nodeValue)) continue;
		// 把每次的元素節點,新增到陣列裡
		ret.push(nodes[i]);
	}
	return ret;
}

上面的方法,採用的忽略空白檔案節點的方法,把得到元素節點累加到陣列裡返回。那麼還有一種做法是,直接刪除空位檔案節點即可。

function filterSpaceNode(nodes) {
	for (var i = 0; i < nodes.length; i ++) {
		if (nodes[i].nodeType == 3 && /^\s+$/.test(nodes[i].nodeValue)) {
			// 得到空白節點之後,移到父節點上,刪除子節點
			nodes[i].parentNode.removeChild(nodes[i]);
		}
	}
	return nodes;
}

如果 firstChild、lastChild、previousSibling 和 nextSibling 在獲取節點的過程中遇到空白節點,我們該怎麼處理掉呢?

function removeWhiteNode(nodes) {
	for (var i = 0; i < nodes.childNodes.length; i ++) {
		if (nodes.childNodes[i].nodeType === 3 && /^\s+$/.test(nodes.childNodes[i].nodeValue)) {
			nodes.childNodes[i].parentNode.removeChild(nodes.childNodes[i]);
		}
	}
	return nodes;
}

6、節點操作

DOM 不單單可以查詢節點,也可以建立節點、複製節點、插入節點、刪除節點和替換節點。
節點操作方法:

方法說明
write()這個方法可以把任意字串插入到文件中
createElement()建立一個元素節點
appendChild()將新節點追加到子節點列表的末尾
createTextNode()建立一個檔案節點
insertBefore()將新節點插入在前面
repalceChild()將新節點替換舊節點
cloneNode()複製節點
removeChild()移除節點

1. write()方法

write()方法可以把任意字串插入到文件中去。

document.write('<p>這是一個段落!</p>')'; 		// 輸出任意字串

2. createElement()方法

createElement()方法可以建立一個元素節點。

document.createElement('p'); 					// 建立一個元素節點

3. appendChild()方法

appendChild()方法講一個新節點新增到某個節點的子節點列表的末尾上。

var box = document.getElementById('box'); 		// 獲取某一個元素節點
var p = document.createElement('p'); 			// 建立一個新元素節點<p>
box.appendChild(p); 							// 把新元素節點<p>新增子節點末尾

4. createTextNode()方法

createTextNode()方法建立一個文字節點。

var text = document.createTextNode('段落'); 		// 建立一個文字節點
p.appendChild(text); 							// 將文字節點新增到子節點末尾

5. insertBefore()方法

insertBefore()方法可以把節點建立到指定節點的前面。

box.parentNode.insertBefore(p, box); 			// 把<div>之前建立一個節點

insertBefore()方法可以給當前元素的前面建立一個節點,但卻沒有提供給當前元素的後面建立一個節點。那麼,我們可以用已有的知識建立一個 insertAfter()函式。

function insertAfter(newElement, targetElement) {
	// 得到父節點
	var parent = targetElement.parentNode;
	// 如果最後一個子節點是當前元素,那麼直接新增即可
	if (parent.lastChild === targetElement) {
		parent.appendChild(newElement);
	} else {
		// 否則,在當前節點的下一個節點之前新增
		parent.insertBefore(newElement, targetElement.nextSibling);
	}
}

createElement 在建立一般元素節點的時候,瀏覽器的相容性都還比較好。但在幾個特殊標籤上,比如 iframe、input 中的 radio 和 checkbox、button 元素中,可能會在 IE6,7以下的瀏覽器存在一些不相容。

var input = null;
if (BrowserDetect.browser == 'Internet Explorer' && BrowserDetect.version <= 7) {
	// 判斷 IE6,7,使用字串的方式
	input = document.createElement("<input type=\"radio\" name=\"sex\">");
} else {
	// 標準瀏覽器,使用標準方式
	input = document.createElement('input');
	input.setAttribute('type', 'radio');
	input.setAttribute('name', 'sex');
}
document.getElementsByTagName('body')[0].appendChild(input);

6. repalceChild()方法

replaceChild()方法可以把節點替換成指定的節點。

box.parentNode.replaceChild(p,box); 			// 把<div>換成了<p>

7. cloneNode()方法

cloneNode()方法可以把子節點複製出來。

var box = document.getElementById('box');
var clone = box.firstChild.cloneNode(true); 	// 獲取第一個子節點,true 表示複製內容
box.appendChild(clone); 						// 新增到子節點列表末尾

8. removeChild()方法

removeChild()方法可以把指定節點刪除。

box.parentNode.removeChild(box); 				// 刪除指定節點

相關文章