CSS样式在JS中的巧用
上一篇里使用css样式虚类nonie(实际属性是定义给了yui3-panel-hidden而不是nonie)解决了在不同浏览器下的样式兼容问题
其实,css虚类(未在CSS文件中定义具体属性)还有很多巧用,在大量使用JS脚本和AJAX的系统中,巧用css虚类,可以实现很多功能
下面以我实际的开发例子介绍
第一:使用代理时,可以通过css虚类作为类选择器
例如,我在YUI的表格里,每一行上都有一列操作列,里面都是以<a>标签形式显示操作,此列都是通过模板动态生成的,所以这些<a>标签没有id,而指定这些<a>标签的响应方法,必须通过代理方式,而对应的<a>标签则通过一个虚类来指定。其实可以在生成标签时指定onclick事件,但是显然没有代理来的好,代理不用给每个标签写click方法。
下面是某table(yui3的DataTable widget)的操作列的定义:
{ key: 'id', label: '<center>操作</center>', sortable: false, width: '180px', allowHTML: true, formatter: function(o) { return '<center><a class="update" href="#"><img src="../image/modify.gif">修改信息</a><a class="delete" href="#"><img src="../image/delete.gif">删除</a></center>'; } }
而该table可以定义代理并可以使用<a>上的虚类类型来指定响应对象
table.delegate('click', showUpdatePanel, '.yui3-datatable-data tr .update', table); table.delegate('click', deleteData, '.yui3-datatable-data tr .delete, table);
第二,可以使用虚类定义隐藏的信息
我在某项目里,需要在鼠标移动到表格里时浮动显示tooltip并根据用户当前选择动态显示不同的信息,这些信息包括:用户填写的备注,领导的批复,历史修改记录,逻辑验证错误提示等,有的是数据载入时一次性载入的(当然也可以选择根据需要通过ajax加载),有的是根据用户填写动态生成的。对此,我的处理办法是,把这些信息各自塞到一个指定了hidden样式的span标签里,然后把span都隐藏到相关的td里,这样,在鼠标移动时,就通过查找对应的span标签里的信息,然后将这些信息拷贝到tooltip里,而判断当前显示哪样信息,就是通过span标签额外的虚类样式了
鼠标移动的代码是这样的:
warningMouseMove: function(e) { if (e.currentTarget.hasClass('warning') || e.currentTarget.hasClass('viewnote') || e.currentTarget.hasClass('viewaudit') || e.currentTarget.hasClass('viewmodify') || e.currentTarget.hasClass('viewhelp')) { if (e.currentTarget.hasClass('warning')) { var m = ''; e.currentTarget.all('.warningmessage').each(function(k, v) { if (m != '') { m += '<br>---------<br>'; } m += k.get('text').replace(/\n/g, "<br>"); }); tooltip.setStdModContent('body', m); } else if (e.currentTarget.hasClass('viewnote')) { tooltip.setStdModContent('body', e.currentTarget.one('.notecontent').get('text').replace(/\n/g, "<br>")); } else if (e.currentTarget.hasClass('viewmodify')) { tooltip.setStdModContent('body', e.currentTarget.one('.modifycontent').get('text').replace(/\n/g, "<br>")); } else if (e.currentTarget.hasClass('viewhelp')) { tooltip.setStdModContent('body', e.currentTarget.one('.helpcontent').get('text').replace(/\n/g, "<br>")); } else if (e.currentTarget.hasClass('viewaudit')) { var c = ''; if (e.currentTarget.one('.auditcontent') != null) { c = e.currentTarget.one('.auditcontent').get('text'); } if (e.currentTarget.one('.addauditcontent') != null) { if (c != '') { c += '<br>'; } c += e.currentTarget.one('.addauditcontent').get('text').replace(/\n/g, "<br>"); } tooltip.setStdModContent('body', c); } var posx = e.pageX > screen.width * 0.8 ? e.pageX - 200 : e.pageX + 10; tooltip.move([posx, (e.pageY + 20)]); if (tooltip.get('visible') === false) { Y.one('#tooltip').setStyle('opacity', '0'); } if (waitingToShow === false) { setTimeout(function() { Y.one('#tooltip').setStyle('opacity', '1'); tooltip.show(); }, 500); waitingToShow = true; } } else if (waitingToShow == true) { tooltip.hide(); waitingToShow = false; } },
解释下:这个方法运行的对象是一个<table>表格里的td,如果是正常状态,则用户输入后会进行校验,有错误的情况下会产生warning样式,并动态生成错误信息,而错误信息涉及的对象并不一定只是输入数据的td。在用户点击了查看备注时,则会移除其它样式,为相关td添加viewnotes样式,点击查看批示,则会添加viewaudit样式等。而用户鼠标移动到格子上,则会根据当前样式判定获取哪个样式span里的信息并显示
第三,这是我编写的统计网站里的一个校验功能,报表有很多项输入,这里面有很多限制,包括:
1、允许空或非空
2、只允许整数
3、必须是数字(可以是浮点数)
4、必须大于0
5、有的项和同期相比不能超过20%(超过的必须填写备注)
6、众多勾稽关系比较,例如a必须大于b,c必须等于d+e等等
用户输入时应当即时提示,并且,用户输入完毕后必须进行全部校验通过方可提交
最初的一个版本,是将逻辑校验写在配置文件里,然后通过读取配置文件的数学公式进行运算
后来推翻重写时,放弃了此项,直接将逻辑编写在代码里,原因么,主要是维护那个配置文件太复杂,每次报表改动要折腾死人,而且里面的项和数据库字段以及表格表格上的代码不匹配
于是解决方法是:非空项上添加虚样式,统一校验逻辑里通过判断样式来进行处理
例如:
<td id="q2014-H02" class="tdinput numeric noteable blankenabled rule compare">
tdinput为该td的正式样式,numeric代表此td必须输入浮点数,blankenabled说明此td允许空着不填,rule说明此td有逻辑校验关系(具体是哪条则由逻辑校验代码通过td的id来判断了),noteable则代表此td允许用户输入备注信息,compare说明此td项应当和去年同期的进行比对,如果超过指定比例(20%),则需要提示用户必须输入原因(备注)
当用户结束某td内的输入时,执行checkNode方法
function compareToLastYear(lnode) { var pspan = lnode.one('.previous'); if (pspan == null) return true; removeWarning('comparewarning', lnode); var lastvalue = parseFloat(pspan.get('text')); var thisvalue = parseFloat(lnode.one('.current').get('text')); if (isNaN(lastvalue)) return true; if (lnode.one('.notecontent') != null && lnode.one('.notecontent').get('text') != '') return true; if (thisvalue > lastvalue * 1.2) { appendWarning('此项与上一期相比增长率超过了20%,上一期数值为' + lastvalue + ',请核对或填写备注!', 'comparewarning', lnode); return false; } if (thisvalue < lastvalue * 0.8) { appendWarning('此项与上一期相比下降超过了20%,上一期数值为' + lastvalue + ',请核对或填写备注!', 'comparewarning', lnode); return false; } return true; }; function removeWarning(classname) { for (var i = 1; i < arguments.length; i++) { var lnode = arguments[i]; lnode.all('.' + classname).each(function(k, v) { k.remove(); }); if (lnode.one('.warningmessage') == null) { lnode.removeClass('warning'); } } }; function appendWarning(message, classname) { for (var i = 2; i < arguments.length; i++) { lnode = arguments[i]; if (reportviewitem == 'warning' && !lnode.hasClass('warning')) { lnode.addClass('warning'); } lnode.appendChild(Y.Node.create('<span class="warningmessage ' + classname + '"></span>').set('text', message)); } }; function checkZero(lnode) { var v = lnode.one('.current').get('text'); removeWarning('zerowarning', lnode); if (parseFloat(v) <= 0) { appendWarning('此项必须大于0!', 'zerowarning', lnode); return false; } return true; }; function checkInput(lnode) { var v = lnode.one('.current').get('text'); removeWarning('inputwarning', lnode); if (!lnode.hasClass('blankenabled') && v == '') { appendWarning('此项必须填写!', 'inputwarning', lnode); return false; } return true; }; //如果有规则,优先检测规则 function checkNode(lnode) { checkInput(lnode); if (lnode.hasClass('rule')) { checkRule(lnode); } if (lnode.hasClass('nonzero')) { checkZero(lnode); } if (lnode.hasClass('compare')) { compareToLastYear(lnode); } };
checkRule方法就不贴了,里面涉及到报表的具体勾稽关系,numberic和number项,则是写在用户输入完毕后,不在统一的校验逻辑里,因为这些项可能允许空
第四,通过css样式来进行多语言切换
其实这是个偷懒方法,在页面里用了多种方法显示多语言,然后呢,有一部分页面上不太好通过多语言资源配置的,例如中文和英文顺序不一样(中间还有其它东西),这时可以用<span class="en hidden">english</span><span class="zh">中文</span>这种模式,然后呢,在脚本里判断下,如果是英文呢,就执行
Y.all('.zh').addClass('hidden'); Y.all('.en').removeClass('hidden');