表头固定的表格绘制
已封装为jquery插件,其中
a、fixedtable.css
1 .fix-header-table { overflow:auto; overflow-x:auto; overflow-y:hidden; position:relative; height:100%;} 2 .fix-header-table table { border-collapse:collapse; table-layout:fixed; width:100%; } 3 .fix-header-table td { line-height:40px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; padding:0 5px; } 4 .fix-header-table tr.trForTdWidth td { border-style:none; height:0; } 5 .divH { background:#f2f2f2; } 6 .divH td { text-align:center; font-weight:bold; } 7 .divH td.sortable { cursor:pointer; } 8 .divB { overflow-x:hidden; overflow-y:auto; } 9 .divB td { border-bottom:#dedede 1px solid; } 10 11 .fix-header-table .table-header td { line-height:16px; padding:0; } 12 .fix-header-table .table-header td div { border-left:1px solid #D3D3D3; margin:12px 0; } 13 .fix-header-table .table-header td:first-child div { border-left:none; } 14 .fix-header-table tr.selected td { background-color:#eee; }
b、fixedtable.js
1 /* Created By jcheng.matt 2 * Date: 2016-10-08 3 */ 4 5 (function ($) { 6 7 // 固定表头表格对象 8 var fixedtable = function (setting, obj) { 9 this.setting = setting; 10 this.obj = obj; 11 }; 12 13 fixedtable.prototype.redraw = function () { 14 var obj = this; 15 // 表头 16 var tbHeader = this.getHeader(); 17 // 表内容 18 var tbBody = '<div class="divB">' + this.getBody() + '</div>'; 19 // 合并 20 var html = '<div class="fix-header-table">' 21 html += tbHeader; 22 html += tbBody; 23 html += '</div>'; 24 25 $(this.obj).empty().append(html); 26 $(this.obj).find('.sortable').click(function (event) { obj.sort(event); }); 27 $(this.obj).find('tr[data-row-index]').click(function (event) { obj.select(event); }); 28 29 // 绑定resize事件 30 $(window).resize(function () { obj.resize(); }); 31 32 this.resize(); 33 } 34 35 fixedtable.prototype.getHeader = function () { 36 var header = this.setting.header; 37 var html = '<div class="divH"><table>'; 38 html += this.getTrForTdWidth(); 39 html += '<tr class="table-header">'; 40 for (var i = 0; i < header.length; i++) { 41 var headerClass = header[i].headerClass ? header[i].headerClass : ''; 42 if (header[i].sortable) { 43 html += '<td data-index="' + i + '" class="sortable ' + headerClass + '" ><div>' + header[i].header + '</div></td>'; 44 } 45 else { 46 html += '<td data-index="' + i + '" class="' + headerClass + '"><div>' + header[i].header + '</div></td>'; 47 } 48 } 49 html += '</tr>'; 50 html += '</table></div>'; 51 return html; 52 } 53 54 fixedtable.prototype.getBody = function () { 55 var header = this.setting.header; 56 var list = this.setting.data; 57 var html = '<table>'; 58 html += this.getTrForTdWidth(); 59 for (var i = 0; i < list.length; i++) { 60 html += '<tr class="detail" data-row-index="' + i + '">'; 61 for (var j = 0; j < header.length; j++) { 62 var prop = header[j].dataIndex; 63 var formatter = header[j].formatter ? header[j].formatter : this.setting.formatter; 64 var align = header[j].textAlign ? header[j].textAlign : this.setting.textAlign; 65 html += '<td style="text-align:' + align + '">' + formatter(list[i][prop], i, list[i]) + '</td>' 66 } 67 html += '</tr>'; 68 } 69 html += '</table>'; 70 return html; 71 } 72 73 fixedtable.prototype.getTrForTdWidth = function () { 74 var header = this.setting.header; 75 var html = '<tr class="trForTdWidth">'; 76 for (var i = 0; i < header.length; i++) { 77 html += '<td style="width:' + header[i].width + ';"></td>'; 78 } 79 html += '</tr>'; 80 return html; 81 } 82 83 fixedtable.prototype.sort = function (event) { 84 var obj = this; 85 var header = this.setting.header; 86 var index = +$(event.target).attr('data-index'); 87 var isDescending; 88 if (!$(event.target).hasClass('asc')) { 89 isDescending = false; 90 $(event.target).addClass('asc'); 91 } 92 else { 93 isDescending = true; 94 $(event.target).removeClass('asc'); 95 } 96 list.sort(function (a, b) { 97 var dataIndex = header[index].dataIndex; 98 var compare = header[index].compare ? header[index].compare : this.setting.compare; 99 return (isDescending ? 1 : -1) * compare(a[dataIndex], b[dataIndex]); 100 }); 101 $(this.obj).find('.divB').html(this.getBody()); 102 $(this.obj).find('tr[data-row-index]').click(function (event) { obj.select(event); }); 103 $(this.obj).find('tr[data-row-index]').first().click(); 104 } 105 106 fixedtable.prototype.select = function (event) { 107 if ($(event.currentTarget).hasClass('selected')) { 108 return; 109 } 110 111 $(event.currentTarget).siblings().removeClass('selected'); 112 $(event.currentTarget).addClass('selected'); 113 114 if (this.setting.rowClick) { 115 this.setting.rowClick(event) 116 } 117 } 118 119 fixedtable.prototype.resize = function () { 120 var fixedTable = $(this.obj); 121 122 // 设置表体高度 123 fixedTable.find('.divB').height(fixedTable.height() - fixedTable.find('.divH').height()); 124 125 // ie7下,width 100%包含父元素的滚动条的宽度,需特殊处理 126 if (/MSIE\s*7/.test(navigator.userAgent)) { 127 var hasVerticalScroll = fixedTable.find('.divB').height() < fixedTable.find('.divB table').height(); 128 if (hasVerticalScroll) { 129 fixedTable.find('.divB table').width(fixedTable.find('.divB').width() - 17); 130 } 131 } 132 133 // 设置表头与表体等宽(出现纵向滚动条会导致不等宽) 134 fixedTable.find('.divH, .divB').css('min-width', ''); 135 fixedTable.find('.divH table').width(fixedTable.find('.divB table').width()); 136 137 var hasScroll = fixedTable.find('.divB table').width() > fixedTable.width(); 138 if (hasScroll) { 139 // 当出现横向滚动条时,表体高度需减去滚动条的高度 140 fixedTable.find('.divB').height(fixedTable.height() - fixedTable.find('.divH').height() - 17); 141 // min-width的宽度包含滚动条的宽度,当出现纵向滚动条时需 142 fixedTable.find('table').each(function () { 143 var hasVerticalScroll = fixedTable.find('.divB').height() < fixedTable.find('.divB table').height(); 144 $(this).parent().css('min-width', ($(this).width() + (hasVerticalScroll ? 17 : 0)) + 'px'); 145 }); 146 } 147 } 148 149 $.fn.fixedtable = function (options) { 150 var fixedTableObj = this.prop('obj'); 151 if (!fixedTableObj) { 152 var setting = $.extend({}, $.fn.fixedtable.defaults); 153 fixedTableObj = new fixedtable(setting, this[0]); 154 this.prop('obj', fixedtable); 155 } 156 if (options) { 157 fixedTableObj.setting = $.extend({}, $.fn.fixedtable.defaults, options); 158 fixedTableObj.redraw(); 159 } 160 161 162 return { 163 resize: function () { 164 fixedTableObj.resize(); 165 } 166 }; 167 }; 168 169 $.fn.fixedtable.defaults = { 170 header: [], 171 data: [], 172 textAlign: 'center', 173 formatter: function (arg) { 174 return arg; 175 }, 176 compare: function (arg1, arg2) { 177 if (arg1 < arg2) { 178 return -1; 179 } 180 else if (arg1 > arg2) { 181 return 1; 182 } 183 else { 184 return 0; 185 } 186 }, 187 rowClick: null 188 } 189 } (jQuery));
c、index.html
1 <html> 2 <head> 3 <title>演示</title> 4 <link href="fixedtable.css" rel="stylesheet" type="text/css" /> 5 <style type="text/css"> 6 .rise { color:#cb0000; } 7 .fall { color:#007c00; } 8 </style> 9 </head> 10 <body> 11 <div> 12 <div><input type="button" value="刷新" onclick="resize()" /></div> 13 <div id="productList" style="width:1200px; height:400px; border:1px red solid;"></div> 14 </div> 15 <script type="text/javascript" src="http://netws12-vpn/jquery/1.8_compress/jquery.js"></script> 16 <script src="fixedtable.js" type="text/javascript"></script> 17 <script type="text/javascript"> 18 var data = JSON.parse('[{"Id":386942,"WindCode":"000011.OF","ProductName":"华夏大盘精选","ProductType":"混合型基金","ProductClassId":1,"NetValue":"9.987","NavDate":"2017-01-24","LastedReturn":"-0.488242327620548","Recent1WReturn":"1.4939024390244","Recent1MReturn":"-1.23615506329113","Recent3MReturn":"-4.14436672749522","Recent6MReturn":"-2.65471837258467","Last1Years":"8.97958911389916","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"阳琨,佟巍","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":386943,"WindCode":"000198.OF","ProductName":"天弘余额宝","ProductType":"货币市场型基金","ProductClassId":1,"NetValue":"3.592","NavDate":"2017-01-24","LastedReturn":"0.00985100127148413","Recent1WReturn":"0.0676997093965305","Recent1MReturn":"0.293511369009213","Recent3MReturn":"0.710604208049393","Recent6MReturn":"1.30324472768988","Last1Years":"2.55870095046288","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"王登峰","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":194263,"WindCode":"159915.OF","ProductName":"易方达创业板ETF","ProductType":"股票型基金","ProductClassId":1,"NetValue":"1.7845","NavDate":"2017-01-24","LastedReturn":"-1.36524430687596","Recent1WReturn":"-0.340667932536573","Recent1MReturn":"-5.23101433882102","Recent3MReturn":"-15.3824268576035","Recent6MReturn":"-17.2233045737081","Last1Years":"-12.6059062637739","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"成曦","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":224735,"WindCode":"688888.OF","ProductName":"浙商聚潮产业成长","ProductType":"混合型基金","ProductClassId":1,"NetValue":"1.556","NavDate":"2017-01-24","LastedReturn":"-0.0642260757867624","Recent1WReturn":"0.842514581983158","Recent1MReturn":"-2.19987429289754","Recent3MReturn":"-4.9480757483201","Recent6MReturn":"-2.13836477987422","Last1Years":"9.42334739803095","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"倪权生,唐桦","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":178498,"WindCode":"000010.OF","ProductName":"易方达天天B","ProductType":"货币市场型基金","ProductClassId":1,"NetValue":"4.141","NavDate":"2017-01-24","LastedReturn":"0.0101480032879236","Recent1WReturn":"0.0778444923164692","Recent1MReturn":"0.335762783591696","Recent3MReturn":"0.790143781115028","Recent6MReturn":"1.48484730061463","Last1Years":"2.89431621475745","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"石大怿,刘朝阳","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":125577,"WindCode":"000001.OF","ProductName":"华夏成长","ProductType":"混合型基金","ProductClassId":1,"NetValue":"1.038","NavDate":"2017-01-24","LastedReturn":"-0.192307692307688","Recent1WReturn":"1.56555772994129","Recent1MReturn":"0.193049684953651","Recent3MReturn":"-7.12539916159948","Recent6MReturn":"-5.64859455462844","Last1Years":"-6.88248390221396","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"董阳阳,许利明,孙萌","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":63905,"WindCode":"100058.OF","ProductName":"富国产业债","ProductType":"债券型基金","ProductClassId":1,"NetValue":"1.013","NavDate":"2017-01-24","LastedReturn":"0","Recent1WReturn":"-0.0986193293885819","Recent1MReturn":"0.881385109780586","Recent3MReturn":"-1.63099754586967","Recent6MReturn":"-0.686957215215817","Last1Years":"0.842707216337094","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"黄纪亮","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":229772,"WindCode":"000065.OF","ProductName":"国富焦点驱动灵活配置","ProductType":"混合型基金","ProductClassId":1,"NetValue":"1.326","NavDate":"2017-01-24","LastedReturn":"0","Recent1WReturn":"0.151057401812696","Recent1MReturn":"0.683389756012791","Recent3MReturn":"-0.394590862359953","Recent6MReturn":"0.974803329836268","Last1Years":"4.76514880017546","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"赵晓东","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":356466,"WindCode":"233009.OF","ProductName":"大摩多因子策略","ProductType":"混合型基金","ProductClassId":1,"NetValue":"1.623","NavDate":"2017-01-24","LastedReturn":"-0.490496627835686","Recent1WReturn":"0.995644057249539","Recent1MReturn":"-3.22003577817531","Recent3MReturn":"-4.95929704611373","Recent6MReturn":"-0.213700813717421","Last1Years":"20.2114597814387","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"杨雨龙","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null},{"Id":381035,"WindCode":"002250.OF","ProductName":"红土创新改革红利","ProductType":"混合型基金","ProductClassId":1,"NetValue":"1.026","NavDate":"2017-01-24","LastedReturn":"-0.388349514563105","Recent1WReturn":"1.38339920948617","Recent1MReturn":"-0.581395348837217","Recent3MReturn":"-4.82374768089054","Recent6MReturn":"-0.677637947725075","Last1Years":"--","Origin":1,"Comment":null,"HasAttachment":false,"Manager":"丁硕","ManagerId":null,"SaleBeginDate":null,"SalesEndDate":null}]'); 19 var headers = [ 20 { header: '产品名称', width: '85px', dataIndex: 'ProductName', textAlign: 'left' }, 21 { header: '产品代码', width: '40px', dataIndex: 'WindCode', sortable: true }, 22 { header: '类型', width: '50px', dataIndex: 'ProductType', sortable: true }, 23 { header: '净值/七日年化', width: '50px', dataIndex: 'NetValue', sortable: true, compare: compareNum }, 24 { header: '近1周', width: '30px', dataIndex: 'Recent1WReturn', formatter: getYieldHtml, sortable: true, compare: compareNum }, 25 { header: '近1月', width: '30px', dataIndex: 'Recent1MReturn', formatter: getYieldHtml, sortable: true, compare: compareNum }, 26 { header: '近3月', width: '30px', dataIndex: 'Recent3MReturn', formatter: getYieldHtml, sortable: true, compare: compareNum }, 27 { header: '近6月', width: '30px', dataIndex: 'Recent6MReturn', formatter: getYieldHtml, sortable: true, compare: compareNum }, 28 { header: '近一年', width: '30px', dataIndex: 'Last1Years', formatter: getYieldHtml, sortable: true, compare: compareNum }, 29 { header: '操作', width: '150px', dataIndex: '' } 30 ]; 31 $('#productList').fixedtable({ 32 header: headers, 33 data: data 34 }); 35 36 function getYieldHtml(value) { 37 if (value == '--') { 38 return '<span>--</span>'; 39 } 40 41 var waveClass = ''; 42 if (value > 0) { 43 waveClass = 'rise'; 44 } 45 else if (value < 0) { 46 waveClass = 'fall' 47 } 48 49 return '<span class="' + waveClass + '">' + (+value).toFixed(2) + '%</span>'; 50 } 51 52 function compareNum(arg1, arg2) { 53 if (arg1 == '--') { 54 return -1; 55 } 56 if (arg2 == '--') { 57 return 1; 58 } 59 60 if (+arg1 < +arg2) { 61 return -1; 62 } 63 else if (+arg1 > +arg2) { 64 return 1; 65 } 66 else { 67 return 0; 68 } 69 } 70 71 function resize() { 72 $('#productList').height($('#productList').height() + 5); 73 $('#productList').fixedtable().resize(); 74 } 75 </script> 76 </body> 77 </html>
关于表格元素的几点说明:
1、在CSS中,内部表元素(如td、tr、col等)生成矩形框,这些矩形框包含内容、内边距和边框,但没有外边距,因此如果定义外边距,浏览器将忽略该定义;对于table元素,外边距有效,而对于内边距,当border-collapse为separate时有效,当border-collapse为collapse时无效,内边距的定义会被忽略
2、表显示的层级关系为:表 - 列组 - 列 - 行组 - 行 - 单元格,但对于边框并非层级重叠,而是边框合并
3、表的布局分为固定布局(table-layout:fixed;)和自动布局(table-layout:auto; 此为默认情况);在chrome中,当table的width为auto时(此为默认情况)就会触发自动布局,而无论table-layout的值是什么,但在ie中表现正常;关于浏览器处理固定布局与自动布局的详细步骤可以参考《css权威指南》
4、为了保持td中的文本不换行,可以应用以下样式:
white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
此样式兼容ie7、ie8、ie11、chrome,注意overflow:hidden是必须项,否则text-overflow:ellipsis无效
5、正常情况下,当table的宽度大于td初始宽度之和,多余的宽度将均分到各td,但ie7下,table第一行的td的文本对齐依据td的初始设置宽度,宽度重新配后文本的偏移距离并不随之调整,为兼容ie7,可添加空行(trForTdWdith)
关于div元素的宽度几点说明:
1、在文档流中,div的宽度取决于父元素,与子元素无关,这与table元素不同
2、css的overflow默认为visible,因此在默认情况下,当div的宽度小于子元素,子元素并不会截取,在这种情况下
2.1 如果该div的overflow为auto,则该div会出现滚动条
2.2 如果该div的overflow为visible(此为默认情况),但该div的某一父元素overflow为auto,则该父元素出现滚动条,但该div宽度依然不变,如同父元素overflow为默认值的情况
2.3 如果该div及其父元素overflow均为默认情况,则页面会出现滚动条,但该div及其父元素不受影响,如同子元素宽度小于div的宽度的情况
3、在ie8及以上、chrome中,div为绝对定位且宽度未设定,并且其子元素为块级元素,这时div宽度取决于子元素;在ie7中,父元素并不会被子元素撑开,因此为了兼容ie7,须使用js动态设定父元素的宽度;对于绝对定位,如果其大小大于包含块,表现与2所示的情况相同
4、将div设为浮动也可以达到宽度被子元素撑开的目的,兼容性与设为绝对定位相同,即在ie7中,父元素并不会被子元素撑开,因此为了兼容ie7,须使用js动态设定父元素的宽度
关于div出现滚动条的几点说明:
1、当子元素宽度或高度大于父元素div,且div样式设为overflow:auto时,div将出现纵向或横向滚动条;在ie7、ie8、ie11、chrome,滚动条的宽度均为17px
2、设父元素为div1,子元素为div2,取父元素的高度或宽度(如$('#div1').width() 或$('#div1').height()),则父元素的宽高始终包含滚动条的宽度
3、对于子元素的高度或宽度,以宽度为例(高度表现与宽度相同),设子元素div2无内边距、外边距及边框,父元素出现纵向滚动条
3.1 div2的宽度为100%时
在ie8、ie11、chrome中,div2的宽度比父元素div1少17px,即:$('#div1').width() - $('#div2').width() == 17
在ie7中,div2的宽度比父元素div1相同
3.2 div2的宽度为auto时
在所有浏览器下,div2的宽度均比父元素div1少17px,即:$('#div1').width() - $('#div2').width() == 17
4、设父元素div1定位为relative,子元素div2定位为absolute,在这种情况下可以通过设置div2的定位属性(top:30; bottom:0;)自动调节div2的高度,即通过css而非js调节绝对定位元素的高度;当div1出现滚动条时,在ie8、ie11、chrome中,bottom相对滚动条上边沿定位(即相对父元素内容区的底端定位),在ie7中,bottom相对滚动条下边沿定位(即相对父元素下边框定位),因此为兼容ie7,需使用js动态设置div2的高度