JavaScript DOM笔记04

动态创建标记

系列文章

一些传统方式

可以使用 document 对象的 .write(text) 方法方便地字符串插入到文档中。

例如,以下标记:

<body>
    <p>hello</p>
    <script>
        document.write("<p>inserting paragraph</p>")
    </script>
    <p>end</p>
</body>

在浏览器中的表示效果为:

document.write()

但它不符合行为与表现相分离的原则,而且必须混杂JavaScript和HTML代码。

现代浏览器都支持属性 innerHTML ,它获取的是元素内所有的HTML内容,且没有任何细节。

但是 innerHTML 属性既支持读取,又支持写入。

以下使用该属性在文档加载完成后额外插入一部分内容。注意插入内容时标签等内容需要手动添加:

window.onload = function() {
    var insertion = document.getElementById("insert-1");
    insertion.innerHTML = "<p>Insert this content by <em>innerHTML</em></p>"
}

这种技术无法区分是要进行插入还是替换,无法对刚插入的内容进行处理。

同时还应该注意,这些方法仅适用于HTML,而不能用于操作XML和XHTML。

利用DOM方法

动态创建标记的本质是修改DOM结点树。

向一个结点插入标记需要执行以下两步:

  1. 创建一个新的元素
  2. 将这个新元素插入结点树

可以使用 document.createElement(name) 创建一个结点对象,其参数是结点元素的标签名,例如段落标签 "p"

创建完成的结点对象已经有了 nodeTypenodeName 值。

为了将其插入文档的合适位置中,首先需要找到父结点的位置,然后使用 parent.appendChild(child) 方法将其添加进去。

以下示例为列表动态添加一个列表项:

<ol id="insert-list">
    <li>list item 1</li>
    <li>list item 2</li>
    <li>list item 3</li>
</ol>
<script>
    var list = document.getElementById("insert-list");
    var newitem = document.createElement("li");
    newitem.innerHTML = "inserted item";
    list.appendChild(newitem);
</script>

插入的效果如下,可以看出插入的位置在子结点的最后:

parent.appendChild()

可以使用DOM方法为元素结点添加内容,这需要通过创建文本结点对象实现。document.createTextNode(text) 用于创建文件结点对象,创建完成后使用 .appendChild() 方法即可将其追加进创建的结点对象中。

如果一个结点的内容比较多,包含嵌套结点,需要分别创建,逐一添加。动态创建这种结点的步骤如下:

var list = document.getElementById("insert-list");
var newitem = document.createElement("li");
newitem.appendChild(
    document.createTextNode("Inserted item by "));
var boldtext = document.createElement("strong");
boldtext.appendChild(document.createTextNode("DOM"));
newitem.appendChild(boldtext);
newitem.appendChild(document.createTextNode(" methods"));
list.appendChild(newitem);
append content

在任意位置创建

可以将元素插入到文档任意位置而不仅仅是追加到子元素结尾。DOM提供了 parent.insertBefore(new, target) 方法,用于把一个新元素插入到一个现有元素的前面。

它需要三个信息:想插入的新元素 new ,把新元素插入到哪个目标元素 target 前,以及目标元素的父元素 parent

不需要两次查找父元素和目标元素,目标元素的 parentNode 属性值就是它。在DOM里,元素结点的父元素必须是另一个元素结点。

向特定列表前插入一个段落的代码如下:

var newpar = document.createElement("p");
list.parentNode.insertBefore(newpar, list);

不存在对应的向后插入新元素方法。不过借助元素的 nextSibling 属性,可以编写一个函数实现它:

function insertAfter(newelem, target) {
    var parent = target.parentNode;
    if (parent.lastChild == target)
        parent.appendChild(newelem);
    else
        parent.insertBefore(newelem, target.nextSibling);
}

最后需要注意的是,如下形式的HTML文档:

<div class="section">
    <p>This is a <strong>bold</strong> text</p>
</div>

