0%

JavaScript-学习笔记(6):动态创建标记

网页的结构由标记语言创建,js 可用来改变细节而不改变结构。
同样,js 也可用来改变网页的结构和内容。

传统方法

  1. document.write
    write方法可以把字符串插入到文档里。
    使用方法,编辑 test.html 文件:

    1
    2
    3
    4
    5
    <body>
    <script>
    document.write("<p> 这是一个段落 </p>");
    </script>
    </body>

    结果:
    Image

    缺点:这样做会使得标记与 js 代码混杂在一起,代码即不容易理解,也无法享受代码与结构分离的好处。应避免<body>乱用<script>标签,尽量减少使用document.write

  2. 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
    4
    window.onload = function () {
    var test = document.getElementById("test");
    alert(test.innerHTML);
    };

    结果:
    Image
    结果显示,innerHTML 只是粗暴地把元素里面所有的代码找出来。当需要把插入大段的 HTML 文件时,它就有用武之地了。

    写:

    1
    2
    3
    4
    window.onload = function () {
    var test = document.getElementById("test");
    test.innerHTML = "<p> Insert in <em>innerHTML</em> </p>";
    };

    结果:
    Image
    直接赋值对应的即可,赋值后会把原来的 html 代码覆盖掉。
    这是 HTML 专有属性,不能用于其它标记语言文档,如 xhtml 文档。

DOM 方法

  1. 添加节点
    编辑 test.html,让id=test的 div 标签内容为空。

    1
    <div id="test"></div>

    把一段文本插入到上面的 div 元素,用 DOM 的话来形容,添加一个 p 元素节点,再把这个节点作为 div 元素的子节点。
    具体步骤:

    1. createElement 创建一个新元素:
      语法:
      1
      document.createElement(nodeName);
      创造一个 p 元素结点并赋值给一个变量:
      1
      var para = document.createElement("p");
    2. appendChild 把新元素插入节点数:
      使用 appendChild 方法。
      语法:
      1
      parent.appendChild(child);
      例子:
      1
      2
      3
      var test = document.getElementById("test");
      var para = document.createElement("p");
      test.appendChild(para);
      或者:
      1
      document.getElementById("test").appendChild(document.createElement("p"));
  2. 添加文本节点
    使用 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
    7
    window.onload = function () {
    var test = document.getElementById("test");
    var para = document.createElement("p");
    test.appendChild(para);
    var txt = document.createTextNode("test");
    para.appendChild(txt);
    };

    结果:
    Image

  3. 一个复杂的例子:
    创建一个以下节点:

    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
    16
    window.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);
    };

    结果:
    Image

图片库

里面只有一个图片和一段文字是为show_pic服务,这很些元素的存在是为了让 DOM 处理它们,那么使用 DOM 来创建它们是最合适的。

  1. 先把文档里的相关元素删掉。

  2. 先编写一个函数 prepare_placeholder 并把它放进 show_pic.js文件,然后在文档加载时调用此函数。

  3. 函数的任务:

    1. 创建一个 img 元素节点;
    2. 设置这个节点的 id 属性;
    3. 设置这个节点的 src 属性;
    4. 设置这个节点的 alt 属性;
    5. 创建一个 p 元素节点;
    6. 设置这个节点的 id 属性;
    7. 创建一个文本节点;
    8. 把这个文本节点追加到 p 元素上;
    9. 把 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 标签的末尾。如果在中间的话,就需要用到其它方法了。

  4. 已有元素前插入元素
    DOM 提供了名为 insertBefore() 方法。使用此方法时,必须告诉它三件事:

    1. 新元素:你想插入的元素;
    2. 目标元素:你想反把这个元素插入到哪个元素之前;
    3. 父元素:目标元素的父元素。

    语法:

    1
    parentElement.insertBefore(newElement, targetElement);

    例子:

    1
    2
    var gallery = document.getElementById("image_gallery");
    gallery.parentElement.insertBefore(placeholder, gallery);

    这样就把元素插入到图片元素之前了。

  5. 元素后插入元素
    DOM 没有提供这个方法,但是可以自己写一个 insertAtfer 方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function 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
    22
    function 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 的主要优势是对页面的请求以异步方式发送到服务器。

  1. XMLHttpRequest 对象
    将下面的代码放进 ajax.html。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    <!DOCTYPE html>
    <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
    4
    function 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
    20
    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 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 对象。

    结果:
    Image

    异步请求有一个异步性的问题,脚本发送 XMLHttpRequest 请求之后,仍会继续执行,不会等待响应返回。
    可以试着加两个警告框,在函数结尾和 onreadystateChange 后添加。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function 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");
    }

    结果显示在函数结尾处的弹窗更快。

  2. Hijax,渐进增强地使用 Ajax。
    登录表单添加 Ajax 功能,需要拦截提交表单的请求(Hijax),让 XMLHttpRequest 请求代为发送,再取消 onsubmit 的默认请求。这样登录就更方便,用户感觉就更快了。

小结

本次学习了几种不同的向浏览器里的文档动态添加标记的方法。
传统:

  • document.write 方法
  • innerHTML 属性

DOM 方法:

  • createElement 方法
  • createTextNode 方法
  • appendChild 方法
  • insertBefore 方法

用 createElement 和 createTextNode 创造的只是孤儿,要用 appendChild 和 insertBefore 才能把这些孤儿插入到文档的节点树,呈现在浏览器的窗口。

对图片库的改进和 insertAfter 方法的创建,还有 Ajax 和 异步请求。

------------ 感谢你的阅读 ------------