上一篇里使用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');