网页的结构由标记语言创建,js 可用来改变细节而不改变结构。
同样,js 也可用来改变网页的结构和内容。
传统方法
document.write:write方法可以把字符串插入到文档里。
使用方法,编辑 test.html 文件:1
2
3
4
5<body>
<script>
document.write("<p> 这是一个段落 </p>");
</script>
</body>结果:

缺点:这样做会使得标记与 js 代码混杂在一起,代码即不容易理解,也无法享受代码与结构分离的好处。应避免
<body>乱用<script>标签,尽量减少使用document.write。innerHTML 属性
innerHTML 属性可以用来读写给定元素里的 HTML 内容。
读,编辑 test.html 文件:1
2
3<div id="test">
<p>This is <em>my</em> content!</p>
</div>然后编写 example.js (记得把 example.js 文件加到 HTML 文件里面):
1
2
3
4window.onload = function () {
var test = document.getElementById("test");
alert(test.innerHTML);
};结果:

结果显示,innerHTML 只是粗暴地把元素里面所有的代码找出来。当需要把插入大段的 HTML 文件时,它就有用武之地了。写:
1
2
3
4window.onload = function () {
var test = document.getElementById("test");
test.innerHTML = "<p> Insert in <em>innerHTML</em> </p>";
};结果:

直接赋值对应的即可,赋值后会把原来的 html 代码覆盖掉。
这是 HTML 专有属性,不能用于其它标记语言文档,如 xhtml 文档。
DOM 方法
添加节点
编辑 test.html,让id=test的 div 标签内容为空。1
<div id="test"></div>
把一段文本插入到上面的 div 元素,用 DOM 的话来形容,添加一个 p 元素节点,再把这个节点作为 div 元素的子节点。
具体步骤:- createElement 创建一个新元素:
语法:创造一个 p 元素结点并赋值给一个变量:1
document.createElement(nodeName);
1
var para = document.createElement("p");
- appendChild 把新元素插入节点数:
使用 appendChild 方法。
语法:例子:1
parent.appendChild(child);
或者:1
2
3var test = document.getElementById("test");
var para = document.createElement("p");
test.appendChild(para);1
document.getElementById("test").appendChild(document.createElement("p"));
- createElement 创建一个新元素:
添加文本节点
使用 crateTextNode 方法。
语法与 createElement 相似:1
document.createTextNode(text);
创建一个内容为 “test”的文本节点并赋值变量。
1
var txt = document.createTextNode("test");
使用 appendChild 添加到 p 元素结点:
1
para.appendChild(txt);
最终代码:
1
2
3
4
5
6
7window.onload = function () {
var test = document.getElementById("test");
var para = document.createElement("p");
test.appendChild(para);
var txt = document.createTextNode("test");
para.appendChild(txt);
};结果:

一个复杂的例子:
创建一个以下节点:1
<p>This is <em>my</em> content.</p>
这个 html 包含一个 p 元素节点,p 元素里又包含一个文本节点“This is”,一个 em 结点和一个文本节点“contents.”。
那么只要依次创建加入即可。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16window.onload = function () {
var test = document.getElementById("test");
var para = document.createElement("p");
test.appendChild(para);
// 添加第一个文本节点
var txt_0 = document.createTextNode("This is ");
para.appendChild(txt_0);
// 添加em节点
var em = document.createElement("em");
var em_txt = document.createTextNode("my");
em.appendChild(em_txt);
para.appendChild(em);
// 添加第三个节点
var text_1 = document.createTextNode(" content.");
para.appendChild(text_1);
};结果:

