虽然DOM为操作节点提供了细致入微的控制手段,但是在需要文档插入大量新HTML标记的情况下,通过DOM操作仍然非常麻烦,因为不仅要创建一系列DOM节点,而且还要按照正确的顺序进行连接。相对而言,使用插入标记的技术,直接插入HTML字符串不仅更简单,速度也更快。
innerHTML()
在读模式下,innerHTML属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的HTML标记(不同浏览器返回的文本格式不同:IE和Opera会将所有标签转换为大写形式,Safari、Chrome和Firefox则是原原本本的按照原先文档中的格式返回)。在写模式下,innerHTML会根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素原来的所有子节点。
<button onclick="inner()">按钮</button> <div id="content"> <p>this is a <strong>title</strong></p> <ul> <li>item 1</li> <li>item 2</li> <li>item 3</li> <li>item 4</li> </ul> </div> <div id="add"></div> <script> function inner(){ var a = document.getElementById("content").innerHTML; console.log(a) var b = document.getElementById("add"); b.innerHTML = "<p>this is a <strong>demo</strong></p><a>one</a><br><a>two</a>" } </script>
设置了innerHTML之后,可以像访问文档中的其他节点一样访问新创建的节点,但是使用innerHTML属性也有一些限制。比如,在大多数浏览器中,通过innerHTML插入<script>元素并不会执行其中的脚本,IE8及更早版本是唯一能够在这种情况下执行脚本的浏览器,但是必须满足一些条件:一是必须为<script>元素指定defer属性,二是<script>元素必须位于有作用域的元素之后。<script>元素被认为是“无作用域元素”,也就是页面中看不到的元素,与<style>元素或者注释类似。
outerHTML()
outerHTML在读模式下,outerHTML返回调用它的元素及所有子节点的HTML标签。在写模式下,outerHTML会根据指定的HTML字符串创建新的DOM子树,然后用这个DOM子树完全替换调用元素。
<button onclick="outer()">按钮</button> <div id="outer"> <p>this is a <strong>title</strong></p> <ul> <li>item 1</li> <li>item 2</li> <li>item 3</li> <li>item 4</li> </ul> </div> <script> function outer(){ var div = document.getElementById("outer"); div.outerHTML = '<p>This is a paragraph</p>'; // 等同于 // var p = document.createElement("P"); // p.appendChild(document.createTextNode("This is a paragraph")); // div.parentNode.replaceChild(p,div); } </script>
insertAdjacentHTML()
insertAdjacentHTML()方法最早也是在IE中出现的,它接收两个参数:插入位置和要插入的HTML文本,第一个参数必须满足下列值之一:
- beforebegin:在当前元素之前插入一个紧邻的同辈元素;
- afterbegin:在当前元素之下插入一个新的子元素或者在第一个子元素之前再插入新的子元素;
- beforeend:在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素;
- afterend:在当前元素之后插入一个紧邻的同辈元素。
<button onclick="insert()">按钮</button> <div id="insert"> <p>this is a <strong>title</strong></p> <ul> <li>item 1</li> <li>item 2</li> </ul> </div> <script> function insert(){ var div = document.getElementById("insert"); div.insertAdjacentHTML("beforebegin",'<p>beforebegin</p>'); div.insertAdjacentHTML("afterbegin",'<p>afterbegin</p>'); div.insertAdjacentHTML("beforeend",'<p>beforeend</p>'); div.insertAdjacentHTML("afterend",'<p>afterend</p>'); } </script>
内存与性能问题
使用本节介绍的方法替换子节点可能会导致浏览器的内存占用问题,尤其是在IE中,问题更加明显。在删除带有事件处理程序或引用了其他JavaScript对象子树时,就有可能导致内存占用问题。假设某个元素有一一个事件处理程序(或者引用了一个JavaScript对象作为属性),在使用前述某个属性将该元素从文档树中删除后,元素与事件处理程序(或JavaScript对象)之间的绑定关系在内存中并没有一并删除。如果这种情况频繁出现, 页面占用的内存数量就会明显增加。因此,在使用innerHTML、outerHTML属性和insertAdjacentHTML()方法时,最好先手工删除要被替换的元素的所有事件处理程序和JavaScript对象属性(第13章将进一步讨论事件处理程序)。
不过,使用这几个属性一特别是使用 innerHTML,仍然还是可以为我们提供很多便利的。般来说,在插入大量新HTML标记时,使用innerhTML属性与通过多次DOM操作先创建节点再指定它们之间的关系相比,效率要高得多。这是因为在设置innerHTML或outerHTML时,就会创建-一个HTML解析器。这个解析器是在浏览器级别的代码(通常是C++编写的)基础上运行的,因此比执行JavaScript快得多。不可避免地,创建和销毁HTML解析器也会带来性能损失,所以最好能够将设置innerHtTML或outerHTML的次数控制在合理的范围内。例如,下列代码使用innerHTML,创建了很多列表项:
for (var i=0,len=values. length; i < len; i++)
{ ul. innerHTML += "<1i>” + values[i] + "</1i>"; //要避 免这种频繁操作! !
}
这种每次循环都设置一一次innerHTML的做法效率很低。而且,每次循环还要从innerHTML中读取一一次信息, 就意味着每次循环要访问两次innerHTML。 最好的做法是单独构建字符串,然后再-一次性地将结果字符串赋值给innerHTML,像下面这样:
var itemsHtml = ""; for(var i = 0;len = values.length;i < len;i++){ itemsHtml +="<li>" + values[i] + "</li>"; } ul.innerHtml = itemsHtml ;
这个例子的效率就要高很多,因为它只对innerHTML执行了一次复制操作。