⤴Top⤴

DOM 节点操作

博客分类: 前端

DOM 节点操作

DOM 节点操作

什么是 DOM

DOM(Document Object Model) 的基本思想是把结构化文档(如 HTML 和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口,以达到使用编程语言操作文档的目的(比如增删内容)。所以,DOM 可以理解成文档的 API。而 JavaScript 是最常用于 DOM 操作的语言。

node 节点

node 类型

DOM 的最小组成单位叫做节点(node),一个文档的树形结构,就是由各种不同类型的节点组成。主要有以下几种类型,它们都继承了 Node 对象的属性和方法:

节点 类型 nodeName nodeType 含义  
Document Node.DOCUMENT_NODE #document 9 文档节点 整个文档(window.document)
Element ELEMENT_NODE 大写的 HTML 元素名 1 元素节点 HTML元素(比如<body>、等)
Attribute ATTRIBUTE_NODE 等同于 Attr.name 2 属性节点 HTML元素的属性(比如 class=”right”)
Text TEXT_NODE #text 3 文本节点 HTML 文档中出现的文本
DocumentType DOCUMENT_TYPE_NODE 等同于 DocumentType.name 10 文档类型节点 文档的类型(比如<!DOCTYPE html>)
DocumentFragment DOCUMENT_FRAGMENT_NODE #document-fragment 11 文档碎片节点 文档的片段
document.nodeName // "#document"
document.nodeType // 9

document.querySelector('a').nodeType === 1 // true
document.querySelector('a').nodeType === Node.ELEMENT_NODE // true

node 关系

dom-node

node 操作

// removeChild
newElement.parentNode.removeChild(newElement);

// cloneNode
var cloneUL = document.querySelector('ul').cloneNode(true);

// insertBefore 将新节点插在当前节点的最前面
parentElement.insertBefore(newElement, parentElement.firstChild);

Node 接口类型

节点对象都是单个节点,但是有时会需要一种数据结构,能够容纳多个节点。DOM 提供两种接口,用于部署这种节点的集合,即 NodeList 接口和 HTMLCollection 接口。

如 Node.childNodes、document.querySelectorAll() 返回的则是一组节点,即一个部署了 NodeList 接口的对象。不同的是前者返回的是动态集合,后者返回的是静态集合。NodeList 接口提供 length 属性和数字索引,因此可以像数组那样,使用数字索引取出每个节点,但是它本身并不是数组,不能使用 pop 或 push 之类数组特有的方法。当然可以转成数组。

// NodeList的继承链
myNodeList --> NodeList.prototype --> Object.prototype --> null
// 转成数组
var div_list = document.querySelectorAll('div');

var div_array = Array.prototype.slice.call(div_list); // ES5
var div_array = Array.from(div_list); // ES6

NodeList 部署了 Iterator 接口,因此还可以用 for…of 进行遍历。

如 document.links、docuement.forms、document.images 等属性,返回的都是 HTMLCollection 接口对象,该接口都是动态集合,节点的变化会实时反映在集合中。提供 namedItem 方法根据成员的 id 属性(优先)或 name 属性,返回该成员。如果没有对应的成员,则返回 null。

var elem = document.forms.namedItem('myForm');
// 等价于
var elem = document.forms['myForm'];

html 元素

html 元素是网页的根元素,document.documentElement 即指向这个元素。

dataset

dataset 属性用于操作 HTML 标签元素的 data-* 属性,返回一个 DOMStringMap 对象。

<!-- data 属性只能使用连词号 - -->
<div id="myDiv" data-id="myId" data-hello-world="tate"></div>
// dataset 属性使用骆驼拼写法表示属性名
var id = document.getElementById("myDiv").dataset.id; // myId
var id = document.getElementById("myDiv").dataset.helloWorld; // tate

// delete 可以删除指定 dataset 属性
delete document.getElementById("myDiv").dataset.id;

tabindex

tabindex 属性用来指定当前 HTML 元素节点是否被 tab 键遍历,以及遍历的优先级:

var btn = document.getElementById('btn');

btn.tabIndex = 1; // tab 遍历优先级为1,大于默认的 0

页面位置

查看这里

Document

document 节点是文档的根节点,有不同的办法可以获取,且都部署了 Document 接口:

Document 属性

{ // document.location 对象的属性
  host: "localhost:8080"
  hostname: "localhost"
  href : "http://localhost:8080/test/babel.html?username=tate&age=18&file="
  origin: "http://localhost:8080"
  pathname : "/test/babel.html"
  port: "8080"
  protocol: "http:"
}

以下属性返回文档内部特定元素的动态集合(HTMLCollection),可配合 namedItem 使用:

Document 方法

document.getElementById('myElement') // 参数为 属性
document.querySelector('#myElement') // 参数为 CSS 选择器语法

document.getElementsByClassName('red orange'); // 返回同时具有 red 和 orange 样式名的节点

以下方法用于生成元素节点:

var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);
var node = document.getElementById('myId');
var a = document.createAttribute('data-hi');
a.value = 'tate';
node.setAttributeNode(a);

