前言:本文的对象是javascript新手,如果您不知道document.clear()干什么用的,那么也可以读写去。
关键字:document.clear, dom 0 event model, scope chain
晚上学习"javascript:the definitive guide"的过程中,碰到一件当时看来很“诡异”的事情。为了验证select控件可以通过select.options.length=0;来清空, 我写了一个clear方法。然后使用inline property的形式给一个按钮的click事件赋值:οnclick="clear();"。问题就来了,无论我在哪个浏览器上测试,select就 是一点反应没有。我有点奇怪:这么权威的书也出错!(不好意冤枉了作者)
接着我试了一下select.innerHTML="";心想只能用这个方法替代清空了,发现也没有反应。 又试了一下alert,终于发现问题根源了:代码没错,方法的调用有问题。当时我甚至有了“clear是关键字”的猜测(大家不要笑话我)。之后将方法名修改为clearOptions,问题解决了。
问题是解决了,但是还是不大明白为什么clear有问题(终于发现clear不是关键字了)。于是google了一下,发现已经有前辈经历过了。发现clear是document的一个方法,定义与w3c dom 1不过 已经弃之不用了。虽然不用了但还是存在的。由于在dom 0 model中event handler是作为target的属性存在的,因此从代码对应的文档结构可以看出,该按钮的click event handler(本身是一个函数)执行的时候首先要查找clear()方法。Click event handler的scope chain可以简化为:Call Object->input->form->body->document->window。而我们想要调用的clear方法是定义在window下面的(window的属性),在这中间偏偏有一个拦路虎就是document,它也有一个clear方法。这就直接造成了我对definitive guide的作者的误解。
这个原因也找到了,以后写代码的时候要注意了。另外,你会发现这个click event handler要调用一次clear方法很不容易,要经过多少收费站啊!这对于一个关心代码质量的人来说是不可忍受的。比较好的做法是将clear方法直接赋值给click event handler:select.οnclick=clear;也可以通过事件注册方法(addEventListener and attachEvent)。代码如下:<%
@ Page Language="C#" AutoEventWireup="true" CodeFile="SelectDemo.aspx.cs" Inherits="SelectDemo" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>select demo</title>
<script type="text/javascript">
function addOption(text,value)
{
var select=document.getElementsByTagName("select")[0];
var op=new Option(text,value,false,false);
var l=select.options.length;
select.options[l]=op;
}
function deleteOption(i)
{
var select=document.getElementsByTagName("select")[0];
select.options[i]=null;
}
function clear()
{
var select=document.getElementsByTagName("select")[0];
select.options.length=0;
alert(typeof(this.addOption));
}
var $=function(id)
{return typeof(id)=="string"?document.getElementById(id):id;}
function pageLoad()
{
var btn=$('btnClear');
// if(document.addEventListener)
// btn.addEventListener("click",clear,false);
// else if(document.attachEvent){
// btn.attachEvent("onclick",clear);
// }
btn.onclick=clear;
}
</script>
</head>
<body onload="pageLoad()">
<form id="form1" runat="server">
<div>
<select id="list1">
<option value="1">Apple</option>
<option value="2">Google</option>
<option value="3">Microsoft</option>
</select>
<input type="button" value="add" onclick="addOption('Sun',4);" />
<input type="button" value="delete" onclick="deleteOption(0);" />
<input id="btnClear" type="button" value="clear" />
</div>
</form>
</body>
</html
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SelectDemo.aspx.cs" Inherits="SelectDemo" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>select demo</title>
<script type="text/javascript">
function addOption(text,value){
var select=document.getElementsByTagName("select")[0];
var op=new Option(text,value,false,false);
var l=select.options.length;
select.options[l]=op;
}
function deleteOption(i){
var select=document.getElementsByTagName("select")[0];
select.options[i]=null;
}
function clear(){
var select=document.getElementsByTagName("select")[0];
select.options.length=0;
alert(typeof(this.addOption));
}
var $=function(id){return typeof(id)=="string"?document.getElementById(id):id;}
function pageLoad(){
var btnClear=$('btnClear'),btnAdd=$('btnAdd'),btnDelete=$('btnDelete');
var btnClear1=$('btnClear1'), btnClear2=$('btnClear2');
btnClear1.onclick=clear;
btnClear2.onclick=function(){clear();}
btnAdd.onclick=function(){addOption('Sun',4);};
btnDelete.onclick=function(){deleteOption(0);};
}
</script>
</head>
<body onload="pageLoad()">
<form id="form1" runat="server">
<div>
<select id="list1" name="select1">
<option value="1">Apple</option>
<option value="2">Google</option>
<option value="3">Microsoft</option>
</select>
<input id="btnAdd" type="button" value="add" />
<input id="btnDelete" type="button" value="delete" />
<input id="btnClear" type="button" value="clear" onclick="clear();" />
<input id="btnClear1" type="button" value="clear1" />
<input id="btnClear2" type="button" value="clear2" />
</div>
</form>
</body>
</html>
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![ContractedSubBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这样做也可以解决问题,并且是更好的方法。并且将event handler中用到的方法拷贝到target中作为属性可以避免其它的你不知道的拦路虎,值得推荐。
之后我又试了一下将方法赋值给按钮的onclick属性(参加代码中的btnClear2),原以为这样写也是不能成功的。结果发现不然。经过仔细阅读definitive guide,发现了一个被我忽视的问题:scope of event handler在赋值给html attribute 和html property的时候是不一样的。前者(参考btnClear)的scope chain和之前分析的类似,是比较复杂的,包含了dom tree。但是这一条本身没有标准,因此各个浏览器在实现的时候也可能不一致。后者(参考btnClear2)的scope chain则是正常的,大致是:Call Object->window。因此btnClear2的onclick是没有问题的。