DOM(Document Object Model)即文档对象模型,针对 HTML 和 XML 文档的 API(应用程序接口) 。DOM 描绘了一个层次化的节点树,运行开发人员添加、移除和修改页面的某一部分。DOM 脱胎于 Netscape 及微软公司创始的 DHTML(动态 HTML) ,但现在它已经成为表现和操作页面标记的真正跨平台、语言中立的方式。
DOM 中的三个字母:
DOM 有三个等级,分别是 DOM1、DOM2、DOM3,并且 DOM1 在 1998 年 10 月成为W3C 标准。DOM1 所支持的浏览器包括 IE6+、Firefox、Safari、Chrome 和 Opera1.7+。
PS:IE 中的所有 DOM 对象都是以 COM 对象的形式实现的,这意味着 IE 中的 DOM可能会和其他浏览器有一定的差异。
加载 HTML 页面时,Web 浏览器生成一个树型结构,用来表示页面内部结构。DOM 将这种树型结构理解为由节点组成。
从上图的树型结构,我们理解几个概念,html 标签没有父辈,没有兄弟,所以 html 标签为根标签。head 标签是 html 子标签,meta 和 title 标签之间是兄弟关系。如果把每个标签当作一个节点的话,那么这些节点组合成了一棵节点树。
PS:后面我们经常把标签称作为元素,是一个意思。
元素节点、文本节点、属性节点。
< div title=”属性节点”>测试 Div< /div>
W3C 提供了比较方便简单的定位节点的方法和属性, 以便我们快速的对节点进行操作 。
分别为:
元素节点方法
方法 | 说明 |
---|---|
getElementById() | 获取特定 ID 元素的节点 |
getElementsByTagName() | 获取相同元素的节点列表 |
getElementsByName() | 获取相同名称的节点列表 |
getAttribute() | 获取特定元素节点属性的值 |
setAttribute() | 设置特定元素节点属性的值 |
removeAttribute() | 移除特定元素节点属性 |
getElementById()方法,接受一个参数:获取元素的 ID。如果找到相应的元素则返回该元素的 HTMLDivElement 对象,如果不存在,则返回 null。
document.getElementById('box'); //获取 id 为 box 的元素节点
PS:上面的例子,默认情况返回 null,这无关是否存在 id=”box”的标签,而是执行顺序问题。解决方法:
window.onload = function () { //预加载 html 后执行
document.getElementById('box');
};
PS:id 表示一个元素节点的唯一性,不能同时给两个或以上的元素节点创建同一个命名的 id。某些低版本的浏览器会无法识别 getElementById()方法,比如 IE5.0-,这时需要做一些判断,可以结合上章的浏览器检测来操作。
if (document.getElementById) { //判断是否支持 getElementById
alert('当前浏览器支持 getElementById');
}
当我们通过 getElementById()获取到特定元素节点时, 这个节点对象就被我们获取到了 ,而通过这个节点对象,我们可以访问它的一系列属性。
getElementsByTagName()方法将返回一个对象数组HTMLCollection(NodeList),这个数组保存着所有相同元素名的节点列表。
document.getElementsByTagName('*'); //获取所有元素
**PS:IE 浏览器在使用通配符的时候,会把文档最开始的 html 的规范声明当第一个元素节点。**
document.getElementsByTagName('li'); //获取所有 li 元素,返回数组
document.getElementsByTagName('li')[0]; //获取第一个 li 元素,HTMLLIElement
document.getElementsByTagName('li').item(0) //获取第一个 li 元素,HTMLLIElement
document.getElementsByTagName('li').length; //获取所有 li 元素的数目
PS:不管是 getElementById 还是 getElementsByTagName,在传递参数的时候,并不是所有浏览器都必须区分大小写, 为了防止不必要的错误和麻烦, 我们必须坚持养成区分大小写的习惯。
getElementsByName()方法可以获取相同名称(name)的元素,返回一个对象数组HTMLCollection(NodeList)。
document.getElementsByName('add') //获取 input 元素
document.getElementsByName('add')[0].value //获取 input 元素的 value 值
document.getElementsByName('add')[0].checked//获取 input 元素的 checked 值
PS:对于并不是 HTML 合法的属性,那么在 JS 获取的兼容性上也会存在差异,IE 浏览器支持本身合法的 name 属性,而不合法的就会出现不兼容的问题。
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 不支持
PS:HTML 通用属性 style 和 onclick,IE7 更低的版本 style 返回一个对象,onclick 返回一个函数式。虽然 IE8 已经修复这个 bug,但为了更好的兼容,开发人员只有尽可能避免使用 getAttribute()访问 HTML 属性了,或者碰到特殊的属性获取做特殊的兼容处理。
setAttribute()方法将设置元素中某个属性和值。它需要接受两个参数:属性名和值。如果属性本身已存在,那么就会被覆盖。
document.getElementById('box').setAttribute('align','center');//设置属性和值
document.getElementById('box').setAttribute('bbb','ccc');//设置自定义的属性和值
PS:在 IE7 及更低的版本中,使用 setAttribute()方法设置 class 和 style 属性是没有效果的,虽然 IE8 解决了这个 bug,但还是不建议使用。
removeAttribute()可以移除 HTML 属性。
document.getElementById('box').removeAttribute('style');//移除属性
PS:IE6 及更低版本不支持 removeAttribute()方法。
节点可以分为元素节点、属性节点和文本节点,而这些节点又有三个非常有用的属性 ,分别为:nodeName、nodeType 和 nodeValue。
信息节点属性
节点类型 | nodeName | nodeType | nodeValue |
---|---|---|---|
元素 | 元素名称 | 1 | null |
属性 | 属性名称 | 2 | 属性值 |
文本 | text | 3 | 文本内容(不包含 html) |
document.getElementById('box').nodeType; //1,元素节点
属性 | 说明 |
---|---|
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 不支持
节点的层次结构可以划分为:父节点与子节点、兄弟节点这两种。当我们获取其中一个元素节点的时候,就可以使用层次节点属性来获取它相关层次的节点。
层次节点属性
属性 | 说明 |
---|---|
childNodes | 获取当前元素节点的所有子节点 |
firstChild | 获取当前元素节点的第一个子节点 |
lastChild | 获取当前元素节点的最后一个子节点 |
ownerDocument | 获取该节点的文档根节点,相当与 document |
parentNode | 获取当前节点的父节点 |
previousSibling | 获取当前节点的前一个同级节点 |
nextSibling | 获取当前节点的后一个同级节点 |
attributes | 获取当前元素节点的所有属性节点集合 |
childeNodes 属性可以获取某一个元素节点的所有子节点,这些子节点包含元素子节点和文本子节点。
var box = document.getElementById('box'); //获取一个元素节点
alert(box.childNodes.length); //获取这个元素节点的所有子节点
alert(box.childNodes[0]); //获取第一个子节点对象
PS:使用 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); }
}
PS:在获取到文本节点的时候,是无法使用 innerHTML 这个属性输出文本内容的。这个非标准的属性必须在获取元素节点的时候,才能输出里面包含的文本。
alert(box.innerHTML); //innerHTML 和 nodeValue 第一个区别
PS:innerHTML 和 nodeValue 第一个区别,就是取值的。那么第二个区别就是赋值的时候, nodeValue 会把包含在文本里的 HTML 转义成特殊字符, 从而达到形成单纯文本的效果。
box.childNodes[0].nodeValue = '<strong>abc</strong>';//结果为:<strong>abc</strong>
box.innerHTML = '<strong>abc</strong>'; //结果为: abc
firstChild 用于获取当前元素节点的第一个子节点,相当于 childNodes[0]
lastChild 用于获取当前元素节点的最后一个子节点,相当于
childNodes[box.childNodes.length - 1]。
ownerDocument 属性返回该节点的文档对象根节点,返回的对象相当于 document。
alert(box.ownerDocument === document); //true,根节点
parentNode 属性返回该节点的父节点
alert(box.parentNode.nodeName); //获取父节点的标签名
previousSibling 属性返回该节点的前一个同级节点,nextSibling 属性返回该节点的后一个同级节点。
alert(box.lastChild.previousSibling); //获取前一个同级节点
alert(box.firstChild.nextSibling); //获取后一个同级节点
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 个
PS:在非 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;
}
PS:上面的方法,采用的忽略空白文件节点的方法,把得到元素节点累加到数组里返回。那么还有一种做法是,直接删除空位文件节点即可。
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;
}
PS:如果 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;
}
DOM 不单单可以查找节点,也可以创建节点、复制节点、插入节点、删除节点和替换节点。
节点操作方法
方法 | 说明 |
---|---|
write() | 这个方法可以把任意字符串插入到文档中 |
createElement() | 创建一个元素节点 |
createTextNode() | 创建一个文件节点 |
appendChild() | 将新节点追加到子节点列表的末尾 |
insertBefore() | 将新节点插入在前面 |
repalceChild() | 将新节点替换旧节点 |
cloneNode() | 复制节点 |
removeChild() | 移除节点 |
write()方法可以把任意字符串插入到文档中去。
document.write('<p>这是一个段落!</p>')'; //输出任意字符串
createElement()方法可以创建一个元素节点。
document.createElement('p'); //创建一个元素节点
appendChild()方法将一个新节点添加到某个节点的子节点列表的末尾上。
var box = document.getElementById('box'); //获取某一个元素节点
var p = document.createElement('p'); //创建一个新元素节点<p>
box.appendChild(p); //把新元素节点<p>添加子节点末尾
createTextNode()方法创建一个文本节点。
var text = document.createTextNode('段落'); //创建一个文本节点
p.appendChild(text); //将文本节点添加到子节点末尾
insertBefore()方法可以把节点创建到指定节点的前面。
box.parentNode.insertBefore(p, box); //把<div>之前创建一个节点
PS:insertBefore()方法可以给当前元素的前面创建一个节点,但却没有提供给当前元素的后面创建一个节点。那么,我们可以用已有的知识创建一个 insertAfter()函数。
function insertAfter(newElement, targetElement) {
//得到父节点
var parent = targetElement.parentNode;
//如果最后一个子节点是当前元素,那么直接添加即可
if (parent.lastChild === targetElement) {
parent.appendChild(newElement);
} else {
//否则,在当前节点的下一个节点之前添加
parent.insertBefore(newElement, targetElement.nextSibling);
}
}
PS: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);
replaceChild()方法可以把节点替换成指定的节点。
box.parentNode.replaceChild(p,box); //把<div>换成了<p>
cloneNode()方法可以把子节点复制出来。
var box = document.getElementById('box');
var clone = box.firstChild.cloneNode(true); //获取第一个子节点,true 表示复制内容
box.appendChild(clone); //添加到子节点列表末尾
removeChild()方法可以把一个节点移出掉
box.parentNode.removeChild(box); //删除指定节点
DOM 自身存在很多类型,在 前几章中大部分都有所接触
比如 Element 类型 :表示的是元素节点,比如 Text 类型:表示的是文本节点。
DOM 也提供了一些扩展功能。DOM 前几章中,我们了解了 DOM 的节点并且了解怎样查询和操作节点,而本身这些不同的节点,又有着不同的类型。
DOM 类型
类型名 | 说明 |
---|---|
Node | 表示所有类型值的统一接口,IE 不支持 |
Document | 表示文档类型 |
Element | 表示元素节点类型 |
Attr | 表示属性节点类型 |
Text | 表示文本节点类型 |
Comment | 表示文档中的注释类型 |
CDATASection | 表示 CDATA 区域类型 |
DocumentType | 表示文档声明类型 |
DocumentFragment | 表示文档片段类型 |
Node 接口是 DOM1 级就定义了,Node 接口定义了 12 个数值常量以表示每个节点的类型值。除了 IE 之外,所有浏览器都可以访问这个类型。
Node 的常量
常量名 | 说明 | nodeType值 |
---|---|---|
ELEMENT_NODE | 元素 | 1 |
ATTRIBUTE_NODE | 属性 | 2 |
TEXT_NODE | 文本 | 3 |
CDATA_SECTION_NODE | CDATA | 4 |
ENTITY_REFERENCE_NODE | 实体参考 | 5 |
ENTITY_NODE | 实体 | 6 |
PROCESSING_INSTRUCETION_NODE | 处理指令 | 7 |
COMMENT_NODE | 注释 | 8 |
DOCUMENT_NODE | 文档根 | 9 |
DOCUMENT_TYPE_NODE | doctype | 10 |
DOCUMENT_FRAGMENT_NODE | 文档片段 | 11 |
NOTATION_NODE | 符号 | 12 |
虽然这里介绍了 12 种节点对象的属性,用的多的其实也就几个而已。
alert(Node.ELEMENT_NODE); //1,元素节点类型值
alert(Node.TEXT_NODE); //2,文本节点类型值
PS:我们建议使用 Node 类型的属性来代替 1,2 这些阿拉伯数字,有可能大家会觉得这样岂不是很繁琐吗?并且还有一个问题就是 IE 不支持 Node 类型。
如果只有两个属性的话,用 1,2 来代替会特别方便,但如果属性特别多的情况下,1 、2、3、4、5、6、7、8、9、10、11、12,你根本就分不清哪个数字代表的是哪个节点。当然 ,如果你只用 1,2 两个节点,那就另当别论了。
IE 不支持,我们可以模拟一个类,让 IE 也支持
if (typeof Node == 'undefined') { //IE 返回true
window.Node = {
ELEMENT_NODE : 1,
TEXT_NODE : 3
};
}
Document 类型表示文档,或文档的根节点,而这个节点是隐藏的,没有具体的元素标签。
document; //document
document.nodeType; //9,类型值
document.childNodes[0]; //DocumentType,第一个子节点对象
document.childNodes[0].nodeType; //非 IE 为 10,IE 为 8
document.childNodes[1]; //HTMLHtmlElement
document.childNodes[1].nodeName; //HTML
如果想直接得到<html>
标签的元素节点对象 HTMLHtmlElement,不必使用 childNodes属性这么麻烦,可以使用 documentElement 即可。
document.documentElement; //HTMLHtmlElement
在很多情况下, 我们并不需要得到<html>
标签的元素节点, 而需要得到更常用的<body>
标签,之前我们采用的是:document.getElementsByTagName(‘body’)[0],那么这里提供一个更加简便的方法:document.body。
document.body; //HTMLBodyElement
在<html>
之前还有一个文档声明:会作为某些浏览器的第一个节点来处理,这里提供了一个简便方法来处理:document.doctype。
document.doctype; //DocumentType
PS:IE8 中,如果使用子节点访问,IE8 之前会解释为注释类型 Comment 节点,而document.doctype 则会返回 null。
document.childNodes[0].nodeName //IE 会是#Comment
在 Document 中有一些遗留的属性和对象合集,可以快速的帮助我们精确的处理一些任务。
//属性
document.title; //获取和设置<title>标签的值
document.URL; //获取 URL 路径
document.domain; //获取域名,服务器端
document.referrer; //获取上一个 URL,服务器端
//对象集合
document.anchors; //获取文档中带name属性的<a>元素集合
document.links; //获取文档中带 href 属性的<a>元素集合
document.applets; //获取文档中<applet>元素集合,已不用
document.forms; //获取文档中<form>元素集合
document.images; //获取文档中<img>元素集合
Element 类型用于表现 HTML 中的元素节点。在 前面几章,我们已经可以对元素节点进行查找、创建等操作,元素节点的 nodeType 为 1,nodeName 为元素的标签名。
元素节点对象在非 IE 浏览器可以返回它具体元素节点的对象类型。
元素对应类型表
元素名 | 类型 |
---|---|
HTML | HTMLHtmlElement |
DIV | HTMLDivElement |
BODY | HTMLBodyElement |
P | HTMLParamElement |
PS:以上给出了部分对应,更多的元素对应类型,直接访问调用即可。
Text 类型用于表现文本节点类型,文本不包含 HTML,或包含转义后的HTML。文本节点的 nodeType 为 3。
//在同时创建两个同一级别的文本节点的时候,会产生分离的两个节点。
var box = document.createElement('div');
var text = document.createTextNode('Mr.');
var text2 = document.createTextNode(Lee!);
box.appendChild(text);
box.appendChild(text2);
document.body.appendChild(box);
alert(box.childNodes.length); //2,两个文本节点
PS:把两个同邻的文本节点合并在一起使用 normalize()即可。
box.normalize(); //合并成一个节点
PS:有合并就有分离,通过 splitText(num)即可实现节点分离。
box.firstChild.splitText(3); //分离一个节点
除了上面的两种方法外,Text 还提供了一些别的 DOM 操作的方法如下:
var box = document.getElementById('box');
box.firstChild.deleteData(0,2); //删除从 0 位置的 2 个字符
box.firstChild.insertData(0,'Hello.'); //从 0 位置添加指定字符
box.firstChild.replaceData(0,2,'Miss'); //从 0 位置替换掉 2 个指定字符
box.firstChild.substringData(0,2); //从 0 位置获取 2 个字符,直接输出
alert(box.firstChild.nodeValue); //输出结果
Comment 类型表示文档中的注释。nodeType 是 8,nodeName 是#comment,nodeValue是注释的内容。
var box = document.getElementById('box');
alert(box.firstChild); //Comment
PS:在 IE 中,注释节点可以使用!当作元素来访问。
var comment = document.getElementsByTagName('!');
alert(comment.length);
Attr 类型表示文档元素中的属性。nodeType 为 11,nodeName 为属性名,nodeValue 为属性值。DOM 基础篇已经详细介绍过,略。
从 IE6 开始开始区分标准模式和混杂模式(怪异模式),主要是看文档的声明。IE 为document 对象添加了一个名为 compatMode 属性,这个属性可以识别 IE 浏览器的文档处于什么模式如果是标准模式,则返回 CSS1Compat,如果是混杂模式则返BackCompat。
if (document.compatMode == 'CSS1Compat') {
alert(document.documentElement.clientWidth);
} else {
alert(document.body.clientWidth);
}
PS: 后来 Firefox、 Opera 和 Chrome 都实现了这个属性。 从 IE8 后, 又引入 documentMode新属性,因为 IE8 有 3 种呈现模式分别为标准模式 8,仿真模式 7,混杂模式 5。所以如果想测试 IE8 的标准模式,就判断 document.documentMode > 7 即可。
DOM 提供了一些滚动页面的方法,如下:
document.getElementById('box').scrollIntoView(); //设置指定可见
由于子节点空白问题,IE 和其他浏览器解释不一致。虽然可以过滤掉,但如果只是想得到有效子节点,可以使用 children 属性,支持的浏览器为:IE5+、Firefox3.5+、Safari2+ 、Opera8+和 Chrome,这个属性是非标准的。
var box = document.getElementById('box');
alert(box.children.length); //得到有效子节点数目
判断一个节点是不是另一个节点的后代,我们可以使用 contains()方法。这个方法是 IE率先使用的,开发人员无须遍历即可获取此信息。
var box = document.getElementById('box');
alert(box.contains(box.firstChild)); //true
PS:早期的 Firefox 不支持这个方法,新版的支持了,其他浏览器也都支持,Safari2.x浏览器支持的有问题,无法使用。所以,必须做兼容。在 Firefox 的 DOM3 级实现中提供了一个替代的方法 compareDocumentPosition()方法。
这个方法确定两个节点之间的关系。
var box = document.getElementById('box');
alert(box.compareDocumentPosition(box.firstChild)); //20
关系掩码表
掩码 | 节点关系 |
---|---|
1 | 无关(节点不存在) |
2 | 居前(节点在参考点之前) |
4 | 居后(节点在参考点之后) |
8 | 包含(节点是参考点的祖先) |
16 | 被包含(节点是参考点的后代) |
PS:为什么会出现 20,那是因为满足了 4 和 16 两项,最后相加了。为了能让所有浏览器都可以兼容,我们必须写一个兼容性的函数。
//传递参考节点(父节点),和其他节点(子节点)
function contains(refNode, otherNode) {
//判断支持 contains,并且非 Safari 浏览器
if (typeof refNode.contains != 'undefined' &&
!(BrowserDetect.browser == 'Safari' && BrowserDetect.version < 3)) {
return refNode.contains(otherNode);
//判断支持 compareDocumentPosition 的浏览器,大于 16 就是包含
} else if (typeof refNode.compareDocumentPosition == 'function') {
return !!(refNode.compareDocumentPosition(otherNode) > 16);
} else {
//更低的浏览器兼容,通过递归一个个获取他的父节点是否存在
var node = otherNode.parentNode;
do {
if (node === refNode) {
return true;
} else {
node = node.parentNode;
}
} while (node != null);
}
return false;
}
虽然在之前我们已经学习了各种 DOM 操作的方法,这里所介绍是 innerText、innerHTML、outerText 和 outerHTML 等属性。除了之前用过的 innerHTML 之外,其他三个还么有涉及到。
document.getElementById('box').innerText; //获取文本内容(如有 html 直接过滤掉)
document.getElementById('box').innerText = 'Mr.Lee'; //设置文本(如有 html 转义)
PS:除了 Firefox 之外,其他浏览器均支持这个方法。但 Firefox 的 DOM3 级提供了另外一个类似的属性:textContent,做上兼容即可通用。
document.getElementById('box').textContent; //Firefox 支持
//兼容方案
function getInnerText(element) {
return (typeof element.textContent == 'string') ?
element.textContent : element.innerText;
}
function setInnerText(element, text) {
if (typeof element.textContent == 'string') {
element.textContent = text;
} else {
element.innerText = text;
}
}
这个属性之前就已经研究过,不拒绝 HTML。
document.getElementById('box').innerHTML; //获取文本(不过滤 HTML)
document.getElementById('box').innerHTML = '<b>123</b>'; //可解析 HTML
虽然 innerHTML 可以插入 HTML,但本身还是有一定的限制,也就是所谓的作用域元
素,离开这个作用域就无效了。
box.innerHTML = "<script>alert('Lee');</script>"; //<script>元素不能被执行 box.innerHTML = "<style>background:red;</style>"; //<style>元素不能被执行
outerText 在取值的时候和 innerText 一样,同时火狐不支持,而赋值方法相当危险,他不单替换了文本内容,还将元素直接抹去。
var box = document.getElementById('box');
box.outerText = '<b>123</b>';
alert(document.getElementById('box')); //null,建议不去使用
outerHTML 属性在取值和 innerHTML 一致,但和 outerText 也一样,很危险,赋值的之后会将元素抹去。
var box = document.getElementById('box');
box.outerHTML = '123';
alert(document.getElementById('box')); //null,建议不去使用,火狐旧版未抹去
PS:关于最常用的 innerHTML 属性和节点操作方法的比较,在插入大量 HTML 标记时使用 innerHTML 的效率明显要高很多。因为在设置 innerHTML 时,会创建一个 HTML 解析器。这个解析器是浏览器级别的(C++编写),因此执行 JavaScript 会快的多。但,创建和销毁 HTML 解析器也会带来性能损失。最好控制在最合理的范围内,如下:
for (var i = 0; i < 10; i ++) {
ul.innerHTML = '<li>item</li>'; //避免频繁
}
//改
for (var i = 0; i < 10; i ++) {
a = '<li>item</li>'; //临时保存
}
ul.innerHTML = a;
DOM 在操作生成 HTML 上, 还是比较简明的。 不过, 由于浏览器总是存在兼容和陷阱 ,导致最终的操作就不是那么简单方便了。 本章主要了解一下 DOM操作表格和样式的一些知识。
<table>
标签是 HTML 中结构最为复杂的一个,我们可以通过 DOM 来创建生成它,或者 HTML DOM 来操作它。
PS:HTML DOM 提供了更加方便快捷的方式来操作Table, 有手册
//需要操作的 table
姓名 | 性别 | 年龄 |
---|---|---|
张三 | 男 | 20 |
李四 | 女 | 22 |
合计:N |
//使用 DOM 来创建这个表格
var table = document.createElement('table');
table.border = 1;
table.width = 300;
var caption = document.createElement('caption');
table.appendChild(caption);
caption.appendChild(document.createTextNode('人员表'));
var thead = document.createElement('thead');
table.appendChild(thead);
var tr = document.createElement('tr');
thead.appendChild(tr);
var th1 = document.createElement('th');
var th2 = document.createElement('th');
var th3 = document.createElement('th');
tr.appendChild(th1);
th1.appendChild(document.createTextNode('姓名'));
tr.appendChild(th2);
th2.appendChild(document.createTextNode('年龄'));
document.body.appendChild(table);
PS:使用 DOM 来创建表格其实已经没有什么难度,就是有点儿小烦而已。下面我们再使用 HTML DOM 来获取和创建这个相同的表格。HTML DOM 中,给这些元素标签提供了一些属性和方法
属性或方法 | 说明 |
---|---|
caption | 保存着< caption>元素的引用 |
tBodies | 保存着< tbody>元素的 HTMLCollection 集合 |
tFoot | 保存着对< tfoot>元素的引用 |
tHead | 保存着对< thead>元素的引用 |
rows | 保存着对< tr> 元素的 HTMLCollection 集合 |
createTHead() | 创建< thead>元素,并返回引用 |
createTFoot() | 创建< tfoot>元素,并返回引用 |
createCaption() | 创建< caption>元素,并返回引用 |
deleteTHead() | 删除< thead>元素 |
deleteTFoot() | 删除< tfoot>元素 |
deleteCaption() | 删除< caption>元素 |
deleteRow(pos) | 删除指定的行 |
insertRow(pos) | 向 rows 集合中的指定位置插入一行 |
< tbody>元素添加的属性和方法
属性或方法 | 说明 |
---|---|
rows | 保存着< tbody>元素中行的 HTMLCollection |
deleteRow(pos) | 删除指定位置的行 |
insertRow(pos) | 向 rows 集合中的指定位置插入一行,并返回引用 |
< tr>元素添加的属性和方法
属性或方法 | 说明 |
---|---|
cells | 保存着< tr>元素中单元格的 HTMLCollection |
deleteCell(pos) | 删除指定位置的单元格 |
nsertCell(pos) | 向 cells 集合的指定位置插入一个单元格,并返回引用 |
PS:因为表格较为繁杂,层次也多,在使用之前所学习的 DOM 只是来获取某个元素会非常难受,所以使用 HTML DOM 会清晰很多
//使用 HTML DOM 来获取表格元素
var table = document.getElementsByTagName('table')[0]; //获取 table 引用
//按照之前的 DOM 节点方法获取<caption>
alert(table.children[0].innerHTML); //获取 caption 的内容
PS:这里使用了 children[0]本身就忽略了空白,如果使用 firstChild 或者 childNodes[0]需要更多的代码。
//按 HTML DOM 来获取表格的<caption>
alert(table.caption.innerHTML); //获取 caption 的内容
//按 HTML DOM 来获取表头表尾<thead>、<tfoot>
alert(table.tHead); //获取表头
alert(table.tFoot); //获取表尾
//按 HTML DOM 来获取表体<tbody>
alert(table.tBodies); //获取表体的集合
PS:在一个表格中和是唯一的,只能有一个。而不是唯一的可以有多个,这样导致最后返回的和是元素引用,而返回的是元素集合。
//按 HTML DOM 来获取表格的行数
alert(table.rows.length); //获取行数的集合,数量
//按 HTML DOM 来获取表格主体里的行数
alert(table.tBodies[0].rows.length); //获取主体的行数的集合,数量
//按 HTML DOM 来获取表格主体内第一行的单元格数量(tr)
alert(table.tBodies[0].rows[0].cells.length); //获取第一行单元格的数量
//按 HTML DOM 来获取表格主体内第一行第一个单元格的内容(td)
alert(table.tBodies[0].rows[0].cells[0].innerHTML); //获取第一行第一个单元格的内容
//按 HTML DOM 来删除标题、表头、表尾、行、单元格
table.deleteCaption(); //删除标题
table.deleteTHead(); //删除<thead>
table.tBodies[0].deleteRow(0); //删除<tr>一行
table.tBodies[0].rows[0].deleteCell(0); //删除<td>一个单元格
//按 HTML DOM 创建一个表格
var table = document.createElement('table');
table.border = 1;
table.width = 300;
table.createCaption().innerHTML = '人员表';
//table.createTHead();
//table.tHead.insertRow(0);
var thead = table.createTHead();
var tr = thead.insertRow(0);
var td = tr.insertCell(0);
td.appendChild(document.createTextNode('数据'));
var td2 = tr.insertCell(1);
td2.appendChild(document.createTextNode('数据 2'));
document.body.appendChild(table);
PS:在创建表格的时候<table>
、<tbody>
、<th>
没有特定的方法,需要使用 document来创建。也可以模拟已有的方法编写特定的函数即可,例如:insertTH()之类的。
CSS 作为(X)HTML 的辅助,可以增强页面的显示效果。但不是每个浏览器都能支持最新的 CSS 能力。CSS 的能力和 DOM 级别密切相关,所以我们有必要检测当前浏览器支持CSS 能力的级别。
DOM1 级实现了最基本的文档处理,DOM2 和 DOM3 在这个基础上增加了更多的交互能力,这里我们主要探讨 CSS,DOM2 增加了 CSS 编程访问方式和改变 CSS 样式信息。
DOM 一致性检测
功能 | 版本号 | 说明 |
---|---|---|
Core | 1.0、2.0、3.0 | 基本的 DOM,用于表现文档节点树 |
XML | 1.0、2.0、3.0 | Core 的 XML 扩展,添加了对 CDATA 等支持 |
HTML | 1.0、2.0 | XML 的 HTML 扩展,添加了对 HTML 特有元素支持 |
Views | 2.0 | 基于某些样式完成文档的格式化 |
StyleSheets | 2.0 | 将样式表关联到文档 |
CSS | 2.0 | 对层叠样式表 1 级的支持 |
CSS2 | 2.0 | 对层叠样式表 2 级的支持 |
Events | 2.0 | 常规的 DOM 事件 |
UIEvents | 2.0 | 用户界面事件 |
MouseEvents | 2.0 | 由鼠标引发的事件(如:click) |
MutationEvents | 2.0 | DOM 树变化时引发的事件 |
HTMLEvents | 2.0 | HTML4.01 事件 |
Range | 2.0 | 用于操作 DOM 树中某个范围的对象和方法 |
Traversal | 2.0 | 遍历 DOM 树的方法 |
LS | 3.0 | 文件与 DOM 树之间的同步加载和保存 |
LS-Async | 3.0 | 文件与 DOM 树之间的异步加载和保存 |
Valuidation | 3.0 | 在确保有效的前提下修改 DOM 树的方法 |
//检测浏览器是否支持 DOM1 级 CSS 能力或 DOM2 级 CSS 能力
alert('DOM1 级 CSS 能力:' + document.implementation.hasFeature('CSS', '2.0'));
alert('DOM2 级 CSS 能力:' + document.implementation.hasFeature('CSS2', '2.0'));
PS:这种检测方案在 IE 浏览器上不精确,IE6 中,hasFeature()方法只为 HTML 和版本1.0 返回 true,其他所有功能均返回 false。但 IE 浏览器还是支持最常用的 CSS2 模块。
任何 HTML 元素标签都会有一个通用的属性:style。它会返回CSSStypeDeclaration 对象。下面我们看几个最常见的行内 style 样式的访问方式。
CSS 属性及 JavaScript 调用
CSS 属性 | JavaScript 调用 |
---|---|
color | style.color |
font-size | style.fontSize |
float 非 IE | style.cssFloat |
float IE | style.styleFloat |
var box = document.getElementById('box'); //获取 box
box.style.cssFloat.style; //CSSStyleDeclaration
box.style.cssFloat.style.color; //red
box.style.cssFloat.style.fontSize; //20px
box.style.cssFloat || box.style.styleFloat; //left,非 IE 用 cssFloat,IE 用 styleFloat
PS:以上取值方式也可以赋值,最后一种赋值可以如下:
typeof box.style.cssFloat != 'undefined' ?
box.style.cssFloat = 'right' : box.style.styleFloat = 'right';
DOM2 级样式规范为 style 定义了一些属性和方法
属性或方法 | 说明 |
---|---|
cssText | 访问或设置 style 中的 CSS 代码 |
length | CSS 属性的数量 |
parentRule | CSS 信息的 CSSRule 对象 |
getPropertyCSSValue(name) | 返回包含给定属性值的 CSSValue 对象 |
getPropertyPriority(name) | 如果设置了!important, 则返回, 否则返回空字符串 |
item(index) | 返回指定位置 CSS 属性名称 |
removeProperty(name) | 从样式中删除指定属性 |
setProperty(name,v,p) | 给属性设置为相应的值,并加上优先权 |
box.style.cssText; //获取 CSS 代码
//box.style.length; //3,IE 不支持
//box.style.removeProperty('color'); //移除某个 CSS 属性,IE 不支持
//box.style.setProperty('color','blue'); //设置某个 CSS 属性,IE 不支持
PS:Firefox、Safari、Opera9+、Chrome 支持这些属性和方法。IE 只支持 cssText,而getPropertyCSSValue()方法只有 Safari3+和 Chrome 支持。
PS:style 属性仅仅只能获取行内的 CSS 样式,对于另外两种形式内联<style>
和链接<link>
方式则无法获取到。
虽然可以通过 style 来获取单一值的 CSS 样式,但对于复合值的样式信息,就需要通过计算样式来获取。DOM2 级样式,window 对象下提供了getComputedStyle()方法。接受两个参数,需要计算的样式元素,第二个伪类(:hover),如果没有没有伪类,就填 null。
PS:IE 不支持这个 DOM2 级的方法,但有个类似的属性可以使用 currentStyle 属性。
var box = document.getElementById('box');
var style = window.getComputedStyle ?
window.getComputedStyle(box, null) : null || box.currentStyle;
alert(style .color); //颜色在不同的浏览器会有 rgb()格式
alert(style .border); //不同浏览器不同的结果
alert(style .fontFamily); //计算显示复合的样式值
alert(box.style.fontFamily); //空
PS: border 属性是一个综合属性, 所以他在 Chrome 显示了, Firefox 为空, IE 为 undefined 。
所谓综合性属性,就是 XHTML 课程里所的简写形式,所以,DOM 在获取 CSS 的时候, 最好采用完整写法兼容性最好,比如:border-top-color 之类的。
使用 style 属性可以设置行内的 CSS 样式,而通过 id 和 class 调用是最常用的方法。
box.id = 'pox'; //把 ID 改变会带来灾难性的问题
box.className = 'red'; //通过 className 关键字来设置样式
在添加 className 的时候,我们想给一个元素添加多个 class 是没有办法的,后面一个必将覆盖掉前面一个,所以必须来写个函数:
//判断是否存在这个 class
function hasClass(element, className) {
return element.className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'));
}
//添加一个 class,如果不存在的话
function addClass(element, className) {
if (!hasClass(element, className)) {
element.className += " "+className;
}
}
//删除一个 class,如果存在的话
function removeClass(element, className) {
if (hasClass(element, className)) {
element.className = element.className.replace(
new RegExp('(\\s|^)'+className+'(\\s|$)'),' ');
}
}
之前我们使用 style 属性,仅仅只能获取和设置行内的样式,如果是通过内联<style>
或链接<link>
提供的样式规则就无可奈何了,然后我们又学习了 getComputedStyle() 和currentStyle,这只能获取却无法设置。
CSSStyleSheet 类型表示通过<link>
元素和<style>
元素包含的样式表。
document.implementation.hasFeature('StyleSheets', '2.0') //是否支持 DOM2 级样式表
document.getElementsByTagName('link')[0]; //HTMLLinkElement
document.getElementsByTagName('style')[0]; //HTMLStyleElement
这两个元素本身返回的是 HTMLLinkElement 和 HTMLStyleElement 类型,但
CSSStyleSheet 类型更加通用一些。得到这个类型非 IE 使用 sheet 属性,IE 使用 styleSheet;
var link = document.getElementsByTagName('link')[0];
var sheet = link.sheet || link.styleSheet; //得到 CSSStyleSheet
属性或方法 | 说明 |
---|---|
disabled | 获取和设置样式表是否被禁用 |
href | 如果是通过包含的,则样式表为 URL,否则为 null |
media | 样式表支持的所有媒体类型的集合 |
ownerNode | 指向拥有当前样式表节点的指针 |
parentStyleSheet | @import 导入的情况下,得到父 CSS 对象 |
title | ownerNode 中 title 属性的值 |
type | 样式表类型字符串 |
cssRules | 样式表包含样式规则的集合,IE 不支持 |
ownerRule | @import 导入的情况下, 指向表示导入的规则, IE 不支持 |
deleteRule(index) | 删除 cssRules 集合中指定位置的规则,IE 不支持 |
insertRule(rule, index) | 向 cssRules 集合中指定位置插入 rule 字符串,IE 不支持 |
sheet.disabled; //false,可设置为 true
sheet.href; //css 的 URL
sheet.media; //MediaList,集合
sheet.media[0]; //第一个 media 的值
sheet.title; //得到 title 属性的值
sheet.cssRules //CSSRuleList,样式表规则集合
sheet.deleteRule(0); //删除第一个样式规则
sheet.insertRule("body {background-color:red}", 0); //在第一个位置添加一个样式规则
PS:除了几个不用和 IE 不支持的我们忽略了,还有三个有 IE 对应的另一种方式:
sheet.rules; //代替 cssRules 的 IE 版本
sheet.removeRule(0); //代替 deleteRule 的 IE 版本
sheet.addRule("body", "background-color:red", 0);//代替 insertRule 的 IE 版本
除了刚才的方法可以得到 CSSStyleSheet 类型,还有一种方法是通过 document 的styleSheets 属性来获取。
document.styleSheets; //StyleSheetList,集合
var sheet = document.styleSheets[0]; //CSSStyleSheet,第一个样式表对象
为了添加 CSS 规则,并且兼容所有浏览器,我们必须写一个函数:
var sheet = document.styleSheets[0];
insertRule(sheet, "body", "background-color:red;", 0);
function insertRule(sheet, selectorText, cssText, position) {
//如果是非 IE
if (sheet.insertRule) {
sheet.insertRule(selectorText + "{" + cssText + "}", position);
//如果是 IE
} else if (sheet.addRule) {
sheet.addRule(selectorText, cssText, position);
}
}
为了删除 CSS 规则,并且兼容所有浏览器,我们必须写一个函数:
var sheet = document.styleSheets[0];
deleteRule(sheet, 0);
function deleteRule(sheet, index) {
//如果是非 IE
if (sheet.deleteRule) {
sheet.deleteRule(index);
//如果是 IE
} else if (sheet.removeRule) {
sheet.removeRule(index);
}
}
通过 CSSRules 属性(非 IE)和 rules 属性(IE),我们可以获得样式表的规则集合列表。这
样我们就可以对每个样式进行具体的操作了。
var sheet = document.styleSheets[0]; //CSSStyleSheet
var rules = sheet.cssRules || sheet.rules; //CSSRuleList,样式表的规则集合列表
var rule = rules[0]; //CSSStyleRule,样式表第一个规则
CSSStyleRule 可以使用的属性
属性 | 说明 |
---|---|
cssText | 获取当前整条规则对应的文本,IE 不支持 |
parentRule | @import 导入的,返回规则或 null,IE 不支持 |
parentStyleSheet | 当前规则的样式表,IE 不支持 |
selectorText | 获取当前规则的选择符文本 |
style | 返回 CSSStyleDeclaration 对象,可以获取和设置样式 |
type | 表示规则的常量值,对于样式规则,值为 1,IE 不支持 |
rule.cssText; //当前规则的样式文本
rule.selectorText; //#box,样式的选择符
rule.style.color; //red,得到具体样式值
PS:Chrome 浏览器在本地运行时会出现问题,rules 会变成 null,只要把它放到服务器上允许即可正常。
总结:三种操作 CSS 的方法
方式 | 影响范围 | 读写属性 |
---|---|---|
style | 行内 | 可读可写 |
getComputedStyle(),currentStyle | 行内、内联和链接 | 只读 |
cssRules 或 rules | 内联和链接 | 可读可写 |
var box = document.getElementById('box'); //获取元素
box.style.width; //200px、空
box.style.height; //200px、空
PS:style 获取只能获取到行内 style 属性的 CSS 样式中的宽和高,如果有获取;如果没有则返回空。
var style = window.getComputedStyle ?
window.getComputedStyle(box, null) : null || box.currentStyle;
style.width; //1424px、200px、auto
style.height; //18px、200px、auto
PS:通过计算获取元素的大小,无关你是否是行内、内联或者链接,它经过计算后得到的结果返回出来。如果本身设置大小,它会返回元素的大小,如果本身没有设置,非 IE浏览器会返回默认的大小,IE 浏览器返回 auto。
var sheet = document.styleSheets[0]; //获取 link 或 style
var rule = (sheet.cssRules || sheet.rules)[0]; //获取第一条规则
rule.style.width; //200px、空
rule.style.height; //200px、空
PS:cssRules(或 rules)只能获取到内联和链接样式的宽和高,不能获取到行内和计算后的样式
总结:以上的三种 CSS 获取元素大小的方法,只能获取元素的 CSS 大小,却无法获取元素本身实际的大小。比如加上了内边距、滚动条、边框之类的
这组属性可以获取元素可视区的大小,可以得到元素内容及内边距所占据的空间大小。
PS: 返回了元素大小, 但没有单位, 默认单位是 px, 如果你强行设置了单位, 比如 100em之类,它还是会返回 px 的大小。(CSS 获取的话,是照着你设置的样式获取)。
PS:对于元素的实际大小,clientWidth 和 clientHeight 理解方式如下:
PS:如果说没有设置任何 CSS 的宽和高度,那么非 IE 浏览器会算上滚动条和内边距的计算后的大小,而 IE 浏览器则返回 0。
这组属性可以获取滚动内容的元素大小
PS:返回了元素大小,默认单位是 px。如果没有设置任何 CSS 的宽和高度,它会得到计算后的宽度和高度。
PS:对于元素的实际大小,scrollWidth 和 scrollHeight 理解如下:
这组属性可以返回元素实际大小,包含边框、内边距和滚动条。
PS:返回了元素大小,默认单位是 px。如果没有设置任何 CSS 的宽和高度,他会得到计算后的宽度和高度。
PS:对于元素的实际大小,offsetWidth 和 offsetHeight 理解如下:
PS:对于元素大小的获取,一般是块级(block)元素并且以设置了 CSS 大小的元素较为方便。如果是内联元素(inline)或者没有设置大小的元素就尤为麻烦,所以,建议使用的时候注意。
这组属性可以获取元素设置了左边框和上边框的大小。
PS:目前只提供了 Left 和 Top 这组,并没有提供 Right 和 Bottom。如果四条边宽度不
同的话,可以直接通过计算后的样式获取,或者采用以上三组获取元素大小的减法求得。
这组属性可以获取当前元素相对于父元素的位置。
PS:获取元素当前相对于父元素的位置,最好将它设置为定位 position:absolute;否则不同的浏览器会有不同的解释。
**PS:加上边框和内边距不会影响它的位置,但加上外边据会累加。
box.offsetParent; //得到父元素**
PS:offsetParent 中,如果本身父元素是,非 IE 返回 body 对象,IE 返回 html 对象。如果两个元素嵌套,如果上父元素没有使用定位 position:absolute,那么 offsetParent 将返回 body 对象或 html 对象。所以,在获取 offsetLeft 和 offsetTop 时候,CSS 定位很重要。
如果说,在很多层次里,外层已经定位,我们怎么获取里层的元素距离 body 或 html元素之间的距离呢?也就是获取任意一个元素距离页面上的位置。那么我们可以编写函数 ,通过不停的向上回溯获取累加来实现。
box.offsetTop + box.offsetParent.offsetTop; //只有两层的情况下
如果多层的话,就必须使用循环或递归。
function offsetLeft(element) {
var left = element.offsetLeft; //得到第一层距离
var parent = element.offsetParent; //得到第一个父元素
while (parent !== null) { //如果还有上一层父元素
left += parent.offsetLeft; //把本层的距离累加
parent = parent.offsetParent; //得到本层的父元素
} //然后继续循环
return left;
}
这组属性可以获取滚动条被隐藏的区域大小,也可设置定位到该区域。
如果要让滚动条滚动到最初始的位置,那么可以写一个函数:
function scrollStart(element) {
if (element.scrollTop != 0) element.scrollTop = 0;
}
上一节已经通过几组属性可以获取元素所需的位置,那么这节课补充一个 DOM 的方法:getBoundingClientRect()。这个方法返回一个矩形对象,包含四个属性:left、top、right和 bottom。分别表示元素各边与页面上边和左边的距离。
var box = document.getElementById('box'); //获取元素
alert(box.getBoundingClientRect().top); //元素上边距离页面上边的距离
alert(box.getBoundingClientRect().right); //元素右边距离页面左边的距离
alert(box.getBoundingClientRect().bottom); //元素下边距离页面上边的距离
alert(box.getBoundingClientRect().left); //元素左边距离页面左边的距离
PS:IE、Firefox3+、Opera9.5、Chrome、Safari 支持,在 IE 中,默认坐标从(2,2)开始计算,导致最终距离比其他浏览器多出两个像素,我们需要做个兼容。
document.documentElement.clientTop; //非 IE 为 0,IE 为 2
document.documentElement.clientLeft; //非 IE 为 0,IE 为 2
function getRect(element) {
var rect = element.getBoundingClientRect();
var top = document.documentElement.clientTop;
var left = document.documentElement.clientLeft;
return {
top : rect.top - top,
bottom : rect.bottom - top,
left : rect.left - left,
right : rect.right - left
}
}
PS:分别加上外边据、内边距、边框和滚动条,用于测试所有浏览器是否一致。
当网站需求变大,脚本的需求也逐步变大。我们就不得不引入太多的 JS 脚本而降低了整站的性能,所以就出现了动态脚本的概念,在适时的时候加载相应的脚本。
比如:我们想在需要检测浏览器的时候,再引入检测文件。
var flag = true; //设置 true 再加载
if (flag) {
loadScript('browserdetect.js'); //设置加载的 js
}
function loadScript(url) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
//document.head.appendChild(script); //document.head 表示<head>
document.getElementsByTagName('head')[0].appendChild(script);
}
PS:document.head 调用,IE 不支持,会报错!
//动态执行 js
var script = document.createElement('script');
script.type = 'text/javascript';
var text = document.createTextNode("alert('Lee')"); //IE 浏览器报错
script.appendChild(text);
document.getElementsByTagName('head')[0].appendChild(script);
**PS:IE 浏览器认为 script 是特殊元素,不能在访问子节点。为了兼容,可以使用 text属性来代替。
script.text = “alert(”)”; //IE 可以支持了。**
PS:当然,如果不支持 text,那么就可以针对不同的浏览器特性来使用不同的方法。 这里就忽略写法了。
为了动态的加载样式表, 比如切换网站皮肤。 样式表有两种方式进行加载, 一种是< link>标签,一种是< style>标签。
//动态执行 link
var flag = true;
if (flag) {
loadStyles('basic.css');
}
function loadStyles(url) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = url;
document.getElementsByTagName('head')[0].appendChild(link); }
//动态执行 style
var flag = true;
if (flag) {
var style = document.createElement('style');
style.type = 'text/css';
//var box= document.createTextNode(#box{background:red}'); IE 不支持
//style.appendChild(box);
document.getElementsByTagName('head')[0].appendChild(style);
insertRule(document.styleSheets[0], '#box', 'background:red', 0);
}
function insertRule(sheet, selectorText, cssText, position) {
//如果是非 IE
if (sheet.insertRule) {
sheet.insertRule(selectorText + "{" + cssText + "}", position);
//如果是 IE
} else if (sheet.addRule) {
sheet.addRule(selectorText, cssText, position);
}
}