案例研究:JavaScript美术馆的改进版
前面学习了JavaScript的编程原则和良好习惯,这节利用前面所学的编程原则和良好习惯,对JavaScript美术馆进行改进。
解决“预留退路”问题
第一个问题是:如果JavaScript功能被禁用,会怎么样?
仔细检查之前写的showPic()函数的代码后,可以知道之前的脚本已经为次预留了退路:即使JavaScript功能被禁用,用户也可以毫无问题地浏览到JavaScript美术馆里的所有图片,网页里的所有链接也都可以正常的工作:
<li>
<a href="images/巴西野牡丹.jpg" οnclick="showPic(this); return false;" title="baxiyebaihe">巴西野百合</a>
</li>
在没有JavaScript“干扰”的情况下,浏览器经验者href属性给出的链接前进,用户将看到一张新图片而不是“该网页无法显示”之类的出错信息。虽说这与JavaScript在当前网页切换显示新图片的效果要略差一些,但网页的基本功能并为受到损害——页面上的所欲内容都可以访问。
说明,前面写的JavaScript美术馆通过了第一个测试。
解决“分离JavaScript”问题
<li>
<a href="images/巴西野牡丹.jpg" " title="baxiyebaihe">巴西野百合</a>
</li>
把JavaScript分离出来,必须找到一种“挂钩”把JavaScript代码与HTML文档中的有关标记关联起来。有很多方法可以实现这一目的。
更为方便简单的是,我们观察到图片清单里的各个链接有一个共同点:他们都包含在同一个列表清单元素(ul)里。给整个图片清单设置一个独一无二的ID的办法要简单得多:
<ul id="imagegallery">
<li>
<a href="images/巴西野牡丹.jpg" οnclick="showPic(this); return false;" title="baxiyebaihe">巴西野百合</a>
</li>
<li>
<a href="images/白玫瑰.jpg" οnclick="showPic(this); return false;" title="baimeigui">白玫瑰</a>
</li>
<li>
<a href="images/白玫瑰裙.jpg" οnclick="showPic(this); return false;" title="baimeiguiqun">白玫瑰裙</a>
</li>
<li>
<a href="images/sunflower.jpg" οnclick="showPic(this); return false;" title="xiangrikui">向日葵</a>
</li>
</ul>
添加事件处理函数、进行必要检查、创建必要变量
现在,需要编写一个简短的函数把有关操作关联到onclick事件上,将其命名为prepareGallery。
下面是这个函数应该要完成的工作:
- 检查当前浏览器是否理解getElementsByTagName()方法
- 检查当前浏览器是否理解getElementById()方法
- 检查当前网页是否包含着一个id属性是“imagegallery”的元素
- 构造一个循环来对“imagegallery”元素中的链接进行遍历处理
- 对onclick事件处理函数进行设置,让它在有关链接被点击时弯沉改一下操作:1.把这个链接作为参数传递给showPic()函数;2.取消链接被点击时的默认行为,不让浏览器打开这个勘界。
结构化程序设计(structed programming)理论有一条原则:每个函数应该只有一个入口点和一个出口点。
但是,同一个函数有多个出口点的情况是可以接受的,但前提是它们应该集中出现在这个函数的开头部分。
目前为止的prepareGallery()函数:
function prepareGallery(){
if (!document.getElementsByTagName) return false;
if(!document.getElementById) return false;
if(!document.getElementById("imagegallery")) return false;
var gallery=document.getElementById("imagegallery");
var links=gallery.getElementsByTagName("a");
}
创建循环
for(var i=0;i<links.length;i++){
}
完成必要的操作
links[i].οnclick=function(){}
这条语句定义了一个
匿名函数。这是一种”临时抱佛脚”式的函数创建办法,最适合用来定义在整个脚本里只出现了一次的函数:
匿名函数没有名字,只能在哪里定义、在哪里使用。
完成JavaScript函数
function prepareGallery(){
if (!document.getElementsByTagName) return false;
if(!document.getElementById) return false;
if(!document.getElementById("imagegallery")) return false;
var gallery=document.getElementById("imagegallery");
var links=gallery.getElementsByTagName("a");
<strong>for(var i=0;i<links.length;i++){
links[i].οnclick=function(){
showPic(this);
return false;
}
}</strong>
}
调用此函数时,它将把onclick事件绑定到id属性值等于“imagegallery”的那个元素所包含的各个链接上去。
把多个JavaScript函数绑定到onload事件处理函数上
必须执行prepareGallery()函数才能对onclick事件进行绑定。
如果现在就执行这个函数,它将无法完成其工作:在HTML文档完成加载之前,DOM是不完整的。必须让这个函数只在网页加载完毕之后才得到执行。网页加载完毕时会出发一个Onload事件,这个事件与window对象相关联。为了让事态的发展不偏离计划,必须把prepareGallery()函数绑定到这个事件上:
window.οnlοad=prepareGallery;
这条浅显易懂的语句解决了上面的问题,但是考虑还不长远。如果有两个函数:firestFunction()和secondFunction()。想让他们两都在页面加载时得到执行,该如何解决?如果把它们一一绑定到onload事件上,它们中将只有最后那个才会被实际执行:
window.οnlοad=firstFunction;
window.οnlοad=secondFunction;
secondFunction将取代firestFunction。由此可以得出一个结论:每个事件处理函数只能绑定一条指令。
还有有个小技巧可以绕过这一难题:可以先创建一个匿名函数来容纳这两个函数,然后把那个匿名函数绑定到onload事件上,如下所示:
window.οnlοad=function(){
firstFunction();
secondFunction();
}
这个技巧既简单又实用——在需要绑定的函数不是很多的场合,这应该是最简明的解决方案了。
还有一个更通用的解决方案——不管要在页面加载完毕时执行多少个函数,它都可以应付自如。需要额外写一个函数,这个函数的名字是addLoadEvent(),它只有一个参数:打算在页面加载完毕时执行的函数的名字。
下面是addLoadEvent()函数将要完成的操作:
- 把现有的window.onload事件处理函数的值存入变量onload。
- 如果在这个处理函数上还没有绑定任何函数,就像平时那样把新函数添加给它。
- 如果在这个处理函数上已经绑定了一些函数,把新函数追加到现有的指令的末尾。
function addLoadEvent(){
var oldοnlοad=window.onload;
if(typeof window.onload!='function'){
window.οnlοad=func;
}else{
window.οnlοad=function(){
oldonload();
func();
}
}
}
这相当于把那些将在页面加载完毕时执行的函数创建为一个队列。如果想把刚才那两个函数添加到这个队列里去,只需写如下的代码就行了:
addLoadEvent(firstFunction);
addLoadEvent(secondFunction);
这个函数非常有用,尤其是在代码变得越来越复杂的时候。无论打算在页面加载完毕时执行多少个函数,都只要多写一条语句就可以安排好一切。
JavaScript函数的优化:不要做太多的假设
showPic()函数没有进行任何测试和检查。showPic()函数中用到了id属性值等于placeholder和description的元素,但却没有做任何的检查:
慎用onkeypress事件处理函数
这里决定不添加onkeypress事件处理函数。这个事件处理函数很容易引起问题。用户每按下键盘上的一个按键都会触发它。在某些浏览器里,那甚至包括Tab键!这意味着如果绑定在onkeypress事件处理函数上的某个函数的返回值是false,那些使用键盘来进行浏览的用户就无法离开当前连接。这里的“JavaScript美术馆”网页就存在着这样的问题——只要图片切换成功,showPic()函数就将返回false。实际上,onclick事件处理函数比想象中要聪明,尽管它的名字“onclick”给人以一种它只与鼠标点击动作相关联的印象,但事实并非如此:在几乎所有的浏览器里,用TAB键移动到某个链接然后按下回车键的动作也会触发onclick事件。从这一点来看,把它命名为“onactive”也许更恰如其分。
PS:如果没有特殊理由,最好不要使用onkeypress事件处理函数。
把JavaScript和CSS结合起来
在HTML文档里为JavaScript代码留下了一个“挂钩”:
<ul id="imagegallery">
这个挂钩完全可以用在CSS样式表里。