10 DOM

10.1 节点层次

10.1.1 Node类型

节点信息
  1. nodeType属性
  • Node.ELEMENT_NODE(1)
  • Node.ATTRIBUTE_NODE(2)
  • Node.TEXT_NODE(3)
  • Node.CDATA_SECTION_NODE(4)
  • Node.ENTITY_REFERENCE_NODE(5)
  • Node.ENTITY_NODE(6)
  • Node.PROCESSING_INSTRUCTION_NODE(7)
  • Node.COMMENT_NODE(8)
  • Node.DOCUMENT_NODE(9)
  • Node.DOCUMENT_TYPE_NODE(10)
  • Node.DOCUMENT_FRAGMENT_NODE(11)
  • Node.NOTATION_NODE(12)
if(someNode.nodeType==1){
		console.log("Node is an element.")	
	}
  1. nodeName和nodeValue属性
    nodeName中始终保存是元素的标签名
    nodeValue值始终为null
节点关系
  1. childNode(NodeList对象)
  • 每个节点都有一个childNode属性,其中保存着一个NodeList对象
  • NodeList是一种类数组对象,用于保存一组节点
  • 取值方式
  • 方括号取值,例,someNode.childNodes[0];
  • item()取值,例,someNode.item(0);
  • 转换成数组
    1.Array.prototype.slice.call(node.childNodes, 0)//适用于非IE浏览器
    2.适用于所有浏览器的converToArray
//类数组转换成数组
  function converToArray(nodes) {
    var array = null;
    try {
      array = Array.prototype.slice.call(nodes, 0); //针对非IE浏览器
    } catch(e) {
      array = new Array();
      for (var i = 0, len = nodes.length; i < len; i++) {
        array.push(node[i]);
      }
    }
    return array;
  }
  1. parentNode
  • 每个节点都有一个parentNode属性,给属性指向文档树中的父节点
  • childNode列表中的所有节点都具有相同的父节点
  1. previousSibling和nextSibling
  • 可以访问同一列表中的其他节点
  • 如果为第一个节点,previousSibling为null
  • 如果为最后一个节点,nextSibling为null
  • 列表中只有一个节点,previousSibling和nextSibling都为null
  1. lastChild和firstChild
  • 分别指向childNode里的第一个节点和最后一个节点
  1. hasChildNodes和ownerDocument
  • hasChildNodes包含一个或多个子节点的情况下返回true
  • ownerDocument,该属性指向表示整个文档的文档节点
    此图展示Node节点的关系
操作节点
  1. appendChild
  • 向childNodes列表的末尾添加一个节点
  1. insertBefore
  • 接受两个参数:要插入的节点和作为参照的节点,插入节点后,被插入的节点会变成参照节点的前一个同胞节点
  1. replaceChild
  • 接受两个参数:要插入的节点和要替换的节点,要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置
  1. removeChild
  • 接受一个参数,移除childNodes列表指定的节点
其他方法
  1. cloneNode
  • 接受一个布尔参数,false是执行浅拷贝,true执行深拷贝,克隆节点
  1. normalize
  • 处理文档树中的文本节点,1.可能出现文本节点不包含文本,2.接连出现两个文本节点

10.1.2 Document 类型

document是HTMLDocument(继承自Document类型)的一个实例,表示整个html页面

节点特征
  • nodetype为9
  • nodeName为"#document"
  • nodeValue为null
  • parentNode为null
  • ownerDocument为null
  • 其子节点可能是一个DocumentType(最多一个),ELement(最多一个),ProcessingingInstruction,Comment
文档子节点

1.DocumentType

//DocumentType 接口表示了一个包含文档类型的节点 Node 
console.log(document.doctype);

2.ELement

Element 是一个通用性非常强的基类,所有 Document 对象下的对象都继承自它。这个接口描述了所有相同种类的元素所普遍具有的方法和属性。

3.ProcessingingInstruction

