? 近来根据项目需求,需要实现动态的根据json文本生成一个可分组的table功能,并且需要实现类似tree的展开和收缩功能, 另外附带checkboxpartial check功能。
? 查阅了不少js framework,没有比较顺手的,都需要二次开发,于是打算自己写一个简单的, 先看一个页面:
?Basic Framework: Jquery, 因为需要很多的selector操作,而jquery的$()不是盖的,而且只需要用到jquery的min js,比较轻量级。
Partial Checkbox 的实现,因为checkbox本身不支持partial check, 所以需要用图片来实现,基本原理是底层一个白色的空图片,根据点击事件来动态的更换background picture。
Json parser: 我实现的很简单,json文本中放置一个数组,里面是需要显示的对象, 利用jquery的each方法遍历文本,分析出group和group item, 调用jquery的append方法生成tr
json 文本如下:
?
var data = {"source": [ { "groupName":100001, "setpointTemplateId":2212, "protocolName":"ComTrol 6k", "unifiedAppTypeName":"Suction Group", "unifiedPointName":"SUCT PRES SETPT", "energyCritical":true, "alarmSetpoint":false, "checkOveride":false, "isChecked":true, "checkWaringMsg":"function(isChecked){if(isChecked){alert('If you unchecked this point, you can not calculate the benefit.');}}" }, { "groupName":100001, "setpointTemplateId":2213, "protocolName":"ComTrol 6k", "unifiedAppTypeName":"Suction Group", "unifiedPointName":"Lead Float Ckt", "energyCritical":false, "alarmSetpoint":true, "checkOveride":false, "isChecked":false, "checkWaringMsg":'' }, { "groupName":100001, "setpointTemplateId":2214, "protocolName":"ComTrol 6k", "unifiedAppTypeName":"Suction Group", "unifiedPointName":"FLOAT TEMP", "energyCritical":true, "alarmSetpoint":false, "checkOveride":false, "isChecked":false, "checkWaringMsg":'' }, { "groupName":100002, "setpointTemplateId":1000, "protocolName":"E2", "unifiedAppTypeName":"Suction Group", "unifiedPointName":"CONTROL TEMP", "energyCritical":false, "alarmSetpoint":true, "checkOveride":false, "isChecked":false, "checkWaringMsg":'' }, { "groupName":100002, "setpointTemplateId":1001, "protocolName":"E2", "unifiedAppTypeName":"Suction Group", "unifiedPointName":"FLOAT TEMP", "energyCritical":true, "alarmSetpoint":false, "checkOveride":false, "isChecked":false, "checkWaringMsg":'' }, { "groupName":100002, "setpointTemplateId":1002, "protocolName":"E2", "unifiedAppTypeName":"Suction Group", "unifiedPointName":"FLOAT TEMP", "energyCritical":false, "alarmSetpoint":true, "checkOveride":false, "isChecked":false, "checkWaringMsg":'' } ] };
?html页面:
?
?
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="data.js"></script> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="index_1.js"></script> <style type="text/css"> .node-unchecked {background-image: url(unchecked.gif);} .node-checked-partial {background-image: url(checked-partial.gif);} .node-checked {background-image: url(checked.gif);} .image-checkbox {border: 0 none;height: 18px;margin: 0;padding: 0;vertical-align: middle;width: 15px;background-repeat: no-repeat;} #custom_profile_table { border-collapse:collapse; } #custom_profile_table td, #custom_profile_table th { border:1px solid #ADADAA; padding:3px 3px 3px 3px; text-align:center; } #custom_profile_table th { text-align:center; padding-top:5px; } #custom_profile_table tr.alt td { color:#000; background-color:#EAF2D3; } </style> </head> <body> <table> <tr> <td><button onClick="selectHelperHandler('SELECT_ALL_ENERGY')">Select all Energy Critical Setpoint</button></td> <td><button onClick="selectHelperHandler('SELECT_ALL_ALARM')">Select all Alarm Setpoint</button></td> <td><button onClick="selectHelperHandler('SELECT_ALL')">Select all</button></td> <td><button onClick="selectHelperHandler('UNSELECT_ALL')">Un Select All</button></td> </tr> </table> <br> <table id="custom_profile_table"> <thead> <tr> <th colspan="2"></th> <th>Application Type</th> <th>Protocol</th> <th>Point</th> <th>Recommend Energy Critical</th> <th>Recommend Alarm Critical</th> <th>Check Override</th> </tr> </thead> <tbody> </tbody> </table> </body> </html>
?javascript如下
?
?
var MAIN_TABLE_ID = "custom_profile_table"; var PLUG_MIN_IMG_ID = "plus-min"; var CHECKBOX_UNCHECKED = 'node-unchecked'; var CHECKBOX_CHECKED = 'node-checked'; var CHECKBOX_PARTIAL = 'node-checked-partial'; //id prefix for the tr tag var PARENT = 'parent_'; var CHILD = 'child_'; //id prefix for the checkbox img tag var ALL = "all_"; var SUB = "sub_"; //unique id var PARENT_UNIQUE_TEMPLATE_ID = '00000'; //the plus and minus picture define var PLUS_SRC = "plusnolines.gif"; var MINUS_SRC = "minusnolines.gif"; var YES = "yes"; var NO = "no"; var SELECT_ALL_ENERGY = "SELECT_ALL_ENERGY"; var SELECT_ALL_ALARM = "SELECT_ALL_ALARM"; var SELECT_ALL = "SELECT_ALL"; var UNSELECT_ALL = "UNSELECT_ALL"; /********************************************** Select all handler for checkbox image **********************************************/ function selectAll(all) { var selectedAllImg = $('#' + all.id); var bUnchecked = selectedAllImg.hasClass(CHECKBOX_UNCHECKED); var bPartialChecked = selectedAllImg.hasClass(CHECKBOX_PARTIAL); var bChecked = selectedAllImg.hasClass(CHECKBOX_CHECKED); if(bUnchecked) { selectedAllImg.removeClass(CHECKBOX_UNCHECKED); selectedAllImg.addClass(CHECKBOX_CHECKED); } else { selectedAllImg.removeClass(CHECKBOX_CHECKED); selectedAllImg.removeClass(CHECKBOX_PARTIAL); selectedAllImg.addClass(CHECKBOX_UNCHECKED); } var groupName = extractGroupName(selectedAllImg); $('table#' + MAIN_TABLE_ID + ' img[id^=sub_'+groupName+']').each(function() { var hiddenId = $(this).attr('id').replace("sub","hidden"); if(bUnchecked) { $(this).removeClass(CHECKBOX_UNCHECKED); $(this).addClass(CHECKBOX_CHECKED); $('table#' + MAIN_TABLE_ID + ' input[id='+hiddenId+']').attr('checked', true); } else { $(this).removeClass(CHECKBOX_CHECKED); $(this).removeClass(CHECKBOX_PARTIAL); $(this).addClass(CHECKBOX_UNCHECKED); $('table#' + MAIN_TABLE_ID + ' input[id='+hiddenId+']').attr('checked', false); } } ) } /********************************************** Select all handler for checkbox image **********************************************/ function selectSub(sub) { selectSubById(sub.id); scanForAllCheckCss(extractGroupName($('#' + sub.id))); } function selectSubById(id, forceValue) { var selectedSubImg = $('#' + id); var bUnchecked = selectedSubImg.hasClass(CHECKBOX_UNCHECKED); var bChecked = selectedSubImg.hasClass(CHECKBOX_CHECKED); var groupName = extractGroupName(selectedSubImg); var hiddenId = id.replace("sub","hidden"); if(bUnchecked || forceValue) { selectedSubImg.removeClass(CHECKBOX_UNCHECKED); selectedSubImg.addClass(CHECKBOX_CHECKED); $('table#' + MAIN_TABLE_ID + ' input[id='+hiddenId+']').attr('checked', true); } else { selectedSubImg.removeClass(CHECKBOX_CHECKED); selectedSubImg.addClass(CHECKBOX_UNCHECKED); $('table#' + MAIN_TABLE_ID + ' input[id='+hiddenId+']').attr('checked', false); } } function scanForAllCheckCss(groupName) { var allImg = $('table#' + MAIN_TABLE_ID + ' img[id=all_'+groupName+'_'+PARENT_UNIQUE_TEMPLATE_ID+']'); allImg.removeClass(CHECKBOX_UNCHECKED); allImg.removeClass(CHECKBOX_CHECKED); allImg.removeClass(CHECKBOX_PARTIAL); var bFoundChecked=false; var bFoundUnchecked=false; $('table#' + MAIN_TABLE_ID + ' img[id^=sub_'+groupName+']').each(function() { if($(this).hasClass(CHECKBOX_CHECKED)) { bFoundChecked=true; } else { bFoundUnchecked=true; } if(bFoundChecked && bFoundUnchecked) { allImg.addClass(CHECKBOX_PARTIAL); return false; } } ) if(bFoundChecked && !bFoundUnchecked) { allImg.addClass(CHECKBOX_CHECKED); } if(bFoundUnchecked && !bFoundChecked) { allImg.addClass(CHECKBOX_UNCHECKED); } } /********************************************** extract the group name **********************************************/ function extractGroupName(jqueryObject) { var items = jqueryObject.attr('id').split('_'); return items[1]; } var cachedGroupName = new Array(); var tbody; function buildTableBody() { $(data.source).each(function(index, element){ if(cachedGroupName.indexOf(element.groupName)==-1) { cachedGroupName[cachedGroupName.length]=element.groupName; addParentTr(element); } addChildTr(element); }) } function addParentTr(element) { var buffer = '' .concat('<tr id="parent_' + element.groupName+ '_' + PARENT_UNIQUE_TEMPLATE_ID + '">') .concat('<td><img src="' + MINUS_SRC + '" id="plus-min_' + element.groupName + '_' + PARENT_UNIQUE_TEMPLATE_ID + '"/><img src="s.gif" class="image-checkbox node-unchecked" onClick="selectAll(this)" id="all_' + + element.groupName + '_' + PARENT_UNIQUE_TEMPLATE_ID + '"/></td>') .concat('<td> </td>') .concat('<td>' + element.unifiedAppTypeName + '</td>') .concat('<td>' + element.protocolName + '</td>') .concat('<td>--</td>') .concat('<td>--</td>') .concat('<td>--</td>') .concat('<td>--</td>') .concat('</tr>'); tbody.append(buffer); } var checkboxIndex; function addChildTr(element) { var checkCss = CHECKBOX_UNCHECKED; var checkStr = ''; var checkFuc = 'null'; if(element.checkWaringMsg != null &&element.checkWaringMsg.trim()!='') { checkFuc = element.checkWaringMsg; } if(element.isChecked) { checkCss = CHECKBOX_CHECKED; checkStr = 'checked'; } var buffer = '' .concat('<tr id="child_' + element.groupName + '_' + element.setpointTemplateId + '">') .concat('<td> </td>') .concat('<td><img src="s.gif" class="image-checkbox ' + checkCss + '" onClick="commonCheck(this,' + checkFuc + '),selectSub(this)" id="sub_'+element.groupName+'_'+element.setpointTemplateId+'"/><input style="display:none" type="checkbox" name="selectedTemplate[' + checkboxIndex + ']" value="' + element.groupName + '" ' + checkStr + ' id="hidden_'+element.groupName+'_'+element.setpointTemplateId+'"/></td>') .concat('<td>--</td>') .concat('<td>--</td>') .concat('<td>' + element.unifiedPointName + '</td>') .concat('<td>' + (element.energyCritical?YES:NO) + '</td>') .concat('<td>' + (element.alarmSetpoint?YES:NO) + '</td>') .concat('<td>' + (element.checkOveride?YES:NO) + '</td>') .concat('</tr>'); tbody.append(buffer); checkboxIndex++; } function commonCheck(node, str) { if(str == null) return; var isChecked = $('#' + node.id).hasClass('node-checked'); eval("var func = " + str); func(isChecked); } function selectHelperHandler(condition) { $(data.source).each(function(index, element){ switch(condition) { case SELECT_ALL_ENERGY: if(element.energyCritical) { selectSubById('sub_' + element.groupName + '_' + element.setpointTemplateId, true); } break; case SELECT_ALL_ALARM: if(element.alarmSetpoint) { selectSubById('sub_' + element.groupName + '_' + element.setpointTemplateId, true); } break; case SELECT_ALL: selectSubById('sub_' + element.groupName + '_' + element.setpointTemplateId, true); break; case UNSELECT_ALL: selectSubById('sub_' + element.groupName + '_' + element.setpointTemplateId, false); break; } }); $(cachedGroupName).each(function(index, element) { scanForAllCheckCss(element); }); } $(function(){ checkboxIndex = 0; tbody = $('table#' + MAIN_TABLE_ID + ' tbody'); buildTableBody(); $(cachedGroupName).each(function(index, element) { scanForAllCheckCss(element); } ); $('table#' + MAIN_TABLE_ID + ' tr td img[id^=' + PLUG_MIN_IMG_ID + ']') .click(function(){ if($(this).attr("src") == PLUS_SRC) { $(this).attr('src', MINUS_SRC); } else { $(this).attr('src', PLUS_SRC); } var parentTr = $(this).parent().parent(); var groupName = extractGroupName(parentTr); //parentTr.siblings('#child_'+ groupName).toggle(); $('table#' + MAIN_TABLE_ID + ' tr[id^=child_'+groupName+']').toggle(); }); $('table#' + MAIN_TABLE_ID + ' tr[id^=child_]').hide(); })
?
?
总结: 里面最重要的jquery的selector的使用,包括模糊匹配, 另外就是jquery的遍历和动态的操作tag的可见性和样式,?scanForAllCheckCss()用来在页面初始和item点击的时候调用,用来确定是否是partial check,selectHelperHandler()用来实现全选,全取消和条件选择。
?
完整代码见附件, 可以访问index_1.html看效果。