1、DOM是什么?
DOM(文档对象模型)是针对HTML和XML文档的一个API,经过DOM能够去改变文档。html
这个说法很官方,你们确定仍是不明白。前端
举个例子:咱们有一段HTML,那么如何访问第二层第一个节点呢,如何把最后一个节点移动到第一个节点上面去呢?node
DOM就是定义了若是作相似操做,那么应该怎么作的标准。好比用getElementById来访问节点,用insertBefore来插入节点。程序员
当浏览器载入HTML时,会生成相应的DOM树。chrome
简而言之,DOM能够理解为一个访问或操做HTML各类标签的实现标准。编程
对于一个HTML来讲,文档节点Document(看不到的)是它的根节点,对应的对象即是document对象(严格讲是子类HTMLDocument对象,下面单独介绍Document类型时会指出)。跨域
换句话说存在一个文档节点Document,而后它有子节点,好比经过document.getElementsByTagName("html"),获得类型为元素节点的Element html。数组
每一段HTML标记均可以用相应的节点表示,例如:浏览器
HTML元素经过元素节点表示,注释经过注释节点表示,文档类型经过文档类型节点表示等。
一共定义了12种节点类型,而这些类型又都继承自Node类型。
因此咱们首先讲Node类型,由于这个类型的方法是全部节点都会继承的。
2、Node类型(基类,全部节点都继承了它的方法)
Node是全部节点的基类型,全部节点都继承自它,因此全部节点都有一些共同的方法和属性。
先讲Node类型的属性
首先是nodeType属性,用来代表节点类型的,例如:
document.nodeType; // 返回 9 ,其中document对象为文档节点Document的实例
这里面,9表明的就是DOCUMENT_NODE节点的意思,能够经过Node.DOCUMENT_NODE查看节点对应的数字
document.nodeType === Node.DOCUMENT_NODE; // true
至于一共有哪些节点,每一个节点对应的数字又是多少,这个能够问谷歌就知道了。反正经常使用的就是元素节点Element(对应数字为1)和文本节点Text(对应数字为3)
而后经常使用的还有nodeName和nodeValue
对于元素节点 nodeName就是标签名,nodeValue就是null
对于文本节点 nodeName为"#text"(chrome里面测试的),nodeValue就是实际的值
每一个节点还有childNodes属性,这是个十分重要的属性,它保存了这个节点全部直接子元素
调用childNodes返回的是一个NodeList对象,它极其像数组,可是有一个最关键的地方,它是动态查询的,也就是说每次调用它都会对DOM结构查询,因此对它的使用须要慎重,注意性能。
访问childNodes可使用数组下表或者item方法
而后各个节点还存在各类属性让它们能够相互访问,下图很好的总结了

