DOM是所有前端开发每天打交道的东西,但是随着jQuery等库的出现,大大简化了DOM操作,导致大家慢慢的“遗忘”了它的本来面貌。不过,要想深入学习前端知识,对DOM的了解是不可或缺的,所以本文力图系统的讲解下DOM的相关知识,如有遗漏或错误,还请大家指出一起讨论^ ^。
  一、DOM是什么?
  DOM(文档对象模型)是针对HTML和XML文档的一个API,通过DOM可以去改变文档。
  这个说法很官方,大家肯定还是不明白。
  举个例子:我们有一段HTML,那么如何访问第二层第一个节点呢,如何把后一个节点移动到第一个节点上面去呢?
  DOM是定义了如果做类似操作,那么应该怎么做的标准。比如用getElementById来访问节点,用insertBefore来插入节点。
  当浏览器载入HTML时,会生成相应的DOM树。
  简而言之,DOM可以理解为一个访问或操作HTML各种标签的实现标准。
  对于一个HTML来说,文档节点Document(看不到的)是它的根节点,对应的对象便是document对象(严格讲是子类HTMLDocument对象,下面单独介绍Document类型时会指出)。
  换句话说存在一个文档节点Document,然后它有子节点,比如通过document.getElementsByTagName(“html”),得到类型为元素节点的Element html。
  每一段HTML标记都可以用相应的节点表示,例如:
  HTML元素通过元素节点表示,注释通过注释节点表示,文档类型通过文档类型节点表示等。
  一共定义了12种节点类型,而这些类型又都继承自Node类型。
  所以我们首先讲Node类型,因为这个类型的方法是所有节点都会继承的。
  二、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方法
  然后各个节点还存在各种属性让它们可以相互访问,下图很好的总结了

  比较有用的方法和属性:
  1、hasChildNodes()
  如果包含子节点返回true,比查询childNodes的length来的简单。
  2、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类型,所以这些方法是所有节点都有的。
  三、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>”;