图片库
里面只有一个图片和一段文字是为show_pic服务,这很些元素的存在是为了让 DOM 处理它们,那么使用 DOM 来创建它们是最合适的。
先把文档里的相关元素删掉。
先编写一个函数
prepare_placeholder并把它放进show_pic.js文件,然后在文档加载时调用此函数。函数的任务:
- 创建一个 img 元素节点;
- 设置这个节点的 id 属性;
- 设置这个节点的 src 属性;
- 设置这个节点的 alt 属性;
- 创建一个 p 元素节点;
- 设置这个节点的 id 属性;
- 创建一个文本节点;
- 把这个文本节点追加到 p 元素上;
- 把 p 元素和 img 元素插入到 gallery.html 文档里。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 创建img元素
var placeholder = document.createElement("img");
placeholder.setAttribute("id", "default");
placeholder.setAttribute("src", "images/default.jpg");
placeholder.setAttribute("alt", "my image gallery");
// 创建p元素
var description = document.createElement("p");
description.setAttribute("id", "description");
var txt = document.createTextNode("choose an image");
description.appendChild(txt);
// 插入到文档中
var body_element = document.getElementsByTagName("body")[0];
body_element.appendChild(placeholder);
body_element.appendChild(description);上述代码有一个完成工作了,但是一切都是依赖于这些元素刚好在 body 标签的末尾。如果在中间的话,就需要用到其它方法了。
已有元素前插入元素
DOM 提供了名为insertBefore()方法。使用此方法时,必须告诉它三件事:- 新元素:你想插入的元素;
- 目标元素:你想反把这个元素插入到哪个元素之前;
- 父元素:目标元素的父元素。
语法:
1
parentElement.insertBefore(newElement, targetElement);
例子:
1
2var gallery = document.getElementById("image_gallery");
gallery.parentElement.insertBefore(placeholder, gallery);这样就把元素插入到图片元素之前了。
元素后插入元素
DOM 没有提供这个方法,但是可以自己写一个insertAtfer方法:1
2
3
4
5
6
7
8
9
10
11
12function insertAfter(newElement, targetElement) {
// 获取父元素
var parent = targetElement.parentNode;
// 如果最后一个结点元素与目标结点相同
if (parent.lastChild == targetElement) {
// 直接加到末尾
parent.appendChild(newElement);
} else {
// 找到目标结点的下一个结点,再插入到结点前
parent.insertBefore(newElement, targetElement.nextSibling);
}
}使用方法和
insertAfter一样。1
parentElement.insertAfter(newElement, targetElement);
还需要测试浏览器是否支持,所以最终代码清单为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function prepare_placeholder() {
if (!document.createElement) return false;
if (!document.createTextNode) return false;
if (!document.getElementById) return false;
if (!document.getElementById("image_gallery")) return false;
// 创建img元素
var placeholder = document.createElement("img");
placeholder.setAttribute("id", "default");
placeholder.setAttribute("src", "images/default.jpg");
placeholder.setAttribute("alt", "my image gallery");
// 创建p元素
var description = document.createElement("p");
description.setAttribute("id", "description");
var txt = document.createTextNode("choose an image");
description.appendChild(txt);
var gallery = document.getElementById("image_gallery");
insertAfter(placeholder, gallery);
insertAfter(description, placeholder);
}目前为止,创建的这些新内容对这个页面来说并不算是新的,比如,页面加载后,标记中就已经存在 title 属性了。通过
createElement添加的新段落也是基于嵌入在脚本中的标记添加的,实际上,我们创建的所有一切都包含在初始页面。
那么怎么才能真正得到原来不存在初始页面的内容呢?
Ajax
使用 Ajax 就可以做到只更新页面中的一小部分,其它内容都不需要重新加载。
Ajax 的主要优势是对页面的请求以异步方式发送到服务器。
XMLHttpRequest 对象
将下面的代码放进 ajax.html。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ajax</title>
</head>
<body>
<div id="new">
</div>
<script src="./scripts/addLoadEvent.js"></script>
<script src="./scripts/getHTTPObject.js"></script>
<script src="./scripts/getNewContent.js"></script>
</body>
</html>其中 addLoadEvent.js、getHTTPObject.js 和 getNewContent.js 都位于 scripts 的文件夹中。
为了模拟服务器响应,在 ajax.html 同目录下创建一个 example.txt 文件,包含以下内容:1
This was loaded as asynchronously!
该文件充当服务器的输出。
为了兼容不同 IE 版本的浏览器,需要写很多兼容代码,不过 IE 早就应该扫进历史的垃圾堆里了,所以就不兼容了。
创建一个 XMLHttpRequest 对象:1
2
3
4function getHTTPObject() {
var request = new XMLHttpRequest();
return request;
}使用:
1
var request = getHTTPObject();
XMLHttpRequest 对象有许多方法,其中最有用的是 open 方法,它用来指定服务器上将要访问的文件,指定请求的类型:GET、POST 或 SEND。该方法第三个参数用于指定请求是否以异步方式发送和处理。
在 getNewContent.js 文件中添加以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20function getNewContent() {
var request = getHTTPObject();
if (request) {
// 发起请求同一目录下的文件
request.open("GET", "example.txt", true);
request.onreadystatechange = function () {
if (request.readyState === 4) {
// 取出并保存数据
var para = document.createElement("p");
var txt = document.createTextNode(request.responseText);
para.appendChild(txt);
document.getElementById("new").appendChild(para);
}
};
// 发送请求
request.send(null);
} else {
alert("Sorry, your browser doesn't support XMLHttpRequest");
}
}request.readyState有 5 个可能的值:- 0 表示初始化
- 1 表示正在加载
- 2 表示加载完毕
- 3 表示正在交互
- 4 表示完成
只要 readyState 属性的值变成了 4,就可以访问服务器发送回来的数据。
responseText 是保存文本字符串的数据,responseXML 属性是保存 Content-Type 头部中指定为“text/xml”的数据,一个是 DocumentFragment 对象。结果:

异步请求有一个异步性的问题,脚本发送 XMLHttpRequest 请求之后,仍会继续执行,不会等待响应返回。
可以试着加两个警告框,在函数结尾和 onreadystateChange 后添加。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21function getNewContent() {
var request = getHTTPObject();
if (request) {
// 发起请求同一目录下的文件
request.open("GET", "example.txt", true);
request.onreadystatechange = function () {
if (request.readyState == 4) {
alert("respose received");
var para = document.createElement("p");
var txt = document.createTextNode(request.responseText);
para.appendChild(txt);
document.getElementById("new").appendChild(para);
}
};
// 发送请求
request.send(null);
} else {
alert("Sorry, your browser doesn't support XMLHttpRequest");
}
alert("function done");
}结果显示在函数结尾处的弹窗更快。
Hijax,渐进增强地使用 Ajax。
登录表单添加 Ajax 功能,需要拦截提交表单的请求(Hijax),让 XMLHttpRequest 请求代为发送,再取消 onsubmit 的默认请求。这样登录就更方便,用户感觉就更快了。
小结
本次学习了几种不同的向浏览器里的文档动态添加标记的方法。
传统:
- document.write 方法
- innerHTML 属性
DOM 方法:
- createElement 方法
- createTextNode 方法
- appendChild 方法
- insertBefore 方法
用 createElement 和 createTextNode 创造的只是孤儿,要用 appendChild 和 insertBefore 才能把这些孤儿插入到文档的节点树,呈现在浏览器的窗口。
对图片库的改进和 insertAfter 方法的创建,还有 Ajax 和 异步请求。