它在JavaScript视角的结点结构是这样的:

Ajax技术

Ajax用于异步加载页面。Ajax的主要优势就是对页面的请求以异步方式发送到服务器。而服务器不会用整个页面来响应请求,而会在后台处理请求,与此同时用户还能继续浏览页面并与页面交互。这种脚本则可以按需加载和创建页面内容,而不会打断用户的浏览体验

Ajax技术的核心是 XMLHttpRequest 对象,充当浏览器的脚本(客户端)与服务器之间的中间人的角色。

以下是一个使用Ajax技术的HTML模板:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Ajax</title>
</head>
<body>
    <div id="new"></div>
    <script src="scripts/load.js"></script>
    <script src="scripts/http.js"></script>
    <script src="scripts/content.js"></script>
</body>
</html>

不同浏览器实现 XMLHttpRequest 对象的方式不太一样,为了保证跨浏览器,必须写不同的代码分支,还要应用 try...catch... 来处理错误。错误处理的代码语法如下:

try {
    statements
}
catch (e) {
    statements  // error handler
}

这样获取新的 XMLHttpRequest 对象的函数为:

function getHTTPObject() {
    if (typeof XMLHttpRequest == 'undefined') {
        XMLHttpRequest = function() {
            try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
                catch (e) {}
            try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
                catch (e) {}
            try { return new ActiveXObject("Msxml2.XMLHTTP"); }
                catch (e) {}
            return false;
        }
    }
    return new XMLHttpRequest();
}

有了以上函数后,通过语句 var request = getHTTPObject(); 就可以获得一个可用的对象了。

该对象包含许多有用的方法,例如 .open(method, file, mode) 。它用来指定服务器上将要访问的文件,并指定请求类型:"GET""POST""SEND" 。其第三个参数用于指定请求是否以异步的形式发送和处理。

content.js 中添加如下代码:

function 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 text = document.createTextNode(request.responseText);
                para.appendChild(text);
                document.getElementById("new").appendChild(para);
            }
        };
        request.send(null);
    }
    else
        alert("Request failed");
}
addLoadEvent(getNewContent);

代码中的 onreadystatechange 是一个事件处理函数,它会在服务器给 XMLHttpRequest 对象送回响应的时候被触发执行,可以在函数内根据服务器具体响应做响应的处理。

在指定了请求的目标,也明确了如何处理响应后,就可以用 .send() 方法来发送请求了。

服务器在向 XMLHttpRequest 对象发回响应时,该对象有许多属性可以使用,浏览器会在不同阶段更新 readyState 属性的值,它有5个可能的值:0 表示未初始化;1 表示正在加载;2 表示加载完毕;3 表示正在交互;4 表示完成。

只要 readyState 属性的值变成了4,就可以访问服务器发送回来的数据了。

访问服务器发送回来的数据通过两个属性完成:responseText 保存文本字符串形式的数据;responseXML 保存 Content-Type 头部中指定为 "text/xml" 的数据,其实是一个 DocumentFragment 对象,可以用各种DOM方法处理这个对象。

使用Ajax时注意同源策略:使用 XMLHttpRequest 对象发送的请求只能访问与其所在HTML处于同一个域中的对象,不能向其它域发送请求。

注意异步请求的异步性:脚本在发送 XMLHttpRequest 请求之后,仍然会继续执行,不会等待响应返回。为此,如果其它脚本依赖于服务器的响应,那么需要把相应的代码都转移到指定给 onreadystatechange 属性的函数中。

Ajax应用主要依赖后台服务器,实际上是服务器端的脚本语言完成了绝大部分工作。XMLHttpRequest 对象只负责传递请求和响应,即使去掉它浏览器与服务器的响应也不会中断。

Ajax用作登录表单,需要通过 onsubmit 事件拦截提交表单的请求,让 XMLHttpRequest 请求代为向服务器发送信息。

Ajax应用主要依赖于服务器而不是客户端处理。