第一个问题是:如果JS功能被禁用,会怎样?因为我们的href没有等于#,因此图片库能够平稳退化。这个没有问题
第二个问题是:JS与HTML标记是分离的吗,很明显没有。下面是我们之前图片库的HTML代码:
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>Image Gallery</title> </head> <body> <h1>Snapshots</h1> <ul> <li> <a href="images/fireworks.jpg" title="A fireworks display" onclick="showPic(this); return false;">Fireworks</a> // return false是为了阻止链接的默认行为 </li> <li> <a href="images/coffee.jpg" title="A cup of black coffee" onclick="showPic(this); return false;">Coffee</a> </li> <li> <a href="images/rose.jpg" title="A red, red rose" onclick="showPic(this); return false;">Rose</a> </li> <li> <a href="images/bigben.jpg" title="The famous clock" onclick="showPic(this); return false;">Big Ben</a> </li> </ul> <img src="images/placeholder.gif" alt="my image gallery" id="placeholder"> <script src="scripts/showPic.js"> </script> </body> </html>
我们需要一个挂钩把JS和HTML的标记关联起来。有多种方法可以达到目的,如给每个li标签添加一个class,但这种技术不够理想。这与给它们分别添加事件处理函数同样麻烦。因为它们都包含在同一个列表清单元素里。给整个清单设置一个独一无二的办法要简单得多:
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>Image Gallery</title> </head> <body> <h1>Snapshots</h1> <ul id="imagegallery"> <!-- ID为挂钩 --> <li> <a href="images/fireworks.jpg" title="A fireworks display">Fireworks</a></li> <li> <a href="images/coffee.jpg" title="A cup of black coffee">Coffee</a> </li> <li> <a href="images/rose.jpg" title="A red, red rose">Rose</a> </li> <li> <a href="images/bigben.jpg" title="The famous clock">Big Ben</a> </li> </ul> <img src="images/placeholder.gif" alt="my image gallery" id="placeholder"> <script src="scripts/showPic.js"> </script> </body> </html>
添加事件处理函数将有关操作关联到onclick上。函数命名为prepareGallery:
function prepareGallery(){ // 最佳实践:对象检测 if(!document.getElementById) return false; if(!document.getElementsByTagName) return false; // 做为一条原则,如果想用JS给某个网页添加一些行为,就不应该让JS代码对这个网页结构有任何依赖 if(!document.getElementById("imagegallery")) return false; // 最佳实践:性能考虑 尽量减少访问DOM,因此保存在变量中 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; } } }
必须执行prepareGallery函数才能对onclick事件进行绑定,应该让这个函数在网页加载完毕之后立刻执行。可以用 window.onload 进行绑定,但如果你有多个函数呢?
window.onload=firstFunction;
window.onload=secondFunction;
如果你像上面那样操作,它们当中只有最后那个才会被实际执行。我们可以编写一个函数来解决这个问题:
function addLoadEvent(func){ // 把现有的window var oldonload = window.onload; if(typeof window.onload != ‘function‘){ window.onload = func; } else{ window.onload = function(){ oldonload(); func(); } } }
addLoadEvent将那些在页面加载完毕时执行的函数创建为一个队列。现在如果想把刚才那两个函数添加到这队列里去,只需这样写:
addLoadEvent(firstFunction)
addLoadEvent(secondFunction)
showPic函数将由prepareGallery函数调用,我们之前的showPic代码是这样的:
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.firstChild.nodeValue = text; }
我们之前没有对它进行任何测试和检查。因为 showPic 函数由prepareGallery调用,而后者的开头已经对 getElementById 和 getElementsByTagName 等DOM方法是否存在进行检查了,所以可以确切知道用户的浏览器不会因为不理解这两个方法而出问题
但我们还是做出了太多的假设。我们并没有对id属性值等于 placeholder 和 description 的元素是否存在进行检查。 showPic函数负责显示所点击链接的图片和相应文本,而文本知识锦上添花的操作,我们希望只要 placeholder 图片存在, 即使 description 元素不存在, 切换显示新图片的操作也将照常进行。 下面是修改后的 showPic 函数代码:
function showPic(whichpic){ if(!document.getElementById("placeholder")) return false; var source = whichpic.getAttribute("src"); 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; }
有一个问题,如果把 placeholder 图片从标记文档里删除,这时无论你点击 imagegallery 清单里的任何一个链接都没有反应, 这意味着我们的脚本不能平稳退化。
问题在于 prepareGallery函数做出了这样一个假设: showPic 函数肯定会正常返回。 基于这一假设 prepareGallery 函数取消了 onclick 事件的默认行为:
links[i].onclick = function(){ showPic(this); return false; // 这里 }
是否要返回一个 false 值以取消 onclick 事件的默认行为,其实应该由showPic的返回值来确定:如果图片切换成功,返回 true; 如果图片切换失败, 返回 false;因此我们改进 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 属性; 假设了 placeholder 是一张图片; 假设了description 元素的第一个子元素是一个文本节点。 下面是引入了以上几项检查之后的 showPic 函数的代码清单:
function showPic(whichpic){ if(!document.getElementById("placeholder")) return false; var source = whichpic.getAttribute("src"); 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"); // 节点值为3的为文本节点 if(description.firstChild.nodeType == 3){ description.firstChild.nodeValue = text; } } return true; }
在实际工作中,你要自己决定是否真的需要这些检查,它们针对的是 HTML 文档有可能不在你控制范围内的情况。 理想情况下,你的脚本不应该对 HTML 文档的内容和结构做大多的假设。你需要具体情况具体分析。
小结:本章我们引入了许多项测试和检查,使得我们的JS脚本代码能够平稳退化;最重要的是把事件处理函数从标记文档分离到一个外部的JS文件。
在图片库的标记文档部分还有一些内容还有一些我感到不太满意。比如说,placeholder 和 description 元素是单纯为了 showPic 函数而存在的,但不支持或禁用了JS功能的浏览器也会把它们呈现出来, 这就难免会给访问者带来不便甚至困扰。理想情况是,让这两个元素旨在遇到那些支持DOM的浏览器才出现在HTML文档里,在下一节里,你将会看到如果利用DOM提供的方法和属性去创建HTML元素,并把它们插入到HTML文档里。
原文:https://www.cnblogs.com/starboy13/p/13509211.html