notebooks
  • notebooks
  • _planning
    • 2022 OKR
    • basketball
    • swimming
  • communication
    • Dubbo
    • Kafka
    • Messaging
    • RPC
    • Thrift
  • computation
    • map-reduce
  • cs-basic-knowledge
    • computer-architecture
    • data-structure-and-algorithms
    • networks
    • os
  • devops
    • Docker
    • Linux
    • Prometheus
    • operations
    • security
    • trouble-shooting
  • distributed-knowledge
    • Zookeeper_CMD
    • distributed-system
  • game-engine
    • Unity
  • others
    • appium使用
  • protocols
    • http(s)协议
    • 官方链接
    • sip
  • storage
    • Elasticsearch
    • GuavaCache
    • MySQL
    • MySQL_CMD
    • NoSQL
    • Redis
    • Redis_CMD
  • system-design
    • system-design
  • tools
    • Git
    • IDEA
    • Mac
    • VScode
    • Vim
  • _working
    • doc-template
      • backend-design-review
      • correction-of-error
      • service-review
    • process
      • domain-backup
      • oncall
  • blogs
    • history
      • 8088/8086微处理器
      • 8088/8086指令系统
      • CSS-DOM
      • CSS定位
      • CSS工作原理
      • CSS控制背景
      • CSS浮动布局
      • CSS盒模型
      • Chrome开发者工具使用方法
      • DOM
      • Django Model模型层学习
      • Django-REST-framework Serializers学习
      • Django-REST-framework Views和ViewSets学习
      • Django View视图层学习
      • Gvim下Emmet安装及使用教程
      • HTTP协议简介
      • HashMap原理初探
      • JavaScript简史
      • JavaScript语法
      • Java内存模型和GC机制
      • Java基础——Lambda学习
      • Java基础——方法引用
      • Java基础——枚举类型
      • Java类加载机制
      • KMP算法
      • Kafka学习
      • Linux下用命令行编译Java程序
      • MathJax简介和基本用法
      • Python实现常见数据结构
      • Python装饰器总结
      • TCP协议的三次握手和四次挥手
      • Thrift学习
      • asyncio学习
      • markdown的常用语法
      • 修改hosts文件实现翻墙
      • 充实文档的内容
      • 关系数据库
      • 关系数据库标准语言SQL(一)
      • 关系数据库标准语言SQL(二)
      • 关系数据理论
      • 关系查询处理和查询优化
      • 内联元素和块级元素
      • 剑指offer算法题练习
      • 动态创建标记
      • 图形化用户界面
      • 在Eclipse中使用Maven构建Java Web项目
      • 增加微博秀遇到的一些问题
      • 处理机调度
      • 如何用github和hexo搭建个人博客
      • 存储管理
      • 存储系统的层次结构
      • 学习模仿lionhit网站首页的过程总结
      • 实用的GitHub小技巧
      • 并发控制
      • 循环与分支程序设计
      • 指令系统的设计
      • 指令级并行及其开发——硬件方法
      • 搭建自己的VPN服务器
      • 操作系统用户界面
      • 数据库安全性
      • 数据库完整性
      • 数据库恢复技术
      • 数据库绪论
      • 数据库编程
      • 数据库设计
      • 数据抽象
      • 文件系统
      • 文法和语言
      • 最佳实践
      • 案例研究:JavaScript图片库
      • 案例研究:图片库改进版
      • 汇编语言程序格式
      • 汇编语言程序设计基础知识
      • 流水线技术
      • 深度优先搜索和广度优先搜索
      • 牛客网——网易2017秋招编程题集合
      • 用JavaScript实现动画效果
      • 第一篇博客
      • 经典排序算法总结(Java实现)
      • 经典查找算法总结(Java实现)
      • 综合示例
      • 编译原理引论
      • 背包、队列和栈
      • 虚拟机安装Linux系统及常用软件
      • 计算机操作系统绪论
      • 计算机系统结构的基础知识
      • 设备管理
      • 设计模式之代理模式
      • 设计模式之单例模式
      • 设计模式之工厂模式
      • 设计模式之策略模式
      • 设计模式之观察者模式
      • 词法分析
      • 进程管理
      • 闭包
      • 阻止Google自动跳转到香港服务器的方法
      • 项目部署过程
  • programming-language
    • C#
      • C#
    • C&C++
      • C
    • C&C++
      • C++
    • Java
      • GoogleGuice
    • Java
      • JVM
    • Java
      • Java
    • Java
      • Maven
    • Java
      • Mybatis
    • Java
      • Spring知识
    • Java
      • SpringBoot
    • Java
      • Tomcat
    • Python
      • Python
    • Shell
      • Shell
  • wheels
    • dcc
      • 产品调研
      • 方案设计
    • red-envelope
      • 方案设计
    • short-url
      • 短链接服务
    • sso
      • 方案设计
