插入DOM

当我们获得了某个DOM节点,想在这个DOM节点内插入新的DOM,应该如何做?

如果这个DOM节点是空的,例如,<div></div>,那么,直接使用innerHTML = '<span>child</span>'就可以修改DOM节点的内容,相当于“插入”了新的DOM节点。

如果这个DOM节点不是空的,那就不能这么做,因为innerHTML会直接替换掉原来的所有子节点。

有两个办法可以插入新的节点。一个是使用appendChild,把一个子节点添加到父节点的最后一个子节点。例如:

  1. <!-- HTML结构 -->
  2. <p id="js">JavaScript</p>
  3. <div id="list">
  4. <p id="java">Java</p>
  5. <p id="python">Python</p>
  6. <p id="scheme">Scheme</p>
  7. </div>

&lt;p id="js"&gt;JavaScript&lt;/p&gt;添加到&lt;div id="list"&gt;的最后一项:

  1. var
  2. js = document.getElementById('js'),
  3. list = document.getElementById('list');
  4. list.appendChild(js);

现在,HTML结构变成了这样:

  1. <!-- HTML结构 -->
  2. <div id="list">
  3. <p id="java">Java</p>
  4. <p id="python">Python</p>
  5. <p id="scheme">Scheme</p>
  6. <p id="js">JavaScript</p>
  7. </div>

因为我们插入的js节点已经存在于当前的文档树,因此这个节点首先会从原先的位置删除,再插入到新的位置。

更多的时候我们会从零创建一个新的节点,然后插入到指定位置:

  1. var
  2. list = document.getElementById('list'),
  3. haskell = document.createElement('p');
  4. haskell.id = 'haskell';
  5. haskell.innerText = 'Haskell';
  6. list.appendChild(haskell);

这样我们就动态添加了一个新的节点:

  1. <!-- HTML结构 -->
  2. <div id="list">
  3. <p id="java">Java</p>
  4. <p id="python">Python</p>
  5. <p id="scheme">Scheme</p>
  6. <p id="haskell">Haskell</p>
  7. </div>

动态创建一个节点然后添加到DOM树中,可以实现很多功能。举个例子,下面的代码动态创建了一个&lt;style&gt;节点,然后把它添加到&lt;head&gt;节点的末尾,这样就动态地给文档添加了新的CSS定义:

  1. var d = document.createElement('style');
  2. d.setAttribute('type', 'text/css');
  3. d.innerHTML = 'p { color: red }';
  4. document.getElementsByTagName('head')[0].appendChild(d);

可以在Chrome的控制台执行上述代码,观察页面样式的变化。

insertBefore

如果我们要把子节点插入到指定的位置怎么办?可以使用parentElement.insertBefore(newElement, referenceElement);,子节点会插入到referenceElement之前。

还是以上面的HTML为例,假定我们要把Haskell插入到Python之前:

  1. <!-- HTML结构 -->
  2. <div id="list">
  3. <p id="java">Java</p>
  4. <p id="python">Python</p>
  5. <p id="scheme">Scheme</p>
  6. </div>

可以这么写:

  1. var
  2. list = document.getElementById('list'),
  3. ref = document.getElementById('python'),
  4. haskell = document.createElement('p');
  5. haskell.id = 'haskell';
  6. haskell.innerText = 'Haskell';
  7. list.insertBefore(haskell, ref);

新的HTML结构如下:

  1. <!-- HTML结构 -->
  2. <div id="list">
  3. <p id="java">Java</p>
  4. <p id="haskell">Haskell</p>
  5. <p id="python">Python</p>
  6. <p id="scheme">Scheme</p>
  7. </div>

可见,使用insertBefore重点是要拿到一个“参考子节点”的引用。很多时候,需要循环一个父节点的所有子节点,可以通过迭代children属性实现:

  1. var
  2. i, c,
  3. list = document.getElementById('list');
  4. for (i = 0; i < list.children.length; i++) {
  5. c = list.children[i]; // 拿到第i个子节点
  6. }

练习

对于一个已有的HTML结构:

  • Scheme
  • JavaScript
  • Python
  • Ruby
  • Haskell
    1. <!-- HTML结构 -->
    2. <ol id="test-list">
    3. <li class="lang">Scheme</li>
    4. <li class="lang">JavaScript</li>
    5. <li class="lang">Python</li>
    6. <li class="lang">Ruby</li>
    7. <li class="lang">Haskell</li>
    8. </ol>

按字符串顺序重新排序DOM节点:

  1. 'use strict';
  2. // sort list:
  3. // 测试:
  4. ;(function () {
  5. var
  6. arr, i,
  7. t = document.getElementById('test-list');
  8. if (t && t.children && t.children.length === 5) {
  9. arr = [];
  10. for (i=0; i<t.children.length; i++) {
  11. arr.push(t.children[i].innerText);
  12. }
  13. if (arr.toString() === ['Haskell', 'JavaScript', 'Python', 'Ruby', 'Scheme'].toString()) {
  14. alert('测试通过!');
  15. }
  16. else {
  17. alert('测试失败: ' + arr.toString());
  18. }
  19. }
  20. else {
  21. alert('测试失败!');
  22. }
  23. })();

原文: https://wizardforcel.gitbooks.io/liaoxuefeng/content/js/35.html