ProcessingInstruction接口表示处理指令; 也就是说,Node嵌入了针对特定应用程序的指令,但其他无法识别该指令的应用程序可以忽略该指令。

var doc = new DOMParser().parseFromString('<foo />', 'application/xml');
var pi = doc.createProcessingInstruction('xml-stylesheet', 'href="mycss.css" type="text/css"');

doc.insertBefore(pi, doc.firstChild);

console.log(new XMLSerializer().serializeToString(doc));
// Displays: <?xml-stylesheet href="mycss.css" type="text/css"?><foo/>

4.Comment

Comment 接口代表标签(markup)之间的文本符号(textual notations)。尽管它通常不会显示出来,但是在查看源码时可以看到它们。在 HTML 和 XML 里,注释(Comments)为 ‘ ’ 之间的内容。在 XML 里,注释中不能出现字符序列 ‘–’。

5.内置访问html和body
- document.documentElement,document.firstchild和document.childNodes[0]值相同,直接指向html标签
- document.body直接指向body标签

文档信息

1.document.title
通过这个属性可以获取当前页面的标题
2.document.URL
通过这个属性可以获取当前页面的完整的URL
3.document.domain

  • domain可以设置
  • 存在安全方面的限制
  • 不能将这个属性设置为URL中不包含的域
    4.document.referrer
    与document.URL类似
查找元素

1.getElementById

  • 接收一个参数,区分大小写

2.getElementsByTagName

  • 接收一个参数,返回零或多个元素的NodeList或HTMLCollection
  • HTMLCollection中的namedItem(),可以根据便签的名称,获取指定元素
  • 参数传入"*",获取所有元素

3.getElementsByName

  • 接收一个参数,标签name名称,返回零或多个元素的NodeList或HTMLCollection
特殊集合
  • document.anchors,包含文档中所有带name特性的a元素
  • document.applets,包含文档中所有元素
  • document.forms,包含文档中所有form元素
  • documen.images包含文档中所有img元素
  • document.links,包含文档中所有带href特性的a元素
DOM一致性检测

document.implementation的hasFeature()

  • 接收两个参数,要检测DOM功能的名称及版本号
console.log(document.implementation.hasFeature("HTML","5.0"));
文档写入

1.document.write
2.document.writeln
3.document.open
4.document.close

10.1.3 Element类型

节点特征
  • nodeType的值为1
  • nodeName的值为元素的标签名
  • nodeValue的值为null
  • parentNode可能是Document或Element
  • 子节点可能是Element,Text,Comment,ProcessingInstruction,CDATASection,EntityReference
HTML元素
  • id,元素在文档中的唯一标识符
  • title,有关元素的附加说明
  • lang,元素内容的语言代码
  • dir,语言的方向
特性

1.getAttribute
获取元素的特性
2.setAttribute
设置元素的特性
接收两个参数:特性名和值
3.removeAttribute
删除元素的某个特性

attribute属性

1.NameNodeMap

  • 类数组对象
  • getNamedItem(name):返回nodeName属性等于name的节点
  • removeNamedItem(name):从列表中移除nodeName属性为索引
  • setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引
  • item(pos):返回位于数字pos位置处的节点
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app" name="ding"></div>
</body>
</html>
<script>
    var app=document.getElementById("app");
    for(var i=0;i<app.attributes.length;i++){
        var nodeValue=app.attributes[i].nodeValue;
        var nodeName=app.attributes[i].nodeName;
        console.log(`第${i}个属性`+nodeName+`---`+nodeValue);
    }
    //输出
    //第0个属性id---app
    //第1个属性name---ding
</script>
创建元素

1.document.createElement
创建一个由标签名称 tagName 指定的 HTML 元素。如果用户代理无法识别 tagName,则会生成一个未知 HTML 元素 HTMLUnknownElement

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>
<script>
    var ding=document.createElement('div');
    var app=document.getElementById("app");
    ding.innerText="你好";
    app.appendChild(ding);
</script>