Powered by GitBook
On this page
  • 快速回顾
  • 它支持平稳退化吗
  • 它的JavaScript与HTML标记是分离的吗
  • 不要做太多假设
  • 优化
  • 把JavaScript与CSS结合起来
  • DOM Core和HTML-DOM
  • 效果预览
  1. blogs
  2. history

案例研究:图片库改进版

Previous案例研究:JavaScript图片库Next汇编语言程序格式

Last updated 3 years ago

在学到如何平稳退化、分离JavaScript、向后兼容和提高性能,在这里,将运用这些知识来改进图片库。

快速回顾

在里,我编写了一个用来替换“占位符”图片的src属性的脚本,只用一个网页就建立起了图片库。

它支持平稳退化吗

第一个问题是:“如果JavaScript功能被禁用,会怎样?” 分析可知:即使JavaScript功能被禁用,用户也可以浏览图片库里的所有内容,网页里的所有连接也都可以正常工作:

<li>
  <a href="images/fireworks.jpg" onclick="showPic(this);return false;" title="a fireworks display">fireworks</a>
</li>

在JavaScript功能被禁用的情况下,浏览器将沿着href属性给出的链接前进,用户将看到一张图片而不是“该页无法显示”之类的错误信息。

它的JavaScript与HTML标记是分离的吗

由代码知显然不是分离的。 把JavaScript代码移出HTML文档不是难事,但为了让浏览器知道页面里都有哪些链接有着不一样的行为,我们必须找到一种“挂钩”把JavaScript代码与HTML文档中的有关标记关联起来。有多种办法可以做到这点: 可以给每个链接分别添加一个class属性:

<li>
  <a href="images/fireworks.jpg" class="gallerypic" title="a fireworks display">fireworks</a>
</li>

但这种方法不够理想,这与给它们分别添加事件处理函数同样麻烦。 第二种办法:图片清单里的每个链接有一个共同点:它们都包含在同一个列表清单元素里。给整个清单设置一个独一无二的id的办法要简单得多:

<ul id="imagegallery">
  <li>...</li>
</ul>

添加事件处理函数

现在,需要编写一个简短的函数把有关操作关联到onclick事件上。我将其命名为prepareGallery。 下面是这个函数需要完成的工作:

  • 检查当前浏览器是否理解getElementsByTagName和getElementById;

  • 检查当前网页是否存在一个id为imagegallery的元素;(这项测试是一个预防性措施。现在我知道调用这个JavaScript函数的文档里有一个id属性值等于imagegallery的列表清单元素,但我不确定这在将来会不会发生变化。有了这个预防性措施,即使以后从网页上删掉图片库,也用不着这个网页的JavaScript代码会突然出错。作为一条原则,如果想用JavaScript给某个网页添加一些行为,就不应该让JavaScript代码对这个网页的结构有任何依赖。)

  • 遍历imagegallery元素中的所有连接;

  • 设置onclick事件,让它在有关链接被点击时完成以下操作: 1.把这个链接作为参数传递给showPic函数; 2.取消链接被点击时的默认行为,不让浏览器打开这个链接。

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].onclick=function(){       //定义了一个匿名函数。这是一种在代码执行时创建函数的办法。
					showPic(this);
					return false;
				}
		}
}

调用此函数,就会把onclick事件绑定到“id”等于“imagegallery”的元素内的各个链接元素上。

共享onload事件

必须执行prepareGallery函数才能对onclick事件进行绑定。 如果马上执行这个函数,它就无法完成其工作。在HTML文档完成加载之前执行脚本,此时DOM是不完整的。 应该让这个函数在网页加载完毕后立刻执行。网页加载完毕时会触发一个onload事件,这个事件与window对象相关联。所以我们须把prepareGallery函数绑定到这个事件上:

window.onload=prepareGallery;

但如果有多个函数,如果像下面这样逐一绑定,它们中只有最后那个函数才会得到实际执行:

window.onload=firstFunction;
window.onload=secondFunction;

有种办法可以解决这个问题:可以先创建一个匿名函数来容纳这两个函数,然后把那个匿名函数绑定到onload事件上:

window.onload=function(){
		firstFunction();
		secondFunction();
	}

这里还有一个弹性最佳的解决方案——使用addloadEvent函数。它只有一个参数:打算在页面加载完毕时执行的函数的名字。 下面是addloadEvent函数将要完成的操作:

  • 把现有的window.onload事件处理函数的值存入变量oldonload。

  • 如果在这个处理函数上还没有绑定任何函数,就像平时那样把新函数添加给它。

  • 如果在这个处理函数上已经绑定了一些函数,就把新函数追加到现有指令的末尾。

下面是addloadEvent函数的代码清单:

function addloadEvent(func){
		var oldonload=window.onload;
		if(typeof window.onload!='function'){
				window.onload=func;
			}else{
					window.onload=function(){
							oldonload();
							func();
						}
				}
	}

