一,DOM 基础
DOM (Document Object Model,文档对象模型)是 W3C 制定的一套用来描述 JavaScript 脚本怎样与 HTML 或 XML 文档进行交互的 Web 标准API。
HTML文档的树状结构包含表示HTML元素 和表示文本字符串的节点:
<html>
<head>
<time>Sample Document</time>
</head>
<body>
<h1>An HTML Document</h1>
<p>This is a <i>simple</i> document.</p>
</body>
</html>
其结构如下:
每个方框是文档的一个节点,它表示一个Node节点对象。
(一)节点类型
DOM 规定:整个文档是一个文档节点,每个元素是一个元素节点,元素包含的文本是文本节点,元素的属性是一个属性节点,注释属于注释节点…所以上面那段HTML中包含文档节点、元素节点和文本节点三种节点。
一些常用的节点类型如下:
节点类型 | 说明 |
---|---|
Document | 表示整个文档, DOM 树的根节点 |
Element | 表示元素 |
Attr | 表示属性 |
Text | 表示兀素或属性中的文本内容 |
Comment | 表示注释 |
Processinglnstruction | 表示处理指令 |
每个节点都有一个 nodeType
属性返回一个数值,用于表明节点的类型:
节点类型 | nodeType |
---|---|
Document | 9 |
Element | 1 |
Attr | 2 |
Text | 3 |
Comment | 8 |
Processinglnstruction | 7 |
看一个例子:遍历所有子节点
<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h1>DOM</h1>
<p>DOM是<cite>Document Object Model</cite>首字母简写,中文翻译为<b>文档对象模型</b>,是<i>W3C</i>组织推荐的处理可扩展标识语言的标准编程接口。</p>
<ul>
<li>D表示文档,HTML文档结构。</li>
<li>O表示对象,文档结构的JavaScript脚本化映射。</li>
<li>M表示模型,脚本与结构交互的方法和行为。</li>
</ul>
<script type="text/javascript">
function count(n) { //定义文档元素统计函数
var num = 0;
if (n.nodeType == 1) {// 检查是否为元素节点
num++;
}
var son = n.childNodes;
for (var i = 0; i < son.length; i++) { // 循环统一每个子元素
num += count(son[i]); // 递归操作
}
return num; // 返回统计值
}
console.log("当前文档包含 " + count(document) + " 个元素"); // 计算元素的总个数
</script>
</body>
</html>
(二)节点名称和值
使用节点的 nodeName
和 nodeValue
属性可以读取节点的名称和值。这两个属性的值完全取决于节点的类型:
节点类型 | nodeName | nodeValue |
---|---|---|
Document | “#document” | null |
Element | 元素标签名 | null |
Attr | 属性名 | 属性名 |
Text | “#text” | 元素节点和属性节点中的纯文本内容 |
Comment | “#comment” | 注释文本内容 |
Processinglnstruction | 等同于 ProcessingInstruction.target 属性的值 | 整个标签的文本内容 |
例子:显示所有节点的nodeName和nodeValue
在上面例子中的递归语句后添加:
console.log("nodeName:" + son[i].nodeName + " nodeValue:" + son[i].nodeValue);
(三)节点关系
在上面展示了一个树形结构,这棵树就是DOM文档树。
节点树的根、叶等概念和父子、兄弟等关系参考数据结构中树的基本概念,这里不再赘述。
二,使用节点
(一)访问节点
通过节点之间的树形关系,JavaScript 可使用 Node 类型的一些属性定位文档树中每个节点。
1,ownerDocument属性
Node.ownerDocument 只读属性以 Node 对象的形式返回当前节点的根节点。
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Example</title>
<script>
function getRootElemt(id) {
var e = document.getElementById(id);
console.log(e.ownerDocument);
}
</script>
</head>
<body style="border: solid green 3px">
<p id="p1">Some outer text</p>
<p id="p2">Some outer text</p>
<div id="div1">
<p id="div1p1">Some div1 text</p>
<p id="div1p2">Some div1 text</p>
<p id="div1p3">Some div1 text</p>
<div id="div2" style="border: solid red 3px">
<p id="div2p1">Some div2 text</p>
<p id="div2p1">Some div2 text</p>
</div>
</div>
<p id="p3">Some outer text</p>
<p id="p4">Some outer text</p>
<button onclick="getRootElemt('div1');">show nodes</button>
</body>
</html>
也能通过document.documentElement
获取根节点。
2,parentNode属性
Node.parentNode属性以 Node 对象的形式返回当前节点的父节点。
function getPareElemt(id) {
var e = document.getElementById(id);
console.log(e.parentNode);
}
<button onclick="getChilElems('div1');">show</button>
3,childNodes属性
Node.childNodes只读属性以 NodeList 对象的形式返回指定节点的所有子节点。
function getChilElems(id) {
var e = document.getElementById(id);
var children = e.childNodes;
var array = null;
try {
var array = Array.prototype.slice.call(children, 0);
} catch (ex) {
var array = new Array();
for (var i = 0; i < children.length; i++) {
array.push(children[i]);
}
}
console.log(array);
for (var i=0; i<children.length; i++) {
console.log(children[i]);
}
}
<button onclick="getChilElems('div1');">show</button>
- 将NodeList转为普通数组再展示内容。
4,firstChild属性
Node.firstChild 只读属性返回指定节点的第一个子节点.如果节点是无子节点,则返回null。
function getFirstChild(id) {id
var d = document.getElementById(id);
console.log(d.firstChild);
}
<button onclick="getChilElems('div1');">show</button>
5,lastChild属性
Node.lastChild只读属性返回当前节点的最后一个子节点。如果没有子节点,则返回 null。
function getLastChild(id) {
var d = document.getElementById(id);
console.log(d.lastChild);
}
console.log(d.lastChild.nodeName + " : " + d.lastChild.nodeValue);
6,nextSibling属性
Node.nextSibling 只读属性返回与指定节点有同一父节点的下一个节点。如果指定的节点为最后一个节点,则返回 null。
function getNextSibling(id) {
var e = document.getElementById(id)
var n = e.nextSibling;
console.log(n.nodeName);
}
<button onclick="getNextSibling('div1p1');">show</button>
7,previousSibling属性
Node.previousSibling 只读属性返回与指定节点有同一父节点的上一个节点。如果指定的节点为第一个节点,则返回 null。
function getPreviousSibling(id) {
var e = document.getElementById(id)
var n = e.previousSibling;
console.log(n.nodeType);
}
(二)操作节点
Node 类型为所有节点定义了很多方法 以方便对节点进行操作。
1,cloneNode()
Node.cloneNode() 方法返回调用该方法的节点的一个副本。
function cloneNode(id) {
var e = document.getElementById(id)
var n = e.cloneNode();
}
<button onclick="cloneNode('div1p1');">show</button>
- 默认deep参数设为false,如果设为true,则会复制整棵DOM子树(包括那些可能存在的Text子节点)。
2,appendChild()
Node.appendChild() 方法将一个节点附加为指定节点的最后一个兄弟。
function append(id) {
var sourceNode = document.getElementById(id); // 获得被克隆的节点对象
var clonedNode = sourceNode.cloneNode(true); // 克隆节点
clonedNode.setAttribute("id", "div3"); // 修改一下id 值,避免id 重复
sourceNode.parentNode.appendChild(clonedNode); // 在父节点插入克隆的节点
}
<button onclick="append('div2');">show</button>
3,insertBefore()
Node.insertBefore() 方法在指定节点之前插入另一个拥有相同父节点的节点。
function insertbefore(id) {
var sourceNode = document.getElementById(id); // 获得被克隆的节点对象
var clonedNode = sourceNode.cloneNode(true); // 克隆节点
clonedNode.setAttribute("id", "div3"); // 修改一下id 值,避免id 重复
sourceNode.parentNode.insertBefore(clonedNode, sourceNode); // 在父节点插入克隆的节点
}
<button onclick="append('div2');">show</button>
没有Node.insertAfter() 方法,但可以使用 insertBefore 和 Node.nextSibling 来实现:
parentDiv.insertBefore(sp1, sp2.nextSibling);
在第一个子元素的前面插入一个元素,可使用 firstChild 属性:
//获得第一个子节点的引用
var theFirstChild = parentElement.firstChild;
//在第一个子节点之前插入新元素
parentElement.insertBefore(newElement, theFirstChild);
4,hasChildNodes()
hasChildNodes()方法返回一个布尔值,表明当前节点是否包含有子节点。
function myFunction(id){
var e=document.getElementById(id);
console.log(e.hasChildNodes());
}
5,normalize()
Node.normalize() 方法将当前节点和它的后代节点规范化——合并相邻的文本节点并删除空的文本节点。
具体参考后面“使用文档节点”的内容。
6,removeChild()
Node.removeChild() 方法删除指定节点的指定子节点并返回被删除的节点。
function insertbefore(id) {
var sourceNode = document.getElementById(id); // 获得被克隆的节点对象
var clonedNode = sourceNode.cloneNode(true); // 克隆节点
clonedNode.setAttribute("id", "div3"); // 修改一下id 值,避免id 重复
sourceNode.parentNode.insertBefore(clonedNode, sourceNode); // 在父节点插入克隆的节点
}
function removehild(id) {
var sourceNode = document.getElementById(id);
var clonedNode = document.getElementById("div3");
sourceNode.parentNode.removeChild(clonedNode);
}
<button onclick="insertbefore('div2');">先插入节点</button>
<button onclick="removehild('div2');">再删除节点</button>
7,replaceCbild()
Node.replaceChild() 方法用指定的节点替换当前节点的指定子节点,并返回被替换掉的节点。
function insertbefore(id) {
var sourceNode = document.getElementById(id); // 获得被克隆的节点对象
var clonedNode = sourceNode.cloneNode(true); // 克隆节点
clonedNode.setAttribute("id", "div3"); // 修改一下id 值,避免id 重复
sourceNode.parentNode.insertBefore(clonedNode, sourceNode); // 在父节点插入克隆的节点
}
function replacechild(id) {
var sourceNode = document.getElementById(id); // 获得被克隆的节点对象
var clonedNode = document.getElementById("p3"); // 克隆节点
clonedNode.setAttribute("style", "border: solid yellow 3px")
sourceNode.parentNode.replaceChild(clonedNode, sourceNode); // 在父节点替换克隆的节点
}
<button onclick="insertbefore('div2');">先插入节点</button>
<button onclick="replacechild('div3');">再替换节点</button>
8,contains()
Node.contains()方法返回的是一个布尔值,来表示传入的节点是否为该节点的后代节点。
function isInPage(id) {
let e = document.getElementById(id);
const result = (e === document.body) ? false : document.body.contains(e);
console.log(result);
}
<button id="ta-example-one" onclick="isInPage('p3')">show</button>
以上都是对Node节点进行操作的通用方法,不同类型的Node节点有不同的特征、有不同的操作,接下来进行简单的介绍。
三,使用文档节点
在 DOM 中,Document 类型表示文档节点,可以在全局作用域中直接访问这个 document 对象。
1,访问文档子节点
(1)使用 childNodes 属性
使用前面提到的 childNodes 访问文档子元素:
function getChilElems() {
var children = document.childNodes;
var array = null;
try {
var array = Array.prototype.slice.call(someNode.childNOdes, 0);
} catch (ex) {
var array = new Array();
for (var i = 0; i < children.length; i++) {
array.push(children[i]);
}
}
console.log(array);
for (var i = 0; i < children.length; i++) {
console.log(children[i]);
}
}
(2)使用 all 属性
该属性返回一个包含了页面上的所有元素 HTMLAllCollection。
var all = document.all;
(3)访问其他子节点
属性 | 作用 |
---|---|
Document.head | 返回当前文档的 head 元素 |
Document.body | 返回当前文档的 body 或 frameset 节点 |
Document.doctype | 返回当前文档的文档类型定义 |
Document.documentElement | 该属性始终指向 HTML 页面中的 html 元素 |
function myFunction(){
var head = document.head;
var body = document.body;
var documentElement = document.documentElement;
console.log("head: "+head);
console.log("body: "+body);
console.log("documentElement: "+documentElement);
}
2,访问文档信息
属性 | 作用 |
---|---|
Document.title | 设置返回当前文档的 title 标签包含的文本信息 |
Document.lastModified | 返回当前文档最后被修改的日期和时间 |
Document.domain | 设置或返回当前文档的域名 |
Document.location | 返回当前文档的 URI |
Document.documentURI | 以字符串的类型,返回当前文档的路径。 |
Document.characterSet | 返回文档正在使用的字符集 |
function myFunction(){
var title = document.title;
var lastModified = document.lastModified;
var domain = document.domain;
var location = document.location;
var documentURI = document.documentURI;
var characterSet = document.characterSet;
console.log("title: "+title);
console.log("lastModified: "+lastModified);
console.log("domain: "+domain);
console.log("location: "+location);
console.log("documentURI: "+documentURI);
console.log("characterSet: "+characterSet);
}
3,访问文档的元素
document 对象包含多个访问文档内元素的方法,简单说明如下:
方法 | 作用 |
---|---|
Document.getElementsByClassName() | 返回当前元素的所有指定类名的子元素 |
Document.getElementsByTagName() | 返回当前元素的所有指定元素名的子元素 |
Document.getElementsByName() | 返回当前元素的所有指定name属性名的子元素 |
4,访问文档集合
除了属性和方法, document 对象还定义了一些特殊的集合:
方法 | 作用 |
---|---|
Document.forms | 返回一个包含当前文档中所有表单元素 form 的列表 |
Document.links | 返回一个包含文档中所有超链接的列表 |
Document.images | 返回当前文档中所包含的图片的列表 |
Document. | |
Document. | |
Document. |
function myFunction(){
var forms = document.forms;
var images = document.images;
var links = document.links;
console.log(forms);
console.log(images);
console.log(links);
}
四,使用元素节点
1,访问元素
方法 | 作用 |
---|---|
Document.getElementsByClassName() | 返回当前元素的所有指定类名的子元素 |
Document.getElementsByTagName() | 返回当前元素的所有指定元素名的子元素 |
Document.getElementsByName() | 返回当前元素的所有指定name属性名的子元素 |
还能使用之前提到的访问节点的方法通过节点间关系访问节点。
function myFunction(id){
var e = document.getElementById(id); // 获取指定元素
var ename = "nodeName:" + e.nodeName; // 获取该节点的名称
var etype = e.nodeType; // 获取该节点的类型
var efirstchild = e.firstElementChild; // 获取该元素的第一个子元素
var elastchild = e.lastElementChild; // 获取该元素的最后一个子元素
var eparent = e.parentNode; // 获取该元素的父元素
var eprevious = e.previousElementSibling; // 获取该元素的上一个相邻元素
var enext = e.nextElementSibling; // 获取该元素的下一个相邻元素
console.log(ename); // 显示提示信息
console.log(etype);
console.log(efirstchild);
console.log(elastchild);
console.log(eparent);
console.log(eprevious);
console.log(enext);
}
<button onclick="myFunction('div1');">show</button>
2,遍历元素
function getChilElems(id) {
var e = document.getElementById(id);
var children = e.childNodes;
var array = null;
try {
var array = Array.prototype.slice.call(children, 0);
} catch (ex) {
var array = new Array();
for (var i = 0; i < children.length; i++) {
array.push(children[i]);
}
}
console.log(array);
for (var i=0; i<children.length; i++) {
console.log(children[i]);
}
}
<button onclick="getChilElems('div1');">show</button>
3,创建元素并插入节点
Document.createElement() 方法创建一个由标签名称 tagName 指定的 HTML 元素。
插入节点的方法参考前面提到的Node.insertBefore() 方法和Node.appendChild() 方法。
function addElement() {
// 创建一个新的 div 元素
let newDiv = document.createElement("div");
// 给它一些内容
let newContent = document.createTextNode("Hi there and greetings!");
// 添加文本节点到这个新的 div 元素
newDiv.appendChild(newContent);
// 将这个新的元素和它的文本添加到 DOM 中
let currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
}
4,复制、替换和删除节点
参考前面的Node.cloneNode() 方法、Node.replaceChild() 方法方法和Node.removeChild() 方法。
5,获取焦点元素
document.activeElement()方法返回当前在DOM或者shadow DOM树中处于聚焦状态的元素。
function active() {
const activeButton = document.activeElement;
console.log(activeButton.getAttribute("id"));
}
<button id="ta-example-one" onclick="active()">show</button>
Document.hasFocus() 方法判断当前文档或者当前文档内的节点是否获得了焦点。
function active() {
const activeButton = document.activeElement;
console.log(activeButton.localName);
if (document.hasFocus()) {
console.log(activeButton.getAttribute("id"));
}
}
<button id="ta-example-one" onclick="active()">show</button>
五,使用文本节点
文本节点由 Text 类型表示,包含纯文本内容,或转义后的 HTML 字符,但不能包含HTML代码。比如:
<!-- 下面div不包含文本节点 -->
<div></div>
<!--下面div包含文本节点,值为空格-->
<div> </div>
<!--下面div包含文本节点,值为换行符-->
<div>
</div>
<!--下面div包含文本节点,值为" Hello World!" -->
<div>Hello World!</div>
(一)访问文本节点
使用文本节点的 nodeValue
属性或 data
属性可以访问或设置 Text 节点中包含的文本。
<body style="border: solid green 3px">
<p id="p1">Some outer text1</p>
<p id="p2">Some outer text2</p>
<div id="div1" style="border: solid blue 3px">
<p id="div1p1">Some div1 text1</p>
<p id="div1p2">Some div1 text2</p>
<p id="div1p3">Some div1 text3</p>
<div id="div2" style="border: solid red 3px">
<p id="div2p1">Some div2 text1<1/p>
<p id="div2p2">Some div2 text2<span id="span1">测试</span></p>
</div>
</div>
<p id="p3">Some outer text3</p>
<p id="p4">Some outer text4</p>
<button onclick="myFunction('div1')">show</button>
</body>
function myFunction(id) {
var div = document.getElementById(id);
var text = div.firstChild;
console.log(text);
text = div.childNodes[1].firstChild.nodeValue;
console.log(text);
};
再结合前面遍历元素打印出的内容,我们就能判断这里的div.firstChild获得的div1的第一个子节点是一个Text类型,其nodeValue和data都是“回车+Tab”,确实我们在div1与其第一个p元素间使用了回车键和Tab键。div1的第二个子节点是一个Element类型,其第一个子节点就是一个Text类型其nodeValue和data都是“Some div1 text1”,因为我们没有在它所在的元素中使用回车与Tab,这直接就能看出来。
(二)创建并插入文本节点
Document.createTextNode()方法创建一个新文本节点。
插入节点的方法参考前面提到的Node.insertBefore() 方法和Node.appendChild() 方法。
function addTextNode() {
// 创建一个新的 div 元素
let newDiv = document.createElement("div");
// 给它一些内容
let newContent = document.createTextNode("Hi there and greetings!");
// 添加文本节点到这个新的 div 元素
newDiv.appendChild(newContent);
// 将这个新的元素和它的文本添加到 DOM 中
let currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
}
(三)操作文本节点
参考前面的Node.cloneNode() 方法、Node.replaceChild() 方法方法和Node.removeChild() 方法操作文本节点。
使用CharacterData的属性操作文本节点中的文本内容。
function addTextNode() {
// 创建一个新的 div 元素
let newDiv = document.createElement("div");
// 创建一个新的文本节点
let newContent = document.createTextNode("Hi there a
// 给文本节点再追加一些内容
newContent.appendData("You too!");
console.log(newContent.length);
// 给文本节点再插入一些内容
newContent.insertData(31, "Happy!");
console.log(newContent.length);
// 添加文本节点到这个新的 div 元素
newDiv.appendChild(newContent);
// 将这个新的元素和它的文本添加到 DOM 中
let currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
// 给文本节点删除一些内容
newContent.deleteData(31, 7);
console.log(newContent.nodeValue);
// 提取文本节点的一些内容
let sub = newContent.substringData(31, 7);
console.log(sub);
// 替换文本节点的一些内容
newContent.replaceData(31, 8, "What's up?");
console.log(newContent.nodeValue);
}
(四)操作 HTML 字符串
元素的 innerHTML 属性设置或获取HTML语法表示的元素的后代。
六,使用属性节点
属性节点由 Attr 类型表示。
尽管属性也是节点,但是 DOM 没有提供相关直接引用,通常用 getAttribute()、 setAttribute()和 removeAttribute()等方法来操作属性。
(一)创建属性节点和设置属性节点的值
Document.createAttribute() 方法创建并返回一个新的属性节点。
Element.setAttribute()方法设置指定元素上的某个属性值。如果属性已经存在,则更新该值;否则,使用指定的名称和值添加一个新的属性。
function myFunction() {
var element = document.getElementById("div1");
var attr = document.createAttribute("align");
attr.value = "center";
element.setAttributeNode(attr);
console.log(element.attributes["align"].value);
}
(二)读取和删除属性节点
Element.getAttribute()方法返回元素上一个指定的属性值。如果指定的属性不存在,则返回 null 或 “”。
Element.removeAttribute()方法从指定的元素中删除一个属性。
function myFunction() {
var element = document.getElementById("div1");
console.log(element.attributes["align"].value);
console.log(element.getAttribute("align").value);
element.removeAttribute("align");
console.log(element.getAttribute("align"));
}
到此为止,关于 DOM 的相关概念与操作就简单介绍完了。
七,一个例子
使用 script 加载远程数据
script 元素能够动态加载外部或远程 JavaScript 脚本文件。 JavaScript 脚本文件不仅仅可以被执行,还可以附加数据。在服务器端使用 JavaScript 文件附加数据之后, 当在客户端使用 script 元素加载这些远程脚本时,附加在 JavaScript 文件中的信息也一同被加载到客户端,从而实现数据异步加载的目的。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>异步信息交互</title>
<script type="text/javascript">
function callback(info) {
//客户端回调函数
alert(info);
}
function request(url) {
/*
创建<script>标签
参数: URL 表示要请求的服务器端文件路径
返回值: 无
*/
if (!document.script) {
document.script = document.createElement("script");
document.script.setAttribute("type", "text/javascript");
document.script.setAttribute("src", url);
document.body.appendChild(document.script);
} else {
document.script.setAttribute("src", url);
}
}
window.onload = function() {
var b = document.getElementsByTagName("input")[0];
b.onclick = function() { //为页面按钮绑定异步请求函数
request("server.js");
}
}
</script>
</head>
<body>
<h1>客户端信息提交页面</h1>
<input name="submit" type="button" id="submit" value="向服务器发出请求">
</body>
</html>
server.js:
//服务器端响应页面
callback("这里是服务器端数据信息");
改进:在url中传递参数。
<html>
<head>
<title>异步信息交互</title>
<script type="text/javascript">
function callback(info) {
alert(info);
}
function request(url) {
if (!document.script) {
document.script = document.createElement("script");
document.script.setAttribute("type", "text/javascript");
document.script.setAttribute("src", url);
document.body.appendChild(document.script);
} else {
document.script.setAttribute("src", url);
}
}
window.onload = function() {
var b = document.getElementsByTagName("input")[0];
b.onclick = function() {
var url = "server(gb2312).asp?callback=callback"
request(url);
}
}
</script>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
<h1>客户端信息提交页面</h1>
<input name="submit" type="button" id="submit" value="向服务器发出请求" />
</body>
</html>
server(gb2312).asp文件:
<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<%
callback = Request.QueryString("callback")
Response.Write("callback('Hi,大家好,我是从服务器端过来的信息使者.')")
%>
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/98089.html