《DOM编程艺术》四、图片库改进


title: 《DOM编程艺术》四、图片库改进
date: 2017-06-03 10:17:04
tags: DOM编程艺术


在这一章主要是用上一章的思想改进图片库,所以我会把之前的javascript图片库案例拿来看看是不是符合上一章所说的规则。

1、支持平稳退化吗?

可以确定,图片库如果禁用了javascript功能,也已经留了退路,网页里的所有链接都是可用的,虽然用户体验游影响,但网页的功能并未受到损害,而如果当时在href中写的不是链接而是伪协议或是'#',那么禁用javascript后它会无法使用。

2、javascript与html分离吗?

在图片库中它们确实是混在了一起,onclick直接插入到了html中,理想情况下,应该在外部文件里添加onclick事件处理函数。

我需要编写一个简短的函数把有关操作关联到onclick事件上,我想让函数完成以下工作:

  1. 检查浏览器是否理解getElementsTagByName。

  2. 检查浏览器是否理解getElementById。

  3. 检查网页是否存在一个id为imagegallery的元素。

  4. 遍历imagegalery元素中的所有链接。

  5. 设置onclick事件,让它在有关链接被点击时完成以下操作。

    • 把这个链接作为参数传递给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(){
            showPic(this);
            return false;
        }
    }
}

为了方便获取元素,我将<ul>的id设为'imagegallery'。

函数的第一部分是检查点,如果不支持或没有检查的内容就离开,其实也可以写为if(!document.getElementsByTagName || !document.getElementById) return false;将两个检查写到一起,但是这样写代码太过冗长不利于阅读,所以将代码写在一行并不一定是个好主意。

代码中我用变量名存储获取到的元素,避免了代码写得太长,在给变量起名时,应该选择一些有意义的单词来命名,可以让代码更容易阅读和理解,并且一定要避免使用保留字和函数或方法名。

函数的最后是一个遍历,links存储了列表中的所有a元素,并且是一个伪数组,所以可以遍历得到每个a元素并且将onclick事件绑定给该元素,这样绑定之后,该事件处理函数的this就是绑定该事件的元素,至此可以从html中彻底删除onclick事件了。

3、共享onload事件

现在我还需要一个功能,就是要在DOM树加载完之后再执行上面的prepareGallery函数,如果只有一个函数用window.onload = prepareGallery即可,但如果有多个函数,就不能直接这样写,后面的会覆盖前面的,所以我还要再写一个函数完成这个绑定功能。

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

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

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

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

这个函数将把那些在页面加载完毕时执行的函数创建一个队列,参数func就是要执行的函数。

4、不要做太多的假设

下面我要改造一下showPic函数,因为我发现showPic函数并没有任何检查和测试,所以需要一些语句来检查这些元素是否存在。

showPic函数负责完成两件事:

  1. 找出id属性是placeholder的图片并修改其src属性。这个是核心功能,必须完成的任务。

  2. 找出id属性是description的元素并修改其第一个子元素的nodeValue属性。这个功能只是锦上添花,所以只要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;
}

现在这个函数完成了检测,只要图片可以正常获取就返回true,但是还有一个问题,就是如果把placeholder图片从标记文档里删掉,无论点击imagegallery清单里的哪一个链接,都没有任何反应,这不符合平稳退化,此时应该让浏览器打开那个被点击的链接,而不是什么都不发生。

导致这个问题的原因是prepareGallery函数中是假设showPic肯定会切换图片成功,基于这一点才取消了a元素onclick事件的默认行为links[i].onclick = function(){showPic(this);return false;},但是现在在showPic中,如果图片切换成功才返回true,如果图片切换失败会返回false。所以如果showPic返回了false就不应该取消点击事件,而是该让a元素的链接正常打开,让切换图片的操作照常进行。所以是否取消默认行为应该由showPic函数决定。

为了达到这个目的,应该先验证showPic的返回值,以便决定是否阻止默认行为,如果showPic返回true,那么更新placeholder,取消默认事件。如果showPic返回false,那么就不取消默认事件。在onclick事件处理函数中,我们可以利用逻辑非来对showPic的返回值进行取反。

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

现在这个函数已经相当完善,虽然它们的长度有所增加,但它们对标记的依赖和假设已经比原来少多了,尽管如此,在showPic函数里仍存在一些需要处理的假设。

  1. 如果a元素的title属性存在,变量text将被赋值a元素的title属性,如果不存在,变量text将被赋值为一个空字符串。

  2. 假设placeholder元素是否是一张图片,可以用nodeName属性来测试,要注意的是,nodeName属性总是返回的值总是大写字母。

  3. 假设description元素的第一个子元素是一个文本节点,可以用nodeType属性来检测,如果nodeType的值是3,就是一个文本节点。

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');
        if(description.firstChild.nodeType == 3){
            description.firstChild.nodeValue = text;
        }
    }
    return true;
}