比较有用的方法和属性:
一、hasChildNodes()
若是包含子节点就返回true,比查询childNodes的length来的简单。
二、ownerDocument
返回文档节点的引用(在html里面也就是document对象)
再介绍下Node类型经常使用的方法
appendChild()方法能够在节点的childNodes的末尾增长一个节点,值得注意的是若是这个节点是已经存在在文档中的,那么便会删除原节点,感受上就像是移动节点同样。
insertBefore()方法接受两个参数,一个是插入的节点,另一个是参照的节点。若是第二个参数为null,则insertBefore和appendChild效果同样。不然便会把节点插入到参照节点以前。这里要注意的是,若是第二个参数不为null,那么插入的节点不能是已经存在的节点。
replaceChild()方法能够替换节点,接受两个参数,须要插入的节点和须要替换的节点。返回被替换掉的节点。
removeChild()移除节点。这里有个常见需求,好比我有一个节点 #waste-node ,那么如何移除它呢?
var wasteNode = document.getElementById("waste-node");
wasteNode.parentNode.removeClhid(wasteNode); // 先拿到父节点,再调用removeClild删除本身
这里先暂停一下,不知道你们注意到没有,以上的几个方法都是操做某个节点的子节点,也就是说,操做前必须找到父节点(经过parentNode来找)
接下来讲下复制节点的方法:
cloneNode();复制节点,接受一个参数 true或者false。若是true就是复制那个节点和它的子节点。若是是false,就是复制节点自己(复制出来的节点就会没有任何子元素)。这个方法返回复制的节点,若是须要操做它,那么须要借助前面讲的4个方法来把这个节点放入到html中去。
至此,Node类型的常见属性和方法都介绍完了。结合开头讲的,全部节点类型都继承自Node类型,因此这些方法是全部节点都有的。
3、Document类型
最开始讲DOM是什么的时候提到了Document类型。其实关于这个类型最重要的是它的一个子类HTMLDocument有一个实例对象document。而这个document对象是咱们最经常使用的一个对象了。
document对象又挂载在window对象上,因此在浏览器就能够直接访问document了。
老规矩,先讲讲document对象的属性,等会讲讲它的方法。
document对象上的一些属性
document.childNodes 继承自上面讲的Node类型,能够返回文档的直接子节点(一般包括文档声明和html节点)
document.documentElement 能够直接拿到html节点的引用(等价于document.getElementsByTagName("html")[0])。
document.body body节点的引用
document.title 页面的title,能够修改,会改变浏览器标签上的名字
document.URL 页面的url
document.referrer 取得referrer,也就是打开这个页面的那个页面的地址,作来源统计时候比较有用
document.domain 取得域名,能够设置,可是一般只能设置为不包含子域名的状况,在一些子域名跨域状况下有效。
接下来介绍两个熟悉的方法
getElementById 和 getElementsByTagName
getElementById,传入id,获得元素节点。里面的参数区分大小写(IE8-不区分)。注意:若是有多个id相同的元素,则返回第一个。IE7-里面表单元素的name也会被当作id来使用。
getElementsByTagName 根据标签取得元素,获得的是HTMLCollection类型。若是传入的是 "*" ,则能够取得所有元素。
还有一个是只有HTMLDocument类型(也就是document对象)才有的方法 getElementsByName 顾名思义,根据name返回元素。
document对象还有一些集合,例如document.forms 能够返回全部的form表单。类型也是HTMLCollection。
说到HTMLCollection,就再说说它
HTMLCollection就是一个包含一个或多个元素的集合,和上面讲的NodeList还挺像的。HTMLCollection这个类型有两个方法,一个是经过下标(或者.item())获得具体元素,还有就是经过['name'](或者.namedItem())得到具体元素。
最后,关于document对象还有一套重要的方法,那即是
write() writeln() open() close()
open和close分别是打开和关闭网页的输出流,在页面加载过程当中,就至关于open状态。这两个方法通常不会去用它。
而后重要的方法就是write和writeln,它们都是向页面写入东西,区别就是后者会多加入一个换行符。
注意的是:在页面加载的过程当中,可使用这两个方法向页面添加内容。若是页面已经加载完了,再调用write,会重写整个页面。
还有一点,若是要动态写入脚本 例如 <script>xxx</script>这样的 ,那么要注意把</script>分开来拼装下,不然会被误觉得是脚本结束的标志,致使这个结束符匹配到上面一个开始符。能够这样写"<scr" + "ipt>";
4、Element类型
接下来说讲最重要也是最多见的一个类型,Element类型。
咱们平常所操做的都是Element类型(实质是HTMLElement,这里为了方便理解,就简单这么说),好比
document.getElementById("test")
返回的就是Element类型。咱们平常所说的“DOM对象”,一般也就是指Element类型的对象。
而后说说这个类型的常见属性:
首先最开始说的Node类型上的那些属性方法它都有,这个就再也不重复了,主要说说它本身独有的。
首先是tagName,这个和继承自Node类型的nodeName同样。都是返回标签名,一般是大写,结果取决于浏览器。因此在作比较
的时候最好是调用下相似toLowerCase()这种方法再作比较。
说说上面提到过的HTMLElement类型
HTMLElement类型继承自Element类型,也是HTML元素的实际类型,咱们在浏览器里用的元素都是这个类型。
这个类型都具备一些标准属性,好比:
id 元素的惟一标识
title 一般是鼠标移上去时候会显示的信息
className 类名
等等,这几个属性是可读写的,也就是说你改变他们会获得相应的效果。
除了属性外,还有几个重要的方法
首先说说操做节点属性的方法
getAttribute 、setAttribute 、removeAttribute这3个方法。
这些是操做属性最经常使用的方法了,怎么用就不说了,很简单,顾名思义。
还有一个attributes属性,保存了元素的所有属性。
这里停下来,出个问题,ele.className 和 ele.getAttribute("class")返回的结果是否是同一个东西?
解答这个问题,我要说一个重要知识点,一个元素的属性结构是这么来的,好比一个inpnt元素
<input id="test" checked="checked">
那么这个元素的属性被包含在 input.attributes里面,好比你在html元素上看到的class、id或者你本身定义的data-test这种属性。
而后 getAttribute 、setAttribute 、removeAttribute这3个方法能够认为是快捷的取attributes集合的方法。而直接input.id或者input.className都是直接挂在input下的属性,和attributes是同级的。因此返回的东西也许看过去同样,实际是不同的,不信你能够试试input.checked这input.getAttribute("checked")试试。
关于这个知识点,详细的说能够再写一篇文章,在个人博客 从is(":checked")提及 中有谈到过,你们能够看看这篇文章和文章后的讨论,即可以知道是怎么一回事。
总得来讲,这3个方法一般用了处理自定义的属性,而不是id、class等这种“公认特性”。
接下来讲说建立元素
document.createElement()能够建立一个元素,好比:
document.createElement("div");
通常以后能够为元素设置属性,两种方法,一种是直接node.property还能够node.setAttribute("propertyName","value")。等
可是作完这些以后,这个元素仍是没有在页面中,因此你还得经过最上面讲的相似appendChild这些方法把元素添加到页面里面。
在IE中,还能够直接穿整个HTML字符串进去,来建立元素,好比
document.createElement("<div>test</div>");
最后,元素节点也支持HTMLDocument类型的那些查找方法,好比getElementsByTagName。不过它只会找本身后代的节点。因此能够这么写代码
document.getElementById("test").getElementsByTagName("div"); // 找到id为test元素下的全部div节点
5、Text类型
这个类型很特殊,也是第三常见类型(第一第二分别就是Document和Element)。
这个节点简单来讲就是一段字符串。
有个很重要的特征就是,它没有子元素(不过这个仔细想一想也知道= =)
访问text节点的文本内容,能够经过nodeValue或者data属性。
下面简单说说它提供的一些方法
appendData(); // 在text末尾加内容
deleteData(offset, count); // 从offset指定的位置开始删除count个字符
还有insertDate、replaceData、splitText等方法,就不一一说了,用的机会不多,能够用的时候再查阅。
而后它还有一个lenght属性,返回字符长度的。
这里说一个常见的坑。好比下面这个html结构
<ul>
<li></li>
<li></li>
</ul>
这里,ul的第一个子节点(firstChild)是什么呢?第一眼看过去,确定认为是li了,可是实际上,你会发现不是li,而是一个文本节点!
这是由于浏览器认为ul和第一个li之间有空白字符,因此就有文本节点了。
这里一个常见的问题就是遍历ul的childNodes的时候,遍历的元素必定要判断下nodeType是否是等于1(等于1就表明是元素节点),这样才能跳过这个坑。不然你也能够删除全部的空格和换行符。
建立文本节点的方法是document.createTextNode
而后接下来和操做Element类型同样,就是再插入到元素中,浏览器就能够看到了。
6、其余的一些类型 Comment、DocumentType和DocumentFragment
这些不经常使用的一句话带过把
Comment是注释节点
DocumentType就是doctype节点,经过docment.doctype来访问
DocumentFragment这个节点是一个文档片断,偶尔会用到。
好比一种常见的用法是,在一个ul中插入3个li。
若是你循环插入3次,那么浏览器就要渲染3次,对性能有蛮大的影响。
因此你们通常这么作
先
var fragment = document.createDocumentFragment();
而后循环把li,用appendChild插入到fragment里面
最后在一次把fragment插入到ul里面。这样就会很快。
7、DOM扩展
进过上面讲的这么多节点类型,想必你们对DOM节点已经有了很深的了解,下面讲一讲DOM扩展的一些东西。
浏览器为了方便开发者,扩展了一些DOM功能。
由于是浏览器本身扩展的,因此使用前兼容性问题必定要注意
判断“标准模式”和“混杂模式”经过 document.compatMode和新的document.documentMode
上面不是说了一个文本节点做为第一子元素的坑吗,因此浏览器又实现了一个children属性,这个属性只包含元素节点。
为了方便判断A节点是否是B节点的子节点,引入了contains方法,好比
B.contains(A); // true就表明是,false就表明不是
这个方法有兼容性问题,使用前能够谷歌解决方法。
针对访问元素,又提供了4个方法innerText/innerHTML/outerTEXT/outerHTML。
经过这些方法,能够读和写元素。
其中,*TEXT是返回文本内容 *HTML是返回html文本。
而outer*则是表明是否包含元素自己。
实际使用来看,在读内容的时候 inner*和outer*没有区别。
在把内容写入元素的时候,就是是否包含元素自己的区别。
重要的是,这几个方法有性能问题,好比在IE中,经过inner*删除的节点,其绑定的事件依然在内存中,就很容易消耗大量内存。
还有一个技巧是,插入大量的html代码,用innerHTML是很是快的,建议使用。
8、总结
首先感谢全部看到这里的朋友,哈哈,关于DOM的东西实在是太多了,不过这也算是最重要的一个前端知识点之一吧。文章比较长,也许有点乏味,不过但愿大家耐着性子看完后能够有所收货^ ^。