10.1.4 Text类型

  • nodeType的值为3;
  • nodeName的值为"#text";
  • nodeValue的值为节点所包含的文本
  • parentNode是一个Element
  • 没有子节点
操作文本节点
  • appendData(text):将text添加到节点的末尾
  • deleteData(offset,count):从offset指定的位置开始删除count个字符
  • insertData(offset,text):在offset指定的位置插入text
  • replaceData(offset,count,text):用text替换从offset指定的位置开始到offset+count为止处的文本
  • splitText(offset):从offset指定的位置将当前文本节点分成两个文本节点
  • substringData(offset,count):提取从offset指定的位置开始到offset+count为止处的字符串
创建文本节点
  • document.createTextNode:接收一个参数-要插入节点中的文本
规范化文本节点
  • normalize:相邻文本节点合并

10.1.5 Comment类型

  • nodeType的值为8;
  • nodeName的值为"#comment";
  • nodeValue的值是注释的内容
  • parentNode可能是Document或Element
  • 没有子节点
  • 与text类型相似

10.1.6 CDATASection类型

  • nodeType的值为4;
  • nodeName的值为"#cdata-section";
  • nodeValue的值是CDATA区域的内容
  • parentNode可能是Document或Element
  • 没有子节点
  • CDATA区域只会出现在XML文档中,多说浏览器都会吧CDTATA区域错误的解析成Comment或Element
<![CDATA["你好"]]>

10.1.7 DocumentType类型

  • 不常用,仅Firefox,Safari和Opera支持它
  • nodeType的值为10;
  • nodeName的值为doctype的名称
  • nodeValue的值为null
  • parentNode是Document
  • 没有子节点
  • 不能动态创建,只能文档解析创建
<!DOCTYPE html>

10.1.8 DocumentFragment类型

  • nodeType的值为11;
  • nodeName的值为"#document-fragment"
  • nodeValue的值为null;
  • parentNode的值为null;
  • 子节点是Element,ProcessingingInstruction,Comment,Text,CDATASection或EntityReference

document.createDocumentfragment

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="myList"></div>
  </body>
</html>
<script>
  var fragment = document.createDocumentFragment();
  var ul = document.getElementById("myList");
  var li = null;

  for (var i = 0; i < 3; i++) {
    li = document.createElement("li");
    li.appendChild(document.createTextNode("Item" + (i + 1)));
    fragment.appendChild(li);
  }

  ul.appendChild(fragment);
</script>

10.1.9 Attr类型

  • nodeType的值为2;
  • nodeName的值是特性的值
  • parentNode的值为null
  • HTML没有子节点
  • XML中子节点可以是text或entityReference
    -name,value和specified是Attr对象的三个属性
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>
<script>
    var app=document.getElementById("app");
    var attr=document.createAttribute("align");
    attr.value="left";
    app.setAttributeNode(attr);
</script>

10.2 DOM操作技术

10.2.1 动态脚本

插入外部文件
var script = document.createElement('script');
script.src = './demo.js';
document.body.appendChild(script);
插入JavaScript代码
function loadScript(code){
    var script = document.createElement('script');
 
    try{
        script.appendChild(document.createTextNode(code));
    }catch(err){
        script.text = code;
    }
 
    document.body.appendChild(script);
}
 
loadScript(' function sayHi(){alert(\'Hi!\')}');

以上两种模式加载的脚本代码会立刻执行,虽然可以,但是不推荐使用eval()来执行字符串代码;

10.2.2 动态样式

引入外部样式
function loadStyles(url){
    var head = document.getElementByTagName('head')[0];
    var link = document.createElement('link');
    link.el = 'stylesheet';
    link.href = url;
    head.appendChild(link);
}
 
loadStyles('styles.css');
function loadInlineStyle(css){
    var head = document.getElementsByTagName('head')[0];
    var style = document.createElement('style');
 
    try{
        style.appendChild(document.createTextNode(css));
    }catch(){
        style.styleSheet.cssText = css
    }
 
    head.appendChild(style);
}
 
loadInlineStyle('body{background-color: red;}');

