标签:javascript easystruct html 结构体
在前端开发的工作中,经常会碰到这样的情况,加载页面数据的时候,有一部分内容的结构是重复的,只是数据不一样。比如说论坛、贴吧里面的各个楼层,还有一些类似工资报表、销售报表的每一行,举个例子:
function addTr(data1,data2,data3){
return ‘<tr style="text-align:center"><td style="height:40px;">‘+data1+‘<td><td style="height:40px;">‘+data2+‘<td><td style="height:40px;">‘+data3+‘<td></tr>‘;
}
类似这样的结构体,在前端开发中很常见。我们通常会创建一个这样的东西之后,放到一个for或者each类型的循环中用于加载数据。但是,这种结构的写法看起来多少有些不便。而且要改动也不是很方便。所以,为了工作上的便捷,我开发了一个创建结构的纯js插件。用户可以轻松创建结构体了。该插件经过压缩后只有4kb的大小,绝对不会占用你的网页空间!而且不依赖于jquery或其他框架,可以独立使用。
好了,下面说一下这款插件如何使用,非常简单,举个例子,只需要写:
EasyStruct("#id").struct("table")(); 就可以在指定的id下方创建一个table了。我在EasyStruct中实现了一个简单的选择器,可以用#id选择id,用.class获取class的第一个,用tag获取tag的第一个。由于不希望该选择器太过复杂占用js插件空间,所以只实现了以上三个选择,不过我相信已经完全够用了。毕竟这个插件的作用仅仅是用来创建结构而已。
肯定会有人觉得,用EasyStruct这一串字符来写非常麻烦,嗯,没错,我也觉得很麻烦,所以我写了一句:var es = EasyStruct;
此时上面的那一句就等价于 es("#id").struct("table")();
如果用户不喜欢es的名字呢,很简单,只需要这样: var 你设定的名字 = EasyStruct; 或者 var 你设定的名字 = es; 这样就可以随便你使用了。
细心的朋友可能会注意到,我刚刚写的es("#id").struct("table")(); 后面还有一个(),嗯,没错,这个绝对不是我写错了或者写多了,而是本来如此。为什么呢,因为前面的es("#id").struct("table")返回的是一个function,而不是直接执行。不采用直接执行是为了我们可以填入参数嘛。
下面再看第二个例子:
es("#id").struct("table|style[background-color]|")("red");
这一次,在“table”的字样后面,多出了"|style[background-color]|"的字样,而后面的花括号里填入了“red”,很简单,就是我们在这个结构中定义了一个设置这个table的background-color的接口,而后面的“red”就是用户自己填入的值,这样,我们就可以将table的背景色定义为红色。
当然,上面的写法也可以写成:
var table = es("#id").struct("table|style[background-color]|");
table("red");
这样看起来是不是就清晰很多了呢?没错,EasyStruct的作用就是用来创建这样的结构的,只要我们将结构赋值给一个变量,就可以在任何地方随意地调用该结构了,放在for循环里面自然就不是什么问题了。当然了,很多时候,我们需要填入的可不止一个变量是不是?看下面:
var table = es("#id").struct("table|style[background-color color]|");
table("red","yellow");
很简单,直接写进去就可以了!这样的话呢,我们就把字体颜色给设置成了黄色了!
var table = es("#id").struct("table|style[color background-color]|");
table("red","yellow");
当然,如果上面的background-color和color的位置倒换过来的话,就会变成红色字体,黄色背景了。变量和出现的顺序是一一对应的。如果你填入的变量过多,比如在后面多了一个“blue”,是没有任何效果的。如果少了,比如说没有“yellow”,就相当于没有添加背景色而已,也不会出现什么bug的。我的设定是在两个竖杠中间可以填入一定的值来表示需要传入的参数内容。这些值需要用空格隔开。
可以传入的值包括:“id” “class” “html” “style”
“id”和“class”没什么好说的,“html”就是文本内容,“style”就是平时整个style的内容。举个例子:
var div = es("#id").struct("div|id class html style|");
div("aaa","bbb","哈哈","display:block;color:red;width:100px;height:50px");
等价于jquery的写法:
function div(id,class,html,style){
$("#id").append(‘<div id=“‘+id+‘” class="‘+class+‘" style="‘+style+‘">‘+html+‘</div>‘);
};
div("aaa","bbb","哈哈","display:block;color:red;width:100px;height:50px");
另外,如果有单个或多个的attr或者style的话,也可以以花括号的形式填充进去,举个例子:
var div = es("#id").struct("div|style=[background-color color] attr[onclick onmouseover]|");
div("red","yellow","alert(1)","this.color=\‘green\‘");
相当于创建了一个这样的东西:<div style="background-color:red; color:yellow" onclick="alert(1)" onmouseover="this.color=\‘green\‘"></div>
相信上面的例子已经说得很清楚了吧。接下来可能有人就会说,我需要填写的内容并不是全部啊,有些东西是固定的,比如table的id,我就希望它是固定的,而不需要自己填进去。这个当然是可以的。举例:
var table = es("#id").struct("table(#aaa .bbb $background-color:red;color:yellow; @onclick=alert(1) @onmouseover=alert(2))");
table();
此时相当于:<table id="aaa" class="bbb" style="background-color:red; color:yellow" onclick="alert(1)" onmouseover="alert(2)"></table>
语法方面很简单,将需要直接填入而不是以参数形式赋值的内容放在一个小括号里面。如果是id的话,就写#id,class就写.class,style就直接在前面加一个$,attr就加一个@,中间加一个=符号,这样就可以将这些内容直接写进去了。小括号的内容和竖杠的内容是可以同时存在的,并且他们谁先睡后都可以,不影响执行。
比如: var div = es.struct("div($color:red;)|id class attr[onclick]|");
div("aaa","bbb","alert(1)");
相当于:<div style="color:red" id="aaa" class="bbb" onclick="alert(1)"></div>
这样一来,无论是写成固定的,还是参数形式的,都没有任何问题了,是不是挺方便的呢?
当然,解决了上面的问题之后,还是没办法做实际应用的,因为我们平时所写的结构体不可能只有一个dom元素,举个最最简单的例子,我们用上面的方法创建了一个tr结构,然后放到一个for循环中执行了10次,这样确实可以在一个table里面插入10个tr,但是这10个tr里面可是连一个td都没有的。完全不能用呢。别急,现在我们就来解决这个问题:
对应EasyStruct来说,每一次创建结构可以传入的参数是无限制个数的,而且后面的参数会变成前面参数的子参数,举例:
es("#id").struct("div","span");
这样的写法相当于在指定的id下方创建了一个div,然后又在div下方创建了一个span。至于想对这个span和div进行各种属性处理的,这里就不赘述了,和上面的写法是一样的。如果是用竖杠直接填入参数的话,那么执行的顺序也是从左到右,如果div里面有竖杠,就先执行,再判断span里面有没有竖杠,再执行,以此类推。
但是这里就有一个问题了,比如说,我想在这个div下面放两个span,如果我这样写呢:
es("#id").struct("div","span","span");
很明显是错的,这样写就相当于在这个div里面放span,在这个span里面又放了一个span,是层级关系,而不是同级关系。
于是我想了一个解决方法,可以改成这样:
es("#id").struct("div",["span","span"]);
现在两个span被放在同一个数组之中,而这个数组作为第二个参数,那么他们就会并列地成为上一个参数div的子参数了。
听起来好像已经解决问题了,是吧?其实没有的,因为就实际工作而言,我们需要的结构体有时候复杂得很,不可能像我举的例子这么简单。我举的例子只是为了方便读者理解而已。
那么我们再看看另一种情况,我先以html的形式写出来:
<table>
<tr>
<td></td><td></td><td></td>
</tr>
<tr>
<td></td><td></td><td></td>
</tr>
</table>
es("#id").struct("table",["tr","tr"], ["td","td","td","td","td","td"]);
如果我们以上面的写法来写的话,出现的结构就是第一个tr下面有六个td,但是第二个tr下面什么都没有!如果我们写第四个参数的话,它是变成第一个tr下面的第一个td的子参数,那完全不是我们想要的结果……
于是肯定有人会说,那我们是不是可以把结构改成这样:
es("#id").struct("table",[ "tr" , ["td","td","td"] , "tr", ["td","td","td"] ]);
理论上讲,要通过函数来实现上面的嵌套式设计虽然有一定难度,但并不是什么问题。但是这样的结构读起来并不直观,也不好理解。所以笔者最后放弃了这样的设计模式,而是改成了下面的形式:
es("#id").struct("table",["tr","tr"],["(0)td","(0)td","(0)td","(1)td","(1)td","(1)td"]);
采用这样的写法看起来就变得很直观了,前三个td的前面多了一个0,也就是指定它们会放在上一层的第一个对象的下方。(由于index通常都是以0代表第一个的,所以笔者在这里也遵循了数组的常用习惯),而后面的三个很明显地就会被放在第二个tr的下方了。
这样一来,写起来虽然稍微长了一点,但却可以很直观地解决这个问题了!(在我设计过程中拟定的五六种布局模式中,这种是笔者认为最简单最好理解的,所以笔者最终选用了这样的布局方式,如果有更好的布局方式,欢迎留言指教,感激不尽!)
采用了这种模式之后,es.struct()里面的每一个参数就代表一个树状结构的层。第一个参数是第一层,第二个参数是第二层,第三个参数是第三层……以此类推,每一层里面如果有多个dom对象,就将该层变成一个数组,在没有特别指定的情况下,每一个参数都会成为上一层的第一个dom对象的子对象。而经过特别指定之后,就会成为指定对象的子对象了。
举个例子,我们上面的例子用了三层,假设现在我想在第一个tr下面的第一个td下面放一个div,想在第二个tr的第二个td下面放一个span,那么就这样写:
es("#id").struct("table",["tr","tr"],["(0)td","(0)td","(0)td","(1)td","(1)td","(1)td"],["(0)div","(4)span"]);
我们来看看第四层,里面的div前面的序列号是0,也就是它会被放在上一层的第一个里面,而上一层的第一个又会追朔到上上一层的第一个tr……(其实在写的时候我们只需要用线性思维就可以了,并不需要一层层地往上考虑,这里只是为了写得明白一些方便读者理解而已。)再看看span,它的index是4,所以它会被放在上一层的第五个下面。而上一层的第五个td就是第二个tr对象的第二个子对象。这样一来,我们就实现了这样的结构:
<table>
<tr>
<td>
<div></div>
</td>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>
</td>
<td>
<span></span>
</td>
<td>
</tr>
</table>
现在,采用分层和决定顺序的方式,无论多复杂的dom树结构都可以排列出来了。采用EasyStruct也不会有任何问题了。现在,不管多复杂的结构我们都可以写出来了。
至于有人说的["(0)td","(0)td","(0)td","(1)td","(1)td","(1)td"]这样的写法看起来很啰嗦,是不是可以简化一下,笔者之前也有考虑过这个问题。虽然有一些可行的解决方法,但是简化之后不便于代码的阅读。毕竟,我们的设计还需要考虑可读性的问题。读者也不希望你写的代码再过一两个月连自己都看不懂吧?
如果真要解决的话,读者自己写一个for循环的方法来创建这个数组,配合着EasyStruct使用就行了。举例:
function tr(){
var arr = [];
for(var i=0;i<3;i++){arr.push("(0)td")}
for(var j=0;j<3j++){arr.push("(1)td")}
return arr;
}
es("#id").struct( "table", ["tr","tr"], tr(), ["(0)div","(4)span"] );
=_=但是我怎么觉得这样写更不简便。。。。。
哈哈,对于冗长的tr结构来说还是可能就有必要,一般不要这样写。
而且需要注意,我们创建的是结构啊!结构啊!不是要你一次性把全部内容创建出来。因为EasyStruct里用到了不少for循环和正则表达式,如果被读者多次执行的话,效率是很低的,也没有这个必要。通常我们只需要创建一到两个结构,然后重复使用这一两个结构就够了。对于页面中的静态内容部分,虽然可以使用EasyStruct,但执行效率并不高,所以笔者是不推荐大家这样写的。
后记:开发这个插件还有一个原因,那就是笔者曾经在设想能不能用js完全代替html,让看起来相当杂乱的html结构彻底滚蛋呢?实验了一段时间之后,笔者发现其实是可以的,的确可以写出完全没有半点html的页面。如果是一次设计,以后都不修改的话,那么这样写也倒是没什么问题。但如果你需要经常修改你的页面结构呢?纯js的写法创建的页面肯定会让你修改起来蛋碎一地……哈哈。
好了,这次的EasyStruct就讲到这里。我觉得js的研究过程本身是最有意思,最有意义的。如果真的想学好前端开发的话,就不要浸泡在jquery的温柔乡里享受,有些问题,比如浏览器之间的兼容性问题,还是要自己思考思考的,有条件的话也要自己把jquery给实现一遍。笔者最近差不多搞定属于自己的jquery了,我取名Joy.js。过几天我会把整个设计过程写出来放到博客上和大家一起分享。
代码地址:http://download.csdn.net/detail/sinolzeng/8465619
标签:javascript easystruct html 结构体
原文地址:http://blog.csdn.net/sinolzeng/article/details/44014533