这将把那些在页面加载完毕时执行的函数创建为一个队列。如果想把刚才那两个函数添加到这个队列里去,只需要写出以下代码就行了:

addloadEvent(firstFunction);
addloadEvent(secondFunction);

不要做太多假设

在showPic函数里发现的一个问题是,没有让它进行任何测试和检查。 showPic函数将由prepareGallery函数调用,而我已经在后者的开头对getElementById和getElementsByTagName等DOM方法是否存在进行过检查,所以我确切地知道用户的浏览器不会因为不理解这个方法而出问题。不过并未对id值等于placeholder和description的元素进行任何检查。

function showPic(whichpic){
	if(!document.getElementById("placeholder")) return false;
	var source = whichpic.getAttribute("href");
	var placeholder = document.getElementById("placeholder");
	placeholder.setAttribute("src",source);

	if(document.getElementById("description")){	
	var text=whichpic.getAttribute("title");
	var description=document.getElementById("description");
	description.firstChild.nodeValue=text;
	}
	return true;                 //即使description元素不存在,切换显示新图片的操作也照常进行
}

改进后的showPic()函数不再假设有关标记文档里肯定存在着placeholder图片和description元素。即使文档里没有placeholder图片,也不会发生任何JavaScript错误。 可是还有一个问题:如果把placeholder图片从标记文档里删除并在浏览器里刷新这页面,就会出现这种情况,无论点击imagegallery清单里的哪个链接,都没有任何反应。这意味着我们的脚本不能平稳退化。问题在于prepareGallery函数做出了这样一个假设:showPic函数肯定会正常返回。基于这一假设,prepareGallery函数取消了onclick事件的默认行为。

showPic函数应该返回两个可能的值:

  • 如果图片切换成功,返回true。

  • 如果图片切换不成功,返回false。

为修正这个问题,应该在返回前验证showPic的返回值,以便决定是否阻止默认行为。showPic返回true,那么更新placeholder。在onclick事件处理函数中,我们可以利用“!”对showPic的返回值进行取反:

links[i].onclick=function(){
		return !showPic(this);
	}

现在,如果showPic返回true,我们就返回false,浏览器不会打开那个链接。如果showPic返回false,那么我们认为图片没有更新,于是返回true以允许默认行为发生。下面是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].onclick=function(){       
					return !showPic(this);
				}
		}
}

优化

这个函数已经很完善了。尽管如此,在showPic函数里仍存在一些需要处理的假设。 例如,假设每个链接都有一个title属性:

var text=whichpic.getAttribute("title");

检查方法:

if(whichpic.getAttribute("title")!=null)

if(whichpic.getAttribute("title"))

在title属性不存在时把text的值设置为空字符串:

if(whichpic.getAttribute("title")){
		var text=whichpic.getAttribute("title");
	}else{
			var text="";
		}


var text=whichpic.getAttribute("title") ? whichpic.getAttribute("title") : "";    //另一种方法

其他检查:

if(placeholder.nodeName!="IMG") return false;  //检查placeholder。nodeName返回一个大写字母的值。

if(description.firstChild.nodeType==3){
		description.firstChild.nodeValue=text;
	}

最终prepareGallery()和showPic()代码清单:

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].onclick=function(){
					return showPic(this)?false:true;
				}
		}
}
function showPic(whichpic){
	if(!document.getElementById("placeholder")) return false;
	var source = whichpic.getAttribute("href");
	var placeholder = document.getElementById("placeholder");
	if(placeholder.nodeName!="IMG") return false;
	placeholder.setAttribute("src",source);

	if(document.getElementById("description")){	
		var text=whichpic.getAttribute("title")?whichpic.getAttribute("title"):""; 
		var description=document.getElementById("description");
		description.firstChild.nodeValue=text;
	}
	return true;               
}

把JavaScript与CSS结合起来

把JavaScript代码从HTML文档里分离出去还带来另一个好处。在把内嵌型事件处理函数移出标记文档时,我在文档里为JavaScript代码留下了一个“挂钩”:

<ul id="imagegallery">

这个挂钩完全可以用在CSS样式表里。

还可以把图片链接换成缩略图:

<li>
  <a href="images/fireworks.jpg" title="a fireworks display">
    <img src="images/thumbnail_fireworks.jpg" alt="fireworks" />
  </a>
</li>

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对象。这个对象可以把下面这样的语句:

document.getElementsByTagName("form")

简化为:

document.forms

类似地,HTML-DOM还提供了许多描述各种HTML元素的属性。比如说,HTML-DOM图片提供的src属性可以把下面这样的语句:

element.getAttribute("src")

简化为:

element.src

这些方法和属性可以相互替换。同样的操作既可以只使用DOM Core来实现,也可以使用HTML-DOM来实现。通常HTML-DOM代码更短,必须提醒一点的是,它们只能用来处理web文档。

效果预览

Snapshots
最佳实践
案例研究:JavaScript图片库