IE将

10.2.3 操作表格

元素的属性和方法

1.caption:返回表格的caption元素节点,没有则返回null;
2.tHead, tBodies, tFoot: 返回表格, , 元素;
3.rows: 返回元素所有行元素的HTMLCollection;
4.createTHead(), createTFoot(), createCaption(): 创建, , 空元素,将其放到表格中,返回5.创建的, , 元素节点;
6.deleteTHead(), deleteTFoot(), deleteCaption(): 删除, , 空元素,无返回值(或返回值为undefined)
7.deleteRow(pos): 删除指定位置(注意参数不是索引,而是从0开始的位置)的行,返回undefined;
8.insertRow(pos): 向rows集合中的指定位置(不是索引)插入一行;

元素的属性和方法

1.rows: 返回元素下所有行元素的HTMLCollection;
2.deleteRow(pos): 删除指定位置(注意参数不是索引,而是从0开始的位置)的行,返回undefined;
3.insertRow(pos):向rows集合中的指定位置(不是索引)插入一行;

元素的属性和方法

1.cells: 返回元素中单元格的HTMLCollection;
2.deleteCell(pos): 删除指定位置(不是索引)的单元格;
3.insertCell(pos): 向cells集合中的指定位置(不是索引)插入一个单元格,返回对新插入单元格的引用;

<!DOCTYPE html>
<html>
    <body>
    </body>
    <script>
        // 创建table
        var table = document.createElement('table');
        table.border = 1;
        table.width = '100%';
 
        // 创建caption
        var caption = table.createCaption();
        caption.innerHTML = 'My Table';
 
        // 创建thead
        var thead = document.createElement('thead');
        thead.insertRow(0);
        thead.rows[0].insertCell(0);
        thead.rows[0].cells[0].appendChild(document.createTextNode('标题一'));
        thead.rows[0].insertCell(1);
        thead.rows[0].cells[1].appendChild(document.createTextNode('标题二'));
        table.appendChild(thead);
 
        // 创建tbody
        var tbody = document.createElement('tbody');
        table.appendChild(tbody);
 
        // 创建第一行
        tbody.insertRow(0);
        tbody.rows[0].insertCell(0);
        tbody.rows[0].cells[0].appendChild(document.createTextNode('Cell 1, 1'));
        tbody.rows[0].insertCell(1);
        tbody.rows[0].cells[1].appendChild(document.createTextNode('Cell 2, 1'));
 
        // 创建第二行
        var row02 = tbody.insertRow(1);
        var cell0201 = row02.insertCell(0),
            cell0202 = row02.insertCell(1);
 
        cell0201.appendChild(document.createTextNode('cell 2,1'));
        cell0202.appendChild(document.createTextNode('cell 2,2'));
 
        // 将表格添加到文档主体中
        document.body.appendChild(table);
    </script>
</html>

10.2.4 使用NodeList

NodeList NamedNodeMap HTMLCollection 这三个集合都是动态的,除了有个例。
NodeList :getElementsByName,childNodes,querySelectorAll(静态集合)等返回的都是NodeList实例
HTMLCollection:getElementsByTagName,getElementsByClassName,getElementsByTagNameNS,document.forms,document.children等返回的都是HTMLCollection实例
NamedNodeMap:表示属性节点对象的集合,ele.attributes返回NamedNodeMap实例

访问DOM文档时实时运行的查询,所以下面代码会导致无限循环。

var divs = document.getElementsByTagName('div'),i,div;
for(i = 0;i< divs.length; i++){
   div = document.createElement('div');
   document.body.appendChild(div);
}

浏览器不会将创建的所有集合都保存在一个列表中,而是在下次访问集合时再更新集合,i和divs.length每次都会同时递增,结果它们的值永远不会相等。正确写法如下:

var divs = document.getElementsByTagName('div'),i,len,div;
for(i = 0,len = divs.length; i<len;i++){
   div = document.createElement('div');
   document.body.appendChild(div);
}