javascript创建可维护幻灯片效果。
幻灯片效果
显然,效果很实用。对于这个效果,我们并不解释如何去使用效果库,而是讲解如何创建类似的效果,并保持他的可用性,分离式(unobtrusive),可维护性(让未来的维护者,在不需要修改你的脚本的情况下,修改图片,外观或文本标签)。
无 javascript 状态下,用户将看到下面的效果:
添加和移除图片、改变图片的顺序以及添加标题,这些在 html 中都很容易做到。并且最后的解决并不意味着维护者需要懂任何 javascript 或者在源码中搜索在哪里修改 css 的 class, id 或者文本标签。
你有没有准备好花费一些时间去一步一步的尝试创建这个效果?
第一步:分析问题(analizing the problem)
创建一个好的脚本,第一步应该是去分析哪些是你要完成的:我们想要创建一个照片的幻灯片效果,并且我们想要保持维护的方便。
如何创建一个幻灯片效果
在一个网站上拥有幻灯片有几种方法:
在我们的案例中,我们采取下面的图片列表,用向前和向后的按钮把他变成一个幻灯片效果,并且一个指示器告诉我们,照片总数中的哪张照片是当前显示的。
<ul id="slideshow">
<li><img src="img/flat1.jpg" alt="hallway" /></li>
<li><img src="img/flat2.jpg" alt="hob" /></li>
<li><img src="img/flat3.jpg" alt="bathroom" /></li>
<li><img src="img/flat4.jpg" alt="living room" /></li>
<li><img src="img/flat5.jpg" alt="bedroom" /></li>
</ul>
最后的输出会看起来像例子中的幻灯片效果。
依赖关系检查
我们这里有一些元素依赖于 javascript 生成:文字指示器和向前和向下的链接。为了保持我们解决方法的可用性,我们需要确保一些事情:
|||
第二步:规划脚本(planning the script)
一旦你已经评估了问题,并挑选出你想使用的解决方法,你便可以开始规划脚本。本质上,我们的脚本应该做这些:
不同的功能处理
我们有一些方法处理这个问题。其中之一是使用 dom 遍历每个 li 条目并隐藏他。在这个事件监听函数,我们先隐藏先前显示的 li (如果有的话),并显示当前的这个。
注:显示和隐藏代替图片的 li 更有意义,因为他允许维护者在每个幻灯片上添加其他的元素,比如,一些标题。
这个方法的问题在于,我们在 javascript 中做必要的样式改变,这意味着如果有需要比刚才我们脚本中改变 display 从 block 到 none 更复杂的样式改变,将使脚本变得更杂乱(没有从行为中分离表现)。
样式留给 css 解析器
更简洁的方法是将所有的外观改变(在所有列表项下载完之后隐藏某些)都留给浏览器的 css 解析器。在我们的例子中,我们可以在幻灯片中使用一个 css 规则很容易地隐藏所有的列表项,并用一个特定的 class 重写当前条目的样式。
html:
<ul id="slideshow">
<li><img src="img/flat1.jpg" alt="hallway" /></li>
<li><img src="img/flat2.jpg" alt="hob" /></li>
<li><img src="img/flat3.jpg" alt="bathroom" /></li>
<li><img src="img/flat4.jpg" alt="living room" /></li>
<li><img src="img/flat5.jpg" alt="bedroom" /></li>
</ul>
css:
#slideshow li{
display:none;
}
#slideshow li.current{
display:block;
}
唯一的问题是,如果我们使 css 和 javascript 不可用,访客将永远不能访问到其他图片。因此,我们需要仅当 javascript 可用时,应用这些样式。技巧是,当 javascript 可用,在幻灯片的 ul 上应用 class ,例如名为 js 。这允许我们仅当 javascript 可用时,显示效果,通过在 css 中简单的修改:
css:
#slideshow.js li{
display:none;
}
#slideshow.js li.current{
display:block;
}
这个 class 的钩子(hook)也能被用来对幻灯片的静态和动态版本提供一个完全不同的外观。
我们所有的脚本需要做的是,通过移除或添加 current 的 class 来显示和隐藏当前及以前的照片。
为了确保我们的脚本将不会影响同一页面上的其他脚本,我们将创建一个主要的对象,并在其上构造所有的方法和属性。这可以确保我们的 init() 函数将不会被覆盖或覆盖其他任何相同名字的函数。
javascript::
slideshow = {
current:0, // 当前幻灯片编码
init:function(){
// 初始化和设置事件处理函数
},
show:function(e){
// 事件监听器
}
}
|||
第三步、基本的工具方法( essential tools)
现在,我们有了规划和建立我们脚本的框架。是时候思考我们需要完成这个功能的一些工具方法。在其最低要求的情况下,dom 脚本的帮助库应该包括:
我们添加这些工具方法到主要的对象上,并准备开始:
javascript:
slideshow = {
current:0, // 当前幻灯片编码
init:function(){
// 初始化和设置事件处理函数
},
show:function(e){
// 事件监听器
},
addevent:function( obj, type, fn ) {
if ( obj.attachevent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){
obj['e'+type+fn]( window.event );
}
obj.attachevent(’on’+type, obj[type+fn] );
} else
obj.addeventlistener( type, fn, false );
},
removeclass:function(o,c){
var rep=o.classname.match(’ ‘+c)?’ ‘+c:c;
o.classname=o.classname.replace(rep,”);
},
addclass:function(o,c){
var test = new regexp(”(^|//s)”+c+”(//s|$)”).test(o.classname);
if(!test){o.classname+=o.classname?’ ‘+c:c;}
},
cancelclick:function(e){
if (window.event){
window.event.cancelbubble = true;
window.event.returnvalue = false;
}
if (e && e.stoppropagation && e.preventdefault){
e.stoppropagation();
e.preventdefault();
}
}
}
当文档完全载完,第一件事情就是需要执行 init() 方法:
javascript:
slideshow = {
current:0, // 当前幻灯片编码
init:function(){
// 初始化和设置事件处理函数
},
show:function(e){
// 事件监听器
},
addevent:function( obj, type, fn ) {
if ( obj.attachevent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){
obj['e'+type+fn]( window.event );
}
obj.attachevent(’on’+type, obj[type+fn] );
} else
obj.addeventlistener( type, fn, false );
},
removeclass:function(o,c){
var rep=o.classname.match(’ ‘+c)?’ ‘+c:c;
o.classname=o.classname.replace(rep,”);
},
addclass:function(o,c){
var test = new regexp(”(^|//s)”+c+”(//s|$)”).test(o.classname);
if(!test){o.classname+=o.classname?’ ‘+c:c;}
},
cancelclick:function(e){
if (window.event){
window.event.cancelbubble = true;
window.event.returnvalue = false;
}
if (e && e.stoppropagation && e.preventdefault){
e.stoppropagation();
e.preventdefault();
}
}
}
slideshow.addevent(window,’load’,slideshow.init);
|||
第四步:脚本(the script)
现在,在适当的位置我们有所有的方法工具,以及当窗口载完时被调用的 init() ,我们可以开始具体化此方法。
注:这仅是 init() 方法,而不是整个脚本。因为有行号,复制并粘贴脚本将会导致错误。
1: init:function(){
2: if(document.getelementbyid && document.createtextnode){
3: var list = document.getelementbyid(' ');
4: if(list){
5: slideshow.items = list.getelementsbytagname('li');
6: slideshow.all = slideshow.items.length;
7: if(slideshow.all > 1){
8: slideshow.addclass(list, 'js');
9: slideshow.createnav(list);
10: }
11: }
12: slideshow.show();
13: }
14: },
createnav() 方法使用 dom 脚本创建幻灯片正常工作所需的 html。
1: createnav:function(o){
2: var p = document.createelement('p');
3: slideshow.addclass(p, 'slidenav');
4: slideshow.prev = document.createelement('a');
5: slideshow.prev.setattribute('href', '#');
6: var templabel = document.createtextnode('<<');
7: slideshow.prev.appendchild(templabel);
8: slideshow.addevent(slideshow.prev, 'click', slideshow.show);
9: p.appendchild(slideshow.prev);
10: slideshow.count = document.createelement('span');
11: templabel = document.createtextnode( (slideshow.current+1) + ' / ' + slideshow.all);
12: slideshow.count.appendchild(templabel);
13: p.appendchild(slideshow.count);
14: slideshow.next = document.createelement('a');
15: slideshow.next.setattribute('href', '#');
16: var templabel = document.createtextnode('>>’);
17: slideshow.next.appendchild(templabel);
18: slideshow.addevent(slideshow.next, ‘click’, slideshow.show);
19: p.appendchild(slideshow.next);
20: o.parentnode.insertbefore(p, o);
21: },
这些被创建的所有标记都是必要的,最后剩下的是去定义一个当链接被点击时调用的监听方法 show() 。
1: show:function(e){
2: if(this === slideshow.next || this === slideshow.prev){
3: slideshow.removeclass(slideshow.items[slideshow.current], ‘current’);
4: var addto = (this === slideshow.next) ? 1 : -1;
5: slideshow.current = slideshow.current + addto;
6: if(slideshow.current < 0){
7: slideshow.current = (slideshow.all-1);
8: }
9: if(slideshow.current > slideshow.all-1){
10: slideshow.current = 0;
11: }
12: }
13: var templabel = document.createtextnode((slideshow.current+1) + ‘ / ‘ + slideshow.all);
14: slideshow.count.replacechild(templabel, slideshow.count.firstchild);
15: slideshow.addclass(slideshow.items[slideshow.current], ‘current’);
16: slideshow.cancelclick(e);
17: },
这些是脚本的所有内容。现在这个脚本可以工作,但仍不是真正可维护的。
|||
第五步:轻松维护(easing maintenance)
脚本功能齐全,分离式而且无懈可击。真正的问题是,现在并不方便维护。
脚本应用的最大的问题大概是,并不是所有的维护者都懂 javascript 和愿意在你的脚本中寻找需要修改的部分。
为了避免维护者做这些,最安全的方法就是把脚本和 css 中使用的命名和 id 从你的脚本功能中分离出来。此外,从使用的脚本中分离出文本标签也是个好点子,因为他们可能会改变。例如,当脚本使用其他语言本地化时。
工具方法的复用
第一件要做的事情就是,从主要脚本中分离出其他脚本也可以再用的工具函数。这也许是大部分 javascript 库的开始。
tools.js:
/* 辅助方法 */
tools = {
addevent:function( obj, type, fn ) {
if ( obj.attachevent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){
obj['e'+type+fn]( window.event );
}
obj.attachevent( ‘on’+type, obj[type+fn] );
} else
obj.addeventlistener( type, fn, false );
},
removeclass:function(o,c){
var rep=o.classname.match(’ ‘+c)?’ ‘+c:c;
o.classname=o.classname.replace(rep,”);
},
addclass:function(o,c){
var test = new regexp(”(^|//s)” + c + “(//s|$)”).test(o.classname);
if(!test){o.classname+=o.classname?’ ‘+c:c;}
},
cancelclick:function(e){
if (window.event){
window.event.cancelbubble = true;
window.event.returnvalue = false;
}
if (e && e.stoppropagation && e.preventdefault){
e.stoppropagation();
e.preventdefault();
}
}
}
css 的 class 和 id —— 外观
下一步要做的是,分离外观的 class 和 id 到一个单独的包含文件。保证他们在 slideshow 命名空间里是安全的,因为其他脚本不太可能用到他们。也不会妨碍写一个简短的说明注释。
slideshow-css.js:
slideshow.css = {
/*
这些都是幻灯片效果中使用到的 classe 和 id。
你可以在这里修改他们中的任何一个。
务必请使用引号包围名称,用逗号结尾(除了最后一个)。
*/
showid :'slideshow',
dynamicclass :'js',
slidenavigationclass :'slidenav',
currentclass :'current'
}
文本标签(text labels)—— 解释给终端用户
最后但不是最不重要的,让我们将文本标签放到一个单独的包含文件,再次使用 slideshow 命名空间。
slideshow-labels.js:
slideshow.labels = {
/*
这些都是幻灯片效果中使用到文本标签。
你可以在这里修改他们中的任何一个。
务必请使用引号包围名称。
最后一个结尾不用逗号。
*/
previous : '<<',
next : '>>’,
counterdivider : ‘ of ‘
}
改变的主要脚本
然后,我们需要修改主要脚本使用此信息,而不是依赖嵌入式的数据。没有太多的改变,很容易用搜索加替换就能做到。
slideshow.js:
slideshow = {
current:0,
init:function(){
if(document.getelementbyid && document.createtextnode){
var list =document.getelementbyid(slideshow.css.showid);
if(list){
slideshow.items = list.getelementsbytagname('li');
slideshow.all = slideshow.items.length;
if(slideshow.all > 1){
tools.addclass(list, slideshow.css.dynamicclass);
slideshow.createnav(list);
}
}
slideshow.show();
}
},
createnav:function(o){
var p = document.createelement('p');
tools.addclass(p, slideshow.css.slidenavigationclass);
slideshow.prev = document.createelement('a');
slideshow.prev.setattribute('href', '#');
var templabel = document.createtextnode(slideshow.labels.previous);
slideshow.prev.appendchild(templabel);
tools.addevent(slideshow.prev, 'click', slideshow.show);
p.appendchild(slideshow.prev);
slideshow.count = document.createelement('span');
templabel =document.createtextnode((slideshow.current+1) + slideshow.labels.counterdivider + slideshow.all);
slideshow.count.appendchild(templabel);
p.appendchild(slideshow.count);
slideshow.next = document.createelement('a');
slideshow.next.setattribute('href', '#');
var templabel = document.createtextnode(
slideshow.labels.next);
slideshow.next.appendchild(templabel);
tools.addevent(slideshow.next, 'click', slideshow.show);
p.appendchild(slideshow.next);
o.parentnode.insertbefore(p, o);
},
show:function(e){
if(this === slideshow.next || this === slideshow.prev){
tools.removeclass(slideshow.items[slideshow.current],
slideshow.css.currentclass);
var addto = this === slideshow.next ? 1 : -1;
slideshow.current = slideshow.current + addto;
if(slideshow.current < 0){
slideshow.current = (slideshow.all-1);
}
if(slideshow.current > slideshow.all-1){
slideshow.current = 0;
}
}
var templabel = document.createtextnode((slideshow.current+1) + slideshow.labels.counterdivider + slideshow.all);
slideshow.count.replacechild(templabel, slideshow.count.firstchild);
tools.addclass(slideshow.items[slideshow.current], slideshow.css.currentclass);
tools.cancelclick(e);
}
}
tools.addevent(window,’load’,slideshow.init);
这些所有文件是确保将来维护者不用麻烦你就可以使用你的脚本工作所需要的。文件名应该很明显,是什么就是什么,并能随着时间的推移,成为一个标准的脚本:
tools.js
slideshow.js
slideshow-labels.js
slideshow-css.js
slideshow.css
原作者:christian heilmann 译者:怿飞
原文:《a detailed explanation how to create a maintainable dynamic slide show in javascript》
新闻热点
疑难解答