// 等价于
var node = document.getElementById('myId');
node.setAttribute('data-hi', 'tate');
// 从 iframe 窗口,拷贝一个指定节点 myNode,插入当前文档
var iframe = document.getElementsByTagName('iframe')[0];
var oldNode = iframe.contentWindow.document.getElementById('myNode');
var newNode = document.importNode(oldNode, true);
document.getElementById('container').appendChild(newNode);

与事件相关的三个方法,详情查看事件代理一节:

Element

Element 对象对应网页的 HTML 标签元素,元素节点的 nodeType 属性都是 1。

Element 属性

var para = document.getElementById('para');
var attr = para.attributes[0];

// 等同于 nodeName 属性和 nodeValue 属性
attr.name // id
attr.value // para
// 添加 class
document.getElementById('foo').classList.add('bold'); // good
document.getElementById('foo').className += 'bold';

// 删除 class
document.getElementById('foo').classList.remove('bold');
document.getElementById('foo').className = document.getElementById('foo').className.replace(/^bold$/, '');
// toggle 方法可以接受一个布尔值作为第二个参数。如果为 true,则添加该属性,否则去除
el.classList.toggleClass('abc', someBool);

// 等价于
if (someBool){
  el.classList.add('abc');
} else {
  el.classList.remove('abc');
}

获取 Element 节点相关的属性:

document.firstChild
// 返回第一个节点 <!DOCTYPE html>

document.firstElementChild
// 返回第一个 Element 节点 <html lang=​"en">​<head>​…​</head>​<body>​…​</body>​</html>​

Element 位置信息

// 判断页面是否滚动到底部
element.scrollHeight - element.scrollTop === element.clientHeight;

element-clientLeft.gif

Element 方法

<article>
  <div id="div-01">Here is div-01
    <div id="div-02">Here is div-02
      <div id="div-03">Here is div-03</div>
    </div>
  </div>
</article>
var el = document.getElementById('div-03');
el.closest("#div-02") // div-02
el.closest("div div") // div-03
el.closest("article > div") //div-01
el.closest(":not(div)") // article

Text

Text 节点代表 Element 节点和 Attribute 节点的文本内容,可以使用 Document 节点的 createTextNode 方法创造一个 Text 节点。通过 firstChild、nextSibling 等获取 Text 节点。

Text 属性

// 读取文本内容
document.querySelector('p').firstChild.data
// 等价于
document.querySelector('p').firstChild.nodeValue

// 设置文本内容
document.querySelector('p').firstChild.data = 'Hello World';

Text 方法

// HTML代码为
// <p>Hello World</p>
var pElementText = document.querySelector('p').firstChild;

pElementText.appendData('!');
// 页面显示 Hello World!
pElementText.deleteData(7, 5);
// 页面显示 Hello W
pElementText.insertData(7, 'Hello ');
// 页面显示 Hello WHello
pElementText.replaceData(7, 5, 'World');
// 页面显示 Hello WWorld
pElementText.substringData(7, 10);
// 页面显示不变,返回 "World "

不同用法的区分

返回当前节点的内容,几种方法的比较:

<article>
  <div id="div-01">Here is div-01
    <div id="div-02">Here is div-02
      <div id="div-03" style="display: none;">Here is div-03</div>
    </div>
  </div>
</article>
var article = document.getElementsByTagName('article')[0];

article.innerText;
// "Here is div-01
// Here is div-02"

article.textContent;
// "
//   Here is div-01
//     Here is div-02
//       Here is div-03
// "

article.innerHTML;
// "
//   <div id="div-01">Here is div-01
//     <div id="div-02">Here is div-02
//       <div id="div-03" style="display: none;">Here is div-03</div>
//     </div>
//   </div>
// "

通过改变节点内容,加上 style 标签还可以增添样式:

var style = document.createElement('style');

style.setAttribute('media', 'screen');
// 或者
// style.setAttribute("media", "@media only screen and (max-width : 1024px)");

style.innerHTML = 'body{color:red}';
// 或者
// sheet.insertRule("header { float: left; opacity: 0.8; }", 1);

document.head.appendChild(style);
// <style media="screen">body{color:red}</style>

参考链接

  1. Gitbooks - JavaScript 标准参考教程
  2. 容易混淆的 client-,scroll-,offset-* By zhangguixu