目录
4.案例研究:JavaScript图片库
关于为什么要用JavaScript创建图片库,有什么好处?
如果简单地把所有图片放在一个网页中,这份文档就会变得过于庞大,用户下载这个网页的数据量就会变大,等待时间就会过长。因此为每张图片分别创建一个网页的解决方案值得考虑,使一个庞大的图片集合的网页变成许多便于下载浏览的网页,但给每张图片制作网页过于麻烦,因此用JavaScript来创建一个图片库将是最佳选择:把整个图片库的浏览链接安排在图片库主页中,只在用户点击这个主页的某个图片链接的时候才把相应图片传送给他。
4.1 标记
首先我编写了一个标记文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片库</title>
</head>
<body>
<h1>四大天王</h1>
<ul id="imagegallery">
<li>
<a href="图片库/300.jpg" title="刘德华">刘德华</a>
</li>
<li>
<a href="图片库/300 (1).jpg" title="张学友">张学友</a>
</li>
<li>
<a href="图片库/300 (2).jpg" title="黎明">黎明</a>
</li>
<li>
<a href="图片库/0.jpg" title="郭富城">郭富城</a>
</li>
</ul>
<img id="placeholder" src="图片库/true.jpg" alt="123">
<p id="description">choose a image</p> //占位图片和描述
</body>
</html>
接下来通过编写JS代码实现点击某个链接时,停留在该页面而不会转到其他窗口。点击某个链接时,能同时看见图片以及原有的图片清单。
4.2 JavaScript
DOM解决方案的函数
function showPic(whichPic) {
var source = whichPic.getAttribute('href'); //取出链接
var placeholder = document.getElementById('placeholder'); //取出占位符的元素节点
placeholder.setAttribute('src',source) //设置src属性
}
4.3 应用这个JavaScript函数
如果仅仅是把这个JS文件引用进HTML文档中,那他永远不会被调用,我们需要给图片列表的链接添加行为,即事件处理函数,这个showPic函数才会被调用。
- 事件处理函数
添加事件处理函数的语法:
event = " Javascript statements "
//在这个例子中使用onlick处理时间函数来给链接添加行为
onclick = "showPic(this);"
但是如果仅仅把事件处理函数放到链接中,还会出现一个问题,在点击链接时不光showPic函数被调用,链接被点击的默认行为也会被调用(带入另外一个图片页面)。我们可以了解一下事件处理函数的工作机制:在给某个元素添加了事件处理函数之后,一旦事件发生,相应的JS代码就会执行,被调用的JS代码可以返回一个值,这个值将传递给事件处理函数,因此在JS代码块中添加一句return false; 就可以使当链接被点击的时候,onclick事件处理函数认为这个链接没有被点击,就阻止了默认行为。
因此代码添加事件处理函数后的样子变成了
<li>
<a href="图片库/300.jpg" title="刘德华" onclick = "showPic(this); return false;">刘德华</a>
</li>
4.4 对这个函数进行扩展
接下来要介绍几个新的DOM属性
1.childNoded 属性
childNodes属性用来获取任何一个元素的所有子元素,是一个包含全部子元素的数组:
element.childNodes
获取到的元素并不仅仅只是元素节点,还有属性,文本节点等,甚至空格都会被解释为节点,这个时候可以通过nodeType属性来查看节点的类型。
2.nodeType 属性
获取节点nodeType属性的语法是:
node.nodeType
但是nodeType的值并不是由字符串表示,而是由数字表示。重点的有元素节点的nodeType属性值为1,属性节点的属性值为2,文本节点的属性值为3。
可以通过nodeType的属性值来只处理想要的节点。
3.nodeValue 属性
如果想要改变一个文本节点的值,可以使用DOM提供的nodeValue属性,它可以得到和设置一个节点的值。
node.nodeValue
但是如果对一个元素节点取nodeValue得到的是空值,包含在其中的文本是另外一种节点,是他的第一个子节点,因此要想获取文本节点应该使用 node.childNodes[0].nodeValue。
4.firstChild 和 lastChild 属性
分别代表了childNodes 的第一个元素和最后一个元素。
接下来对函数扩展,使用nodeValue属性来改变点击链接的时候<p>标签内部文本的值
function showPic(whichPic) {
var source = whichPic.getAttribute('href');
var placeholder = document.getElementById("placeholder");
placeholder.setAttribute("src", source);
var text = whichPic.getAttribute('title');
var description = document.getElementById('description');
description.childNodes[0].nodeValue = text;
}
5.最佳实践
这一章讲了几个编写JavaScript脚本时的重要思想,如平稳退化渐进增强,向后兼容等等。
5.1
没啥内容 就是讲了一下过去人们写出了一些不符合标准的代码,编写出糟糕的网页
5.2 平稳退化
正确使用了JavaScript脚本可以让访问者在他们浏览器不支持JavaScript的情况下仍能够顺利浏览到网站的内容,这就是平稳退化。
此处介绍了一个新建一个浏览器窗口的方法
window.open(url,name,features)
在各种浏览器页面中,内容才是最重要的,要确保用户能在禁用JS的时候仍然能够正确获取到文档中的内容,即使错过了一些功能。
5.3 向CSS学习
HTML构成结构,CSS构成样式,JavaScript描述行为。这一节主要讲的是编写JavaScript代码的时候要向CSS学习,把结构和样式相分离,这样有利于平稳退化。保证html文档的内容能按照正确的结构表现出来。
渐进增强
这个概念是用一些额外的信息层去包裹原始结构,首先保证标记好良好的内容,再去额外增加一些样式和行为,这样的做法就是渐进增强,一般来说按照渐进增强原则编写出的网页都符合平稳退化原则。
5.4 分离JavaScript
在之前的代码中,虽然已经把JavaScript函数存入了外部文件,但问题在于事件处理函数仍然内嵌在html文档中。把JavaScript代码调用行为和HTML文档的结构和样式分离开,会使得网页健壮很多。
JavaScript语言不要求事件必须在HTML文档里处理,因此可以在外部JavaScript文件中把一个事件添加到HTML文档中某个元素上。
element.event = action ...
拿图片库案例作为例子写一个函数
var links = document.getElementsByTagName('a')
for (var i =0 ; i<links.length ; i++){
link[i].onclick = function() {
showPic();
return false
}
}
接下来重点注意的是 如果把这段代码存入JavaScript外部文件中,并不能正常运行,因为第一个语句会在JavaScript文件被加载的时候立刻运行,但是如果JavaScript文件是从HTML文档中的<head>部分中的<script>标签调用,那么此时脚本加载时的文档可能不完整,DOM树不完整,则该段代码就无法正常工作。
这些代码必须在HTML文档加载到浏览器后马上开始执行。还好HTML文档全部加载完毕的时候会触发一个事件,当文档被加载到一个浏览器窗口时,window对象会触发onload事件,document对象已经存在。因此需要把代码进行修改:
window.onload = prepareLinks();
function prepareLinks() {
var links = document.getElementsByTagName('a')
for (var i = 0; i < links.length; i++) {
link[i].onclick = function() {
showPic();
return false
}
}
}
5.5 向后兼容
平稳退化是考虑禁用JS的访问者如何获取正确的内容,而向后兼容是考虑一些古老的浏览器对DOM提供的方法和属性的支持程度。因此即使支持JavaScript,某些脚本也不一定能正常工作。此时我们可以用到一个检测方法——对象检测
对象检测:把某个方法打包在一个if语句里面根据这个表达式的求值是true还是false来决定采取怎样的行动。通过这种方式可以把不支持某个特定DOM方法的浏览器检测出来
if (method) {
stataments
}
//eg:
function Myfunction(){
if(document.getElementById)
{
statements using getElementById
}
}
//把测试条件更换为如果不理解这种方式,请离开更为简单
if (!method) {
return false;
}
5.6 性能考虑
1.尽量少访问DOM和尽量减少标记
过多的标签和访问DOM数的次数都会影响到脚本性能。过多的标签则会扩大DOM树的规模,进而增加遍历DOM树查找特定元素的时间。
2.合并和放置脚本
包含脚本的最佳方式是使用外部文件,因为外部文件可以和标记清晰地分离,但一个HTML文档最好不要包括了多个外部文件如:
<script src='a.js'></script>
<script src='b.js'></script>
<script src='c.js'></script>
<script src='d.js'></script>
这个样子会增加加载页面时的请求数量,因此最好是把多个js文件合并到一个文件中,减少请求数量是性能优化首先要考虑的。
而放置js文件的最佳位置是在文档的末尾,</body>标签之前。因为处于<head>块中的脚本会导致浏览器无法并行加载其他文件,因为在下载脚本期间,浏览器不会下载其他任何文件,其他资源都要在脚本加载完毕后才能下载。
3.压缩脚本
使用一些工具把代码精简,虽然逻辑性和可读性差了一些,但是可以增快加载的速度。
这些工具比较有代表性的有 雅虎的YUI compressor , 谷歌的 Closure Compiler 等。
6 案例研究:图片库改进版
本章主要是把上一章节学到的一些思想和实践运用到图片库案例中
6.1 & 6.2 是否支持平稳退化
关于之前的代码能否支持平稳退化看禁用JavaScript后我们能否得到正确的内容呢,根据代码可以看出如果JavaScript没了,我们仍会弹出一个图片页面,得到我们点击链接的图片,即使他不会出现在当前页面的占位符图片中,但我们依然可以看到内容,因此算是通过了平稳退化。
6.3 JavaScript和HTML标记是分离的吗
在之前的图片库案例中很明显并不是分离的,事件处理函数在标签内部,因此需要把事件处理函数彻底移除在标签中,可以编写这么一段代码,在JavaScript中绑定元素和事件。
window.onload = prepareGallery();
function prepareGallery() {
if (!getElementById) {
return false; //检测浏览器能否理解该方法
}
if (!getElementsByTagName) {
return false; //检测浏览器能否理解该方法
}
if (!document.getElementById('imageGallery')) {
return false; //检测文档中是否有id为imagegallery的元素
}
var gallery = document.getElementById('imageGallery');
var links = gallery.getElementById('a'); //遍历链接
for (var i = 0; i < links.length; i++) {
links[i].onclick = function() {
showPic(this);
return false; //设置onclick事件
}
}
}
共享onload事件
在上面代码中的第一行便是让这个函数在网页加载完毕后立即执行,如果不加这一行那么有可能此时运行的DOM树不完整,那么代码的准确性也就无从谈起。
但是如果要绑定多个函数希望在页面加载完成后立即执行该怎么办?
window.onload = fisrtFunction();
window.onload = secondFunction(); //只有这个函数能够生效
如果希望在页面加载结束时执行多个函数,我们可以使用一些额外的代码,这个函数的名字是addLoadEvent,下面是这个函数的代码:
function addLoadEvent(func){
var oldonload = window.onload;
if (typeof window.onload != 'function'){
window.onload = func;
}
else {
oldonload();
func();
}
}
这个函数会完成的操作是
- 把现有的window.onload事件处理函数的值存入变量oldonload
- 如果这个处理函数上没有绑定任何函数,则像平时那样把新函数添加给他
- 如果在这个处理函数上已经绑定了一些函数,几把新函数追加到现有指令的末尾
6.8 DOM Core 和 HTML-DOM
在之前所学到的一些DOM方法都是属于DOM Core的组成部分,如getElementById,getElementByTagName等,这些方法并不专属于JS,任何支持DOM的程序设计语言都可以使用他们,但也可以使用HTML-DOM。
比如document.getElementByTagName('form')
等同于 document.forms
placeholder.setAttribute('src',source);
等同于
placeholder.src = source;
就算不使用HTML-DOM进行简化也应该了解一下,这样在阅读别人代码的时候可以知道意味着什么。