WEB 开发中我们常常在 IE 下遇到“浏览器无法打开,操作已终止”的错误提示,这个问题往往另初学者头痛不已。即便是经验老道的工程师,也时常被这个问题搞得焦头烂额。深究此问题的根源,谷老师和白老师给我们最多的参考答案基本上都是千篇一律的“因为文档未完成前使用了 appendChild 方法”,而提出得解决方案有如下几种:
- 给 document 对象绑定 onreadystatechange 事件,在 document.readyState 为 complete | loaded 的时候执行 appendChild 方法;
- 给 window 对象绑定 onload 事件,事件触发时执行 appendChild 方法;
- 将 执行 appendChild 方法的代码块后置到DOM Tree的尾部(如:body 标签结束前后,html 标签结束前后);
- 延时处理 使用 setTimeout 或者 setInterval 不断的对 document.readyState 进行判断,一旦满足条件则执行 appendChild 操作并清空计时器;
上述解决方案一般情况下都是可行的,但并不完美。有时候我们需要的 DOM 操作可能因为上下文关系,不得不在文档完成前去进行。
接下来,我们就需要找到终止操作的根源,何时会触发这个错误,如何避免。
创建一个简单的 html 文件,确保会出现“终止操作”错误。代码如下:
------------------------
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Jtails Template Page</title>
</head>
<body>
<div id="a" style="border:1px solid #000;">i'm a div element a
</div>
<div id="b" style="border:1px solid #000;">i'm a div element b
<div id="c" style="border:1px solid #990;">i'm a div element c
<script type="text/javascript">
var p = document.body;
var c = document.createElement("div");
c.innerHTML = "i'm a div element from dom created";
p.appendChild(c);
</script>
</div>
</div>
<div id="d" style="border:1px solid #000;">i'm a div element d
</div>
</body>
</html>
------------------------
下面对脚本块中的变量 p 做不同的赋值:
- document.getElementById("a"),AMAZING! 错误消失了;
- document.getElementById("b"),SHIT! 该死的错误又出现了;
- document.getElementById("c"),YEAH! 错误消失;
- document.getElementById("d"),囧rz 不做任何解释,一般性 js 报错;
- document.body,Oh NO! 我不想看到操作被终止;
上述不同的测试结果中,我们可以得出 appendChild or insertBefore 操作其实并非在文档完成前不具可操作性,而是执行的时机问题。OK,下面我们将 script 块放到 id 为 d 的 div 结点中( <div id="d">这个标签内 )再做上述的测试:
- document.getElementById("a"),AMAZING! 错误消失了;
- document.getElementById("b"),AMAZING! 错误消失了;
- document.getElementById("c"),AMAZING! 错误消失了;
- document.getElementById("d"),AMAZING! 错误消失了;
- document.body,囧囧囧囧囧囧囧囧,错误重现;
这次测试中错误只在 document.body 操作的时候发生,仔细分析分析,相信老道的工程师们已经看出其中的猫腻了。执行 appendChild 操作时,DOM Tree 的完成点(上述操作中我们可以将脚本块的结束时)的所在结点执行操作时并不会发生错误,而其父节点执行操作时错误就会重现,实际上当前结点也并未完成。
草草总结下(要赶班车上班了。。。):
由此我们可以得出,当一个未完成结点的父结点执行 DOM append|insert 操作时,IE 就会重现“操作终止”的错误。或者可以更通俗来阐述,我们无法在一个正在修建的建筑上直接去修建比当前最高楼层更高的楼层。