showPic的代码变得更多了,在实际中,你需要自己决定是否真的需要这些检查,它们针对的是html文档有可能不再你的控制范围内的情况,但理想情况下,脚本不应该对html文档的内容和结构做太多假设。

5、键盘访问事件

前面的代码只能用鼠标操作,而浏览器web页面也是可以用键盘进行操作的,有个名叫onkeypress的事件处理函数就是专门用来处理键盘事件的,按下键盘的任何一个按键都会触发onkeypress事件。

如果想把onkeypress事件加到上面的案例并完成和onclick相同的操作,只需要这样写links[i].onkeypress = links[i].onclick;,但是在这个案例中我认为并不需要键盘访问事件,有onclick事件足矣,我想说的是,在这里完全体现出了javascript和html分离带来的方便,如果像是最开始那样把javascript写在html中,将不得不去修改每行html语句,而现在只要在javascript代码中加一条语句即可。

6、DOM Core和HTML-DOM

至此,我在编写javascript代码时只用到了以下几个DOM方法:getElementById、getElementsByTagName、getAttribute、setAttribute。

这些方法都是DOM Core的组成部分,它们并不是专属javascript语言,支持DOM的任何一种程序设计语言都可以使用它们,它们的用途也并非仅限于处理网页,它们可以用来处理任何一种标记语言(比如XML)编写出来的文档。
像onclick属于HTML-DOM,它们在DOM Core出现之前很久就已经为人们所熟知,比如HTML-DOM提供了一个forms对象,写法是document.forms可以代替document.getElementsByTagName(forms)。还有element.src可以代替element.getAttribute

它们的区别是HTML-DOM只能处理web文档,我使用DOM Core也是因为其兼容多种类型的文档,其实这个完全是根据使用场景来选择,并没有绝对的对错之分。

7、增加css

body {
    font-family: "Helvetica","Arial",serif;
    color: #333;
    background-color: #ccc;
    margin: 1em 10%;
}

h1 {
    color: #333;
    background-color: transparent;
}

a {
    color: #c60;
    background-color: transparent;
    font-weight: bold;
    text-decoration: none;
}

ul {
    padding: 0;
}

li {
    float: left;
    padding: 1em;
    list-style: none;
}
img{
    display: block;
}

#imagegallery {
    list-style: none;
    overflow: hidden;
}

#imagegallery li {
    display: inline;
}

#imagegallery li a img {
    border: 0;
}

8、把图片的文字链接改为缩略图

<!DOCTYPE html>
<html lang = "en">
<head>
    <meta charset = "utf-8"/>
    <title>Image Gallery</title>
    <link rel="stylesheet" type="" href="./css/style.css">
</head>
<body>
    <h1>Snapshots</h1>
    <ul id="imagegallery">
        <li>
            <a href = "images/fireworks.jpg" title = "A fireworks display">
                ![](images/thumbnail_fireworks.jpg)
            </a>
        </li>
        <li>
            <a href = "images/coffee.jpg" title = "A cup of black offee">
                ![](images/thumbnail_coffee.jpg)
            </a>
        </li>
        <li>
            <a href = "images/rose.jpg" title = "A red,red rose">
                ![](images/thumbnail_rose.jpg)
            </a>
        </li>
        <li>
            <a href = "images/bigben.jpg" title = "The famous clock">
                ![](images/thumbnail_bigben.jpg)
            </a>
        </li>        
    </ul>
    <img id = "placeholder" src = "images/placeholder.gif" alt = "my image galley"/>
    <p id="description">Choose an image</p>

    <script src="script/script.js"></script>
</body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容

  • 前言 归根结底,代码都是思想和概念的体现。没人能把一种程序设计语言的所有语法和关键字都记住,可以查阅参考书来解决。...
    朱细细阅读 2,918评论 4 14
  • 勤于思考是每位有创新精神的网页设计人员都应该具备的特质。 平稳退化 第一个问题,如果JS功能被禁用,会怎么样?把h...
    fumier阅读 270评论 0 0
  • JavaScript 程序采用了异步事件驱动编程模型。在这种程序设计风格下,当文档、浏览器、元素或与之相关的对象发...
    劼哥stone阅读 1,252评论 3 11
  • Dom是一种使用于多种环境和多种程序设计语言的通用性API。如果想Dom技巧运用在Web服务器以外的应用环境里,严...
    zchuhyy阅读 317评论 0 0
  • 01 碗里满满的牛杂 下班了,陈宝宝拖着我走进人满为患的地铁。在拥挤的人群里,聊着日常,他在背后抱着我,我在指指画...
    一位喵先生阅读 11,110评论 240 380