案例研究:图片库改进版
在最佳实践学到如何平稳退化、分离JavaScript、向后兼容和提高性能,在这里,将运用这些知识来改进图片库。
快速回顾
在案例研究:JavaScript图片库里,我编写了一个用来替换“占位符”图片的src属性的脚本,只用一个网页就建立起了图片库。
它支持平稳退化吗
第一个问题是:“如果JavaScript功能被禁用,会怎样?” 分析可知:即使JavaScript功能被禁用,用户也可以浏览图片库里的所有内容,网页里的所有连接也都可以正常工作:
在JavaScript功能被禁用的情况下,浏览器将沿着href属性给出的链接前进,用户将看到一张图片而不是“该页无法显示”之类的错误信息。
它的JavaScript与HTML标记是分离的吗
由代码知显然不是分离的。 把JavaScript代码移出HTML文档不是难事,但为了让浏览器知道页面里都有哪些链接有着不一样的行为,我们必须找到一种“挂钩”把JavaScript代码与HTML文档中的有关标记关联起来。有多种办法可以做到这点: 可以给每个链接分别添加一个class属性:
但这种方法不够理想,这与给它们分别添加事件处理函数同样麻烦。 第二种办法:图片清单里的每个链接有一个共同点:它们都包含在同一个列表清单元素里。给整个清单设置一个独一无二的id的办法要简单得多:
添加事件处理函数
现在,需要编写一个简短的函数把有关操作关联到onclick事件上。我将其命名为prepareGallery。 下面是这个函数需要完成的工作:
检查当前浏览器是否理解getElementsByTagName和getElementById;
检查当前网页是否存在一个id为imagegallery的元素;(这项测试是一个预防性措施。现在我知道调用这个JavaScript函数的文档里有一个id属性值等于imagegallery的列表清单元素,但我不确定这在将来会不会发生变化。有了这个预防性措施,即使以后从网页上删掉图片库,也用不着这个网页的JavaScript代码会突然出错。作为一条原则,如果想用JavaScript给某个网页添加一些行为,就不应该让JavaScript代码对这个网页的结构有任何依赖。)
遍历imagegallery元素中的所有连接;
设置onclick事件,让它在有关链接被点击时完成以下操作: 1.把这个链接作为参数传递给showPic函数; 2.取消链接被点击时的默认行为,不让浏览器打开这个链接。
调用此函数,就会把onclick事件绑定到“id”等于“imagegallery”的元素内的各个链接元素上。
共享onload事件
必须执行prepareGallery函数才能对onclick事件进行绑定。 如果马上执行这个函数,它就无法完成其工作。在HTML文档完成加载之前执行脚本,此时DOM是不完整的。 应该让这个函数在网页加载完毕后立刻执行。网页加载完毕时会触发一个onload事件,这个事件与window对象相关联。所以我们须把prepareGallery函数绑定到这个事件上:
但如果有多个函数,如果像下面这样逐一绑定,它们中只有最后那个函数才会得到实际执行:
有种办法可以解决这个问题:可以先创建一个匿名函数来容纳这两个函数,然后把那个匿名函数绑定到onload事件上:
这里还有一个弹性最佳的解决方案——使用addloadEvent函数。它只有一个参数:打算在页面加载完毕时执行的函数的名字。 下面是addloadEvent函数将要完成的操作:
把现有的window.onload事件处理函数的值存入变量oldonload。
如果在这个处理函数上还没有绑定任何函数,就像平时那样把新函数添加给它。
如果在这个处理函数上已经绑定了一些函数,就把新函数追加到现有指令的末尾。
下面是addloadEvent函数的代码清单:
这将把那些在页面加载完毕时执行的函数创建为一个队列。如果想把刚才那两个函数添加到这个队列里去,只需要写出以下代码就行了:
不要做太多假设
在showPic函数里发现的一个问题是,没有让它进行任何测试和检查。 showPic函数将由prepareGallery函数调用,而我已经在后者的开头对getElementById和getElementsByTagName等DOM方法是否存在进行过检查,所以我确切地知道用户的浏览器不会因为不理解这个方法而出问题。不过并未对id值等于placeholder和description的元素进行任何检查。
改进后的showPic()函数不再假设有关标记文档里肯定存在着placeholder图片和description元素。即使文档里没有placeholder图片,也不会发生任何JavaScript错误。 可是还有一个问题:如果把placeholder图片从标记文档里删除并在浏览器里刷新这页面,就会出现这种情况,无论点击imagegallery清单里的哪个链接,都没有任何反应。这意味着我们的脚本不能平稳退化。问题在于prepareGallery函数做出了这样一个假设:showPic函数肯定会正常返回。基于这一假设,prepareGallery函数取消了onclick事件的默认行为。
showPic函数应该返回两个可能的值:
如果图片切换成功,返回true。
如果图片切换不成功,返回false。
为修正这个问题,应该在返回前验证showPic的返回值,以便决定是否阻止默认行为。showPic返回true,那么更新placeholder。在onclick事件处理函数中,我们可以利用“!”对showPic的返回值进行取反:
现在,如果showPic返回true,我们就返回false,浏览器不会打开那个链接。如果showPic返回false,那么我们认为图片没有更新,于是返回true以允许默认行为发生。下面是prepareGallery函数现在的代码清单:
优化
这个函数已经很完善了。尽管如此,在showPic函数里仍存在一些需要处理的假设。 例如,假设每个链接都有一个title属性:
检查方法:
在title属性不存在时把text的值设置为空字符串:
其他检查:
最终prepareGallery()和showPic()代码清单:
把JavaScript与CSS结合起来
把JavaScript代码从HTML文档里分离出去还带来另一个好处。在把内嵌型事件处理函数移出标记文档时,我在文档里为JavaScript代码留下了一个“挂钩”:
这个挂钩完全可以用在CSS样式表里。
还可以把图片链接换成缩略图:
DOM Core和HTML-DOM
至此,在编写JavaScript代码时只用到了一下几个DOM方法:
getElementById
getElementsByTagName
getAttribute
setAttribute
这些方法都是DOM Core的组成部分。它们并不专属于JavaScript,支持DOM的任何一种程序设计语言都可以使用它们。它们的用途也并非仅限于处理网页,它们可以用来处理用任何一种标记语言(比如XML)编写出来的文档。 在使用JavaScript语言和DOM为HTML文件编写脚本时,还有许多属性可供选择。例如属性onclick,用于图片库的事件管理。这些属性属于HTML-DOM,它们在DOM Core出现之前很久就已经为人们所熟悉了。 比如说,HTML-DOM提供了一个forms对象。这个对象可以把下面这样的语句:
简化为:
类似地,HTML-DOM还提供了许多描述各种HTML元素的属性。比如说,HTML-DOM图片提供的src属性可以把下面这样的语句:
简化为:
这些方法和属性可以相互替换。同样的操作既可以只使用DOM Core来实现,也可以使用HTML-DOM来实现。通常HTML-DOM代码更短,必须提醒一点的是,它们只能用来处理web文档。
效果预览
Last updated