JavaScript中的浏览器检测和DOM基础
1、navigator 对象
由于每个浏览器都具有自己独到的扩展,所以在开发阶段来判断浏览器是一个非常重要的步骤。虽然浏览器开发商在公共接口方面投入了很多精力,努力的去支持最常用的公共功能;但在现实中,浏览器之间的差异,以及不同浏览器的“怪癖”却是非常多的,因此客户端检测除了是一种补救措施,更是一种行之有效的开发策略。 navigator 对象最早由 Netscape Navigator2.0 引入的 navigator 对象,现在已经成为识别客户端浏览器的事实标准。与之前的 BOM 对象一样,每个浏览器中 的 navigator 对象也都有一套自己的属性。 navigator 对象的属性或方法:
属性或方法 |
说明 |
IE |
Firefox |
Safari/Chrome |
Opera |
---|---|---|---|---|---|
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() |
表示当前浏览器中是否启用了Java |
4.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 |
插件的磁盘文件名 |
length |
plugins 数组的元素个数 |
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 对象的属性:
属性 |
含义 |
---|---|
type |
MIME 类型名 |
description |
MIME 类型的描述信息 |
enabledPlugin |
指定 MIME 类型配置好的 plugin 对象引用 |
suffixes |
MIME 类型所有可能的文件扩展名 |
// 遍历非 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+ |
Gecko |
Opera < 7 |
Opera 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 属性值 |
style |
CSS 内联样式属性值 |
className |
CSS 元素的类 |
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。 信息节点属性:
节点类型 |
nodeName |
nodeType |
nodeValue |
---|---|---|---|
元素 |
元素名称 |
1 |
null |
属性 |
属性名称 |
2 |
属性值 |
文本 |
#text |
3 |
文本内容(不包含 html) |
document.getElementById('box').nodeType; // 1,元素节点
2. 层次节点属性
节点的层次结构可以划分为:父节点与子节点、兄弟节点这两种。当我们获取其中一个元素节点的时候,就可以使用层次节点属性来获取它相关层次的节点。 层次节点属性:
属性 |
说明 |
---|---|
childNodes |
获取当前元素节点的所有子节点 |
firstChild |
获取当前元素节点的第一个子节点 |
lastChild |
获取当前元素节点的最后一个子节点 |
ownerDocument |
获取该节点的文档根节点,相当于 document |
parentNode |
获取当前节点的父节点 |
previousSibling |
获取当前节点的前一个同级节点 |
nextSibling |
获取当前节点的后一个同级节点 |
attributes |
获取当前元素节点的所有属性节点集合 |
- 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
- firstChild 和 lastChild 属性
firstChild 用于获取当前元素节点的第一个子节点,相当于 childNodes[0];lastChild 用于获取当前元素节点的最后一个子节点,相当于 childNodes[box.childNodes.length - 1]。
alert(box.firstChild.nodeValue); // 获取第一个子节点的文本内容
alert(box.lastChild.nodeValue); // 获取最后一个子节点的文本内容
- ownerDocument 属性
ownerDocument 属性返回该节点的文档对象根节点,返回的对象相当于 document。
alert(box.ownerDocument === document); // true,根节点
- parentNode、previousSibling、nextSibling 属性
parentNode 属性返回该节点的父节点,previousSibling 属性返回该节点的前一个同级节点,nextSibling 属性返回该节点的后一个同级节点。
alert(box.parentNode.nodeName); // 获取父节点的标签名
alert(box.lastChild.previousSibling); // 获取前一个同级节点
alert(box.firstChild.nextSibling); // 获取后一个同级节点
- 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
- 忽略空白文本节点
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); // 删除指定节点
- Leetcode-Easy 572. Subtree of Another Tree
- 图解javascript this指向什么?
- 2017/6/8-python正则表达式的使用
- 洛谷P1306 斐波那契公约数
- Angular开发实践(二):HRM运行机制
- Angular开发实践(一):环境准备及框架搭建
- 洛谷P2818 天使的起誓
- 连续子数组的最大和
- 10.25解题报告
- React第三方组件5(状态管理之Redux的使用①简单使用)
- JavaScript设计模式与开发实践 - 观察者模式
- React第三方组件4(状态管理之Reflux的使用⑤异步操作)
- Leetcode-Easy 804. Unique Morse Code Words
- JavaScript设计模式与开发实践 - 策略模式
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python 中的数字到底是什么?
- 详解 Python 的二元算术运算,为什么说减法只是语法糖?
- 详解增强算术赋值:“-=”操作是怎么实现的?
- Hyperledger Explorer 环境搭建详解
- [译]在Solidity中创建无限制列表
- java安全编码指南之:声明和初始化
- java安全编码指南之:表达式规则
- java安全编码指南之:Number操作
- 如何提高代码质量
- 小姐姐非要问我:spring编程式事务是啥?
- 阿里3面:Spring声明式事务连环炮,让我措手不及。。
- 旷视科技|商用端侧Raw图像降噪方案
- Python | 时间戳转换
- Stata | 2020年国家社科基金立项名单分析
- 还在用Guava Cache?它才是Java本地缓存之王