JS如何使用Bootstrap Table的冻结列功能彻底解决高度问题
这篇文章主要为大家展示了“JS如何使用Bootstrap Table的冻结列功能彻底解决高度问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JS如何使用Bootstrap Table的冻结列功能彻底解决高度问题”这篇文章吧。
一、问题追踪
记得在之前的那篇里面介绍过,bootstrapTable组件自带的冻结列扩展,不能兼容ie浏览器,即使最新版本的ie也会无法使用,这是一般的系统不能忍受的,所以在那篇里面给出过解决方案,但并未分析ie浏览器不能兼容的原因,昨天博主花了点时间特意调试了下源码,原来在ie里面,使用jquery的clone()方法和谷歌等浏览器有所区别。为了展示这个区别,这里先抛个砖。比如有如下代码:
<tableid="tbtest"> <tr><td>aaa</td><td>bbb</td><td>ccc</td></tr> <tr><td>ddd</td><td>eee</td><td>fff</td></tr> <tr><td>ggg</td><td>hhh</td><td>iii</td></tr> </table> <scripttype="text/javascript"> var$tr=$('#tbtesttr:eq(0)').clone(); var$tds=$tr.find('td'); $tr.html(''); alert($tds.eq(0).html()); </script>
代码本身很简单,只是为了测试用。看到这里你可以试着猜一下alert的结果。
算了,不考大家了,直接贴出来吧,有图有真相!
相信不用我过多的解释哪个是ie,哪个是谷歌了吧。
两者的区别很明显,谷歌里面得到“aaa”,而ie里面得到空字符串。这是为什么呢?
其实如果你用值类型和引用类型的区别来解释这个差别你就不难理解了,在谷歌浏览器里面,$tr变量是一个引用类型,当你清空了它里面的内容,只是清除了$tr这个变量的“指针”,或者叫指向,$tds变量仍然指向了$tr的原始内容,所以调用$tds.eq(0).html()的时候仍然能得到结果aaa;同样的代码在ie浏览器里面,$tr变量就是一个值类型,你清空了它里面的内容之后,$tds的内容也被清空了。如果你有更好的解释,欢迎赐教哈。
之所以组件原生的js不能兼容ie浏览器,就是因为它使用了clone()这个方法,导致在不同的浏览器看到不同的结果。相信bootstrapTable组件的作者应该是知道这个区别的,只不过没有太在意这些,从作者做的很多功能的兼容性能够看出,他做的功能很多没有太多的考虑ie浏览器的效果。
二、效果预览
还是老规矩,说了这个多,没图怎么行,小二,上图!
没有固定高度的情况:单列冻结。
多列冻结。
固定任意高度效果
ie浏览器也没有问题,这里就不再重复上图了。
三、源码解析
源码没啥说的,有兴趣可以自己看看,主要的原理还是重写bootstrapTable构造器的事件,来达到想要的效果。
(function($){ 'usestrict'; $.extend($.fn.bootstrapTable.defaults,{ fixedColumns:false, fixedNumber:1 }); varBootstrapTable=$.fn.bootstrapTable.Constructor, _initHeader=BootstrapTable.prototype.initHeader, _initBody=BootstrapTable.prototype.initBody, _resetView=BootstrapTable.prototype.resetView; BootstrapTable.prototype.initFixedColumns=function(){ this.$fixedHeader=$([ '<divclass="fixed-table-header-columns">', '<table>', '<thead></thead>', '</table>', '</div>'].join('')); this.timeoutHeaderColumns_=0; this.$fixedHeader.find('table').attr('class',this.$el.attr('class')); this.$fixedHeaderColumns=this.$fixedHeader.find('thead'); this.$tableHeader.before(this.$fixedHeader); this.$fixedBody=$([ '<divclass="fixed-table-body-columns">', '<table>', '<tbody></tbody>', '</table>', '</div>'].join('')); this.timeoutBodyColumns_=0; this.$fixedBody.find('table').attr('class',this.$el.attr('class')); this.$fixedBodyColumns=this.$fixedBody.find('tbody'); this.$tableBody.before(this.$fixedBody); }; BootstrapTable.prototype.initHeader=function(){ _initHeader.apply(this,Array.prototype.slice.apply(arguments)); if(!this.options.fixedColumns){ return; } this.initFixedColumns(); varthat=this,$trs=this.$header.find('tr').clone(); $trs.each(function(){ $(this).find('th:gt('+(that.options.fixedNumber-1)+')').remove(); }); this.$fixedHeaderColumns.html('').append($trs); }; BootstrapTable.prototype.initBody=function(){ _initBody.apply(this,Array.prototype.slice.apply(arguments)); if(!this.options.fixedColumns){ return; } varthat=this, rowspan=0; this.$fixedBodyColumns.html(''); this.$body.find('>tr[data-index]').each(function(){ var$tr=$(this).clone(), $tds=$tr.find('td'); //$tr.html('');这样存在一个兼容性问题,在IE浏览器里面,清空tr,$tds的值也会被清空。 //$tr.html(''); var$newtr=$('<tr></tr>'); $newtr.attr('data-index',$tr.attr('data-index')); $newtr.attr('data-uniqueid',$tr.attr('data-uniqueid')); varend=that.options.fixedNumber; if(rowspan>0){ --end; --rowspan; } for(vari=0;i<end;i++){ $newtr.append($tds.eq(i).clone()); } that.$fixedBodyColumns.append($newtr); if($tds.eq(0).attr('rowspan')){ rowspan=$tds.eq(0).attr('rowspan')-1; } }); }; BootstrapTable.prototype.resetView=function(){ _resetView.apply(this,Array.prototype.slice.apply(arguments)); if(!this.options.fixedColumns){ return; } clearTimeout(this.timeoutHeaderColumns_); this.timeoutHeaderColumns_=setTimeout($.proxy(this.fitHeaderColumns,this),this.$el.is(':hidden')?100:0); clearTimeout(this.timeoutBodyColumns_); this.timeoutBodyColumns_=setTimeout($.proxy(this.fitBodyColumns,this),this.$el.is(':hidden')?100:0); }; BootstrapTable.prototype.fitHeaderColumns=function(){ varthat=this, visibleFields=this.getVisibleFields(), headerWidth=0; this.$body.find('tr:first-child:not(.no-records-found)>*').each(function(i){ var$this=$(this), index=i; if(i>=that.options.fixedNumber){ returnfalse; } if(that.options.detailView&&!that.options.cardView){ index=i-1; } that.$fixedHeader.find('th[data-field="'+visibleFields[index]+'"]') .find('.fht-cell').width($this.innerWidth()); headerWidth+=$this.outerWidth(); }); this.$fixedHeader.width(headerWidth).show(); }; BootstrapTable.prototype.fitBodyColumns=function(){ varthat=this, top=-(parseInt(this.$el.css('margin-top'))), //thefixedheightshouldreducethescorll-xheight height=this.$tableBody.height()-18; debugger; if(!this.$body.find('>tr[data-index]').length){ this.$fixedBody.hide(); return; } if(!this.options.height){ top=this.$fixedHeader.height()-1; height=height-top; } this.$fixedBody.css({ width:this.$fixedHeader.width(), height:height, top:top+1 }).show(); this.$body.find('>tr').each(function(i){ that.$fixedBody.find('tr:eq('+i+')').height($(this).height()-0.5); varthattds=this; debugger; that.$fixedBody.find('tr:eq('+i+')').find('td').each(function(j){ $(this).width($($(thattds).find('td')[j]).width()+1); }); }); //events this.$tableBody.on('scroll',function(){ that.$fixedBody.find('table').css('top',-$(this).scrollTop()); }); this.$body.find('>tr[data-index]').off('hover').hover(function(){ varindex=$(this).data('index'); that.$fixedBody.find('tr[data-index="'+index+'"]').addClass('hover'); },function(){ varindex=$(this).data('index'); that.$fixedBody.find('tr[data-index="'+index+'"]').removeClass('hover'); }); this.$fixedBody.find('tr[data-index]').off('hover').hover(function(){ varindex=$(this).data('index'); that.$body.find('tr[data-index="'+index+'"]').addClass('hover'); },function(){ varindex=$(this).data('index'); that.$body.find('>tr[data-index="'+index+'"]').removeClass('hover'); }); }; })(jQuery);
.fixed-table-header-columns, .fixed-table-body-columns{ position:absolute; background-color:#fff; display:none; box-sizing:border-box; overflow:hidden; } .fixed-table-header-columns.table, .fixed-table-body-columns.table{ border-right:1pxsolid#ddd; } .fixed-table-header-columns.table.table-no-bordered, .fixed-table-body-columns.table.table-no-bordered{ border-right:1pxsolidtransparent; } .fixed-table-body-columnstable{ position:absolute; animation:none; } .bootstrap-table.table-hover>tbody>tr.hover>td{ background-color:#f5f5f5; }
如何使用呢?这里博主单独搞了一个静态的html测试页,还是贴出来供大家参考。
<!DOCTYPEhtml> <htmllang="en"xmlns="http://www.w3.org/1999/xhtml"> <head> <metacharset="utf-8"/> <title></title> <!--必须的css引用--> <linkhref="Content/bootstrap/css/bootstrap.min.css"rel="stylesheet"/> <linkhref="Content/bootstrap-table/bootstrap-table.min.css"rel="stylesheet"/> <linkhref="Content/bootstrap-table/extensions/fixed-column/bootstrap-table-fixed-columns.css"rel="stylesheet"/> </head> <body> <divclass="panel-body"> <!--<divclass="panelpanel-default"> <divclass="panel-heading">查询条件</div> <divclass="panel-body"> <formid="formSearch"class="form-horizontal"> <divclass="form-group"> <labelclass="control-labelcol-sm-1"for="name">员工姓名</label> <divclass="col-sm-3"> <inputtype="text"class="form-control"id="name"> </div> <labelclass="control-labelcol-sm-1"for="address">家庭住址</label> <divclass="col-sm-3"> <inputtype="text"class="form-control"id="address"> </div> <divclass="col-sm-4"> <buttontype="button"id="btn_query"class="btnbtn-primary">查询</button> </div> </div> </form> </div> </div>--> <divid="toolbar"class="btn-group"> <buttonid="btn_add"type="button"class="btnbtn-success"> <spanclass="glyphiconglyphicon-plus"aria-hidden="true"></span>新增 </button> </div> <tableid="tb_user"></table> </div> <!--新增或者编辑的弹出框--> <divclass="modalfade"id="myModal"tabindex="-1"role="dialog"aria-labelledby="myModalLabel"> <divclass="modal-dialog"role="document"> <divclass="modal-content"> <divclass="modal-header"> <buttontype="button"class="close"data-dismiss="modal"aria-label="Close"><spanaria-hidden="true">×</span></button> <h5class="modal-title"id="myModalLabel">操作</h5> </div> <divclass="modal-body"> <divclass="row"> <labelclass="control-labelcol-xs-2">姓名</label> <divclass="col-xs-10"> <inputtype="text"name="Name"class="form-control"placeholder="姓名"> </div> </div> <divclass="row"> <labelclass="control-labelcol-xs-2">年龄</label> <divclass="col-xs-10"> <inputtype="text"name="Age"class="form-control"placeholder="年龄"> </div> </div> <divclass="row"> <labelclass="control-labelcol-xs-2">学校</label> <divclass="col-xs-10"> <inputtype="text"name="School"class="form-control"placeholder="学校"> </div> </div> <divclass="row"> <labelclass="control-labelcol-xs-2">家庭住址</label> <divclass="col-xs-10"> <inputtype="text"name="Address"class="form-control"placeholder="学校"> </div> </div> <divclass="row"> <labelclass="control-labelcol-xs-2">备注</label> <divclass="col-xs-10"> <textareaclass="form-control"placeholder="备注"name="Remark"></textarea> </div> </div> </div> <divclass="modal-footer"> <buttontype="button"class="btnbtn-default"data-dismiss="modal"><spanclass="glyphiconglyphicon-remove"aria-hidden="true"></span>关闭</button> <buttontype="submit"class="btnbtn-primary"><spanclass="glyphiconglyphicon-floppy-disk"aria-hidden="true"></span>保存</button> </div> </div> </div> </div> <!--必须的js文件--> <scriptsrc="Content/jquery-1.9.1.min.js"></script> <scriptsrc="Content/bootstrap/js/bootstrap.min.js"></script> <scriptsrc="Content/bootstrap-table/bootstrap-table.min.js"></script> <scriptsrc="Content/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script> <scriptsrc="Content/bootstrap-table/extensions/fixed-column/bootstrap-table-fixed-columns.js"></script> <scripttype="text/javascript"> //页面加载完成之后 vardata=[ {Id:1,Name:'Jim',Age:30,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:2,Name:'Kate',Age:30,School:'光明小学',Address:'深圳市',Remark:'MyNameisJimGreen'}, {Id:3,Name:'Lucy',Age:30,School:'光明小学',Address:'广州天河机场',Remark:'MyNameisJimGreen'}, {Id:4,Name:'Lilei',Age:30,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:5,Name:'Lintao',Age:30,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:6,Name:'Lily',Age:30,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:7,Name:'Hanmeimei',Age:30,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:8,Name:'张三',Age:46,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:9,Name:'李四',Age:23,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:10,Name:'王五',Age:33,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:11,Name:'赵六',Age:22,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:12,Name:'Polly',Age:300,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, {Id:13,Name:'Uncle',Age:50,School:'光明小学',Address:'北京市光明小学旁',Remark:'MyNameisJimGreen'}, ]; varchildData=[ {SourceField:'A',BackField:'BB'}, {SourceField:'CC',BackField:'UU'}, {SourceField:'DD',BackField:'J'}, ]; $(function(){ //表格的初始化 $('#tb_user').bootstrapTable({ data:data,//直接从本地数据初始化表格 method:'get',//请求方式(*) toolbar:'#toolbar',//工具按钮用哪个容器 striped:true,//是否显示行间隔色 cache:false,//是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*) pagination:true,//是否显示分页(*) sortable:false,//是否启用排序 sortOrder:"asc",//排序方式 queryParams:function(params){ returnparams; },//传递参数(*) sidePagination:"client",//分页方式:client客户端分页,server服务端分页(*) pageNumber:1,//初始化加载第一页,默认第一页 pageSize:5,//每页的记录行数(*) pageList:[10,25,50,100],//可供选择的每页的行数(*) search:true,//是否显示表格搜索,此搜索是客户端搜索,不会进服务端,所以,个人感觉意义不大 strictSearch:true, showColumns:true,//是否显示所有的列 showRefresh:true,//是否显示刷新按钮 minimumCountColumns:2,//最少允许的列数 height:400, selectItemName:'parentItem', fixedColumns:true, fixedNumber:6, //注册加载子表的事件。注意下这里的三个参数! onExpandRow:function(index,row,$detail){ InitSubTable(index,row,$detail); }, columns:[{ checkbox:true },{ field:'Name', title:'姓名', width:200 },{ field:'Age', title:'年龄', width:200 },{ field:'School', title:'毕业院校', width:200 },{ field:'Address', title:'家庭住址', width:100 },{ field:'Remark', title:'备注', width:100 }, { field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ field:'Remark', title:'备注', width:100 },{ title:'操作', width:200, formatter:function(value,row,index){//这里的三个参数:value表示当前行当前列的值;row表示当前行的数据;index表示当前行的索引(从0开始)。 varhtml='<buttontype="button"onclick="editModel('+row.Id+')"class="btnbtn-primary"><spanclass="glyphiconglyphicon-pencil"aria-hidden="true"></span>编辑</button> '+ '<buttontype="button"onclick="deleteModel('+row.Id+')"class="btnbtn-danger"><spanclass="glyphiconglyphicon-remove"aria-hidden="true"></span>删除</button>'; returnhtml; } }], onEditableSave:function(field,row,oldValue,$el){ alert("更新保存事件,原始值为"+oldValue); //$.ajax({ //type:"post", //url:"/Editable/Edit", //data:row, //dataType:'JSON', //success:function(data,status){ //if(status=="success"){ //alert('提交数据成功'); //} //}, //error:function(){ //alert('编辑失败'); //}, //complete:function(){ //} //}); } }); //新增事件 $("#btn_add").on('click',function(){ $('#tb_user').bootstrapTable("resetView"); //弹出模态框 $("#myModal").modal(); //给弹出框里面的各个文本框赋值 $("#myModalinput").val(""); $("#myModaltextarea").val(""); }); }); //加载子表 varInitSubTable=function(index,row,$detail){ varparentid=row.MENU_ID; varcur_table=$detail.html('<table></table>').find('table'); //子表的初始化和父表完全相同 $(cur_table).bootstrapTable({ //url:'/api/MenuApi/GetChildrenMenu', data:childData, method:'get', queryParams:{strParentID:parentid}, ajaxOptions:{strParentID:parentid}, clickToSelect:true, uniqueId:"MENU_ID", pageSize:10, pageList:[10,25], selectItemName:'childItem'+index, checkboxHeader:false, columns:[{ checkbox:true },{ field:'SourceField', title:'源端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' },{ field:'BackField', title:'备端字段' }], //无线循环取子表,直到子表里面没有记录 onExpandRow:function(index,row,$Subdetail){ //oInit.InitSubTable(index,row,$Subdetail); } }); }; //编辑事件 vareditModel=function(id){ //根据当前行的id获取当前的行数据 varrow=$("#tb_user").bootstrapTable('getRowByUniqueId',id); //弹出模态框 $("#myModal").modal(); //给弹出框里面的各个文本框赋值 $("#myModalinput[name='Name']").val(row.Name); $("#myModalinput[name='Age']").val(row.Age); $("#myModalinput[name='School']").val(row.School); $("#myModalinput[name='Address']").val(row.Address); $("#myModaltextarea[name='Remark']").val(row.Remark); } //删除事件 vardeleteModel=function(id){ alert("删除id为"+id+"的用户"); } </script> </body> </html> bootstrapTableFixColumns.html
代码释疑:
1、源码各个方法解释
BootstrapTable.prototype.initFixedColumns :当初始化的时候配置了fixedColumns: true时需要执行的冻结列的方法。
BootstrapTable.prototype.initHeader:重写组件的的初始化表头的方法,加入冻结的表头。
BootstrapTable.prototype.initBody:重写组件的初始化表内容的方法,加入冻结的表内容。
BootstrapTable.prototype.resetView:重写“父类”的resetView方法,通过setTimeout去设置冻结的表头和表体的宽度和高度。
BootstrapTable.prototype.fitHeaderColumns:设置冻结列的表头的宽高。
BootstrapTable.prototype.fitBodyColumns :设置冻结列的表体的宽高,以及滚动条和主体表格的滚动条同步。
2、对于上述抛出的ie和谷歌的兼容性问题的解析
查看BootstrapTable.prototype.initBody方法,你会发现里面写有部分注释。
this.$body.find('>tr[data-index]').each(function(){ var$tr=$(this).clone(), $tds=$tr.find('td'); //$tr.html('');这样存在一个兼容性问题,在IE浏览器里面,清空tr,$tds的值也会被清空。 //$tr.html(''); var$newtr=$('<tr></tr>'); $newtr.attr('data-index',$tr.attr('data-index')); $newtr.attr('data-uniqueid',$tr.attr('data-uniqueid')); varend=that.options.fixedNumber; if(rowspan>0){ --end; --rowspan; } for(vari=0;i<end;i++){ $newtr.append($tds.eq(i).clone()); } that.$fixedBodyColumns.append($newtr); if($tds.eq(0).attr('rowspan')){ rowspan=$tds.eq(0).attr('rowspan')-1; } });
这一段做了部分修改,有兴趣可以调适细看。
3、项目中的使用
最近在研究学习abp的相关源码,将bootstrapTable融入abp里面去了,贴出表格冻结的一些效果图。
4、扩展
除此之外,还特意做了右边操作列的冻结。
和左边列的冻结一样,最右边列的冻结也是可以做的,最不同的地方莫过于右边列有一些操作按钮,如果在点击冻结列上面的按钮时触发实际表格的按钮事件是难点。如果有这个需求,可以看看。
bootstrap-table-fixed-columns.js bootstrap-table-fixed-columns.css
需要说明的是,由于时间问题,右侧固定列的代码和上述解决高度的代码并未合并,所以如果你既想要解决冻结列的高度,又想要右侧列的冻结,需要自己花点时间合并下代码。
以上是“JS如何使用Bootstrap Table的冻结列功能彻底解决高度问题”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注恰卡编程网行业资讯频道!
推荐阅读
-
node如何链接多个JS模块
-
前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上
-
换一种思维看待PHP VS Node.js
-
JS遍历数组的三种方法map、forEach与filter实例详解
-
物联网宠儿mqtt.js那些事儿
-
js不跳转传值php
-
Node.js基本内容和知识点
简单的说node.js就是运行在服务端的JavaScript,起初段定位是后端开发语言,由于技术的不够成熟,一般小型项目...
-
为什么选择 Node.js 作为 Web 应用程序?
-
node.js后台快速搭建在阿里云(一)(express篇)
-
基于WebRTC 如何借助Laravel 7和Vue.js创建视频聊天应用
本文介绍了如何借助Vue.js和laravel7创建一个简单的视频聊天应用。如何实现视频聊天我们会用到一个免...