JS组件系列——自己动手扩展BootstrapTable的 冻结列 功能:彻底解决高度问题

前言:一年前,博主分享过一篇关于bootstrapTable组件冻结列的解决方案  JS组件系列——Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案 ,通过该篇,确实可以实现bootstrapTable的冻结列效果,并且可以兼容ie浏览器。这一年的时间,不断有园友以及群里面的朋友问过我关于固定高度之后,冻结列页面效果不能对齐的问题,奈何博主太忙,一直没有抽空将这个问题优化。最近项目里面也不断有人提过这个bug,这下子不能再推了,必须要直面“惨淡的bug”,于是昨天利用一天的时间将原来的扩展做了一下修改,能够完美解决固定高度之后冻结列的问题,并且,博主还加了一些特性,比如右侧列的冻结、冻结列的选中等等,有需要的朋友可以捧个场。相信通过此篇,老板再也不用担心我的冻结列不能固定高度了~~

本文原创地址:http://www.cnblogs.com/landeanfen/p/7095414.html

一、问题追踪

记得在之前的那篇里面介绍过,bootstrapTable组件自带的冻结列扩展,不能兼容ie浏览器,即使最新版本的ie也会无法使用,这是一般的系统不能忍受的,所以在那篇里面给出过解决方案,但并未分析ie浏览器不能兼容的原因,昨天博主花了点时间特意调试了下源码,原来在ie里面,使用jquery的clone()方法和谷歌等浏览器有所区别。为了展示这个区别,这里先抛个砖。比如有如下代码:

<table id="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>

<script type="text/javascript">
    var $tr = $(‘#tbtest tr: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 ($) {
    ‘use strict‘;

    $.extend($.fn.bootstrapTable.defaults, {
        fixedColumns: false,
        fixedNumber: 1
    });

    var BootstrapTable = $.fn.bootstrapTable.Constructor,
        _initHeader = BootstrapTable.prototype.initHeader,
        _initBody = BootstrapTable.prototype.initBody,
        _resetView = BootstrapTable.prototype.resetView;

    BootstrapTable.prototype.initFixedColumns = function () {
        this.$fixedHeader = $([
            ‘<div class="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 = $([
            ‘<div class="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();

        var that = 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;
        }

        var that = 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‘));
            var end = that.options.fixedNumber;
            if (rowspan > 0) {
                --end;
                --rowspan;
            }
            for (var i = 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 () {
        var that = 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) {
                return false;
            }

            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 () {
        var that = this,
            top = -(parseInt(this.$el.css(‘margin-top‘))),
            // the fixed height should reduce the scorll-x height
            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);
            var thattds = 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 () {
            var index = $(this).data(‘index‘);
            that.$fixedBody.find(‘tr[data-index="‘ + index + ‘"]‘).addClass(‘hover‘);
        }, function () {
            var index = $(this).data(‘index‘);
            that.$fixedBody.find(‘tr[data-index="‘ + index + ‘"]‘).removeClass(‘hover‘);
        });
        this.$fixedBody.find(‘tr[data-index]‘).off(‘hover‘).hover(function () {
            var index = $(this).data(‘index‘);
            that.$body.find(‘tr[data-index="‘ + index + ‘"]‘).addClass(‘hover‘);
        }, function () {
            var index = $(this).data(‘index‘);
            that.$body.find(‘> tr[data-index="‘ + index + ‘"]‘).removeClass(‘hover‘);
        });
    };

})(jQuery);

bootstrap-table-fixed-columns.js

.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: 1px solid #ddd;
    }

        .fixed-table-header-columns .table.table-no-bordered,
        .fixed-table-body-columns .table.table-no-bordered {
            border-right: 1px solid transparent;
        }

    .fixed-table-body-columns table {
        position: absolute;
        animation: none;
    }

.bootstrap-table .table-hover > tbody > tr.hover > td {
    background-color: #f5f5f5;
}

bootstrap-table-fixed-columns.css

如何使用呢?这里博主单独搞了一个静态的html测试页,还是贴出来供大家参考。

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <!--必须的css引用-->
    <link href="Content/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
    <link href="Content/bootstrap-table/bootstrap-table.min.css"  rel="stylesheet" />
<link href="Content/bootstrap-table/extensions/fixed-column/bootstrap-table-fixed-columns.css"  rel="stylesheet" />
</head>
<body>
    <div class="panel-body" style="padding-bottom:0px;">
        <!--<div class="panel panel-default">
            <div class="panel-heading">查询条件</div>
            <div class="panel-body">
                <form id="formSearch" class="form-horizontal">
                    <div class="form-group" style="margin-top:15px">
                        <label class="control-label col-sm-1" for="name">员工姓名</label>
                        <div class="col-sm-3">
                            <input type="text" class="form-control" id="name">
                        </div>
                        <label class="control-label col-sm-1" for="address">家庭住址</label>
                        <div class="col-sm-3">
                            <input type="text" class="form-control" id="address">
                        </div>
                        <div class="col-sm-4" style="text-align:left;">
                            <button type="button" style="margin-left:50px" id="btn_query" class="btn btn-primary">查询</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>-->

        <div id="toolbar" class="btn-group">
            <button id="btn_add" type="button" class="btn btn-success">
                <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增
            </button>
        </div>
        <table id="tb_user"></table>
    </div>

    <!--新增或者编辑的弹出框-->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <h4 class="modal-title" id="myModalLabel">操作</h4>
                </div>
                <div class="modal-body">
                    <div class="row" style="padding:10px;">
                        <label class="control-label col-xs-2">姓名</label>
                        <div class="col-xs-10">
                            <input type="text" name="Name" class="form-control" placeholder="姓名">
                        </div>
                    </div>
                    <div class="row" style="padding:10px;">
                        <label class="control-label col-xs-2">年龄</label>
                        <div class="col-xs-10">
                            <input type="text" name="Age" class="form-control" placeholder="年龄">
                        </div>
                    </div>
                    <div class="row" style="padding:10px;">
                        <label class="control-label col-xs-2">学校</label>
                        <div class="col-xs-10">
                            <input type="text" name="School" class="form-control" placeholder="学校">
                        </div>
                    </div>
                    <div class="row" style="padding:10px;">
                        <label class="control-label col-xs-2">家庭住址</label>
                        <div class="col-xs-10">
                            <input type="text" name="Address" class="form-control" placeholder="学校">
                        </div>
                    </div>
                    <div class="row" style="padding:10px;">
                        <label class="control-label col-xs-2">备注</label>
                        <div class="col-xs-10">
                            <textarea class="form-control" placeholder="备注" name="Remark"></textarea>
                        </div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>关闭</button>
                    <button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button>
                </div>

            </div>
        </div>
    </div>

        <!--必须的js文件-->
        <script src="Content/jquery-1.9.1.min.js"></script>
        <script src="Content/bootstrap/js/bootstrap.min.js"></script>
        <script src="Content/bootstrap-table/bootstrap-table.min.js"></script>
        <script src="Content/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
<script src="Content/bootstrap-table/extensions/fixed-column/bootstrap-table-fixed-columns.js"></script>
        <script type="text/javascript">
            //页面加载完成之后
            var data = [
                { Id: 1, Name: ‘Jim‘, Age: 30, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 2, Name: ‘Kate‘, Age: 30, School: ‘光明小学‘, Address: ‘深圳市‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 3, Name: ‘Lucy‘, Age: 30, School: ‘光明小学‘, Address: ‘广州天河机场‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 4, Name: ‘Lilei‘, Age: 30, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 5, Name: ‘Lintao‘, Age: 30, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 6, Name: ‘Lily‘, Age: 30, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 7, Name: ‘Hanmeimei‘, Age: 30, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 8, Name: ‘张三‘, Age: 46, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 9, Name: ‘李四‘, Age: 23, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 10, Name: ‘王五‘, Age: 33, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 11, Name: ‘赵六‘, Age: 22, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 12, Name: ‘Polly‘, Age: 300, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
                { Id: 13, Name: ‘Uncle‘, Age: 50, School: ‘光明小学‘, Address: ‘北京市光明小学旁‘, Remark: ‘My Name is Jim Green‘ },
            ];
            var childData = [
                { 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) {
                        return params;
                    },                                  //传递参数(*)
                    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开始)。
                            var html = ‘<button type="button" onclick="editModel(‘+row.Id+‘)" class="btn btn-primary"><span class="glyphicon glyphicon-pencil" aria- hidden="true" ></span >编辑</button >&nbsp;&nbsp;‘ +
                                       ‘<button type="button" onclick="deleteModel(‘ + row.Id + ‘)" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria- hidden="true" ></span >删除</button >‘;
                            return html;
                        }
                    }],
                    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();
                    //给弹出框里面的各个文本框赋值
                    $("#myModal input").val("");
                    $("#myModal textarea").val("");
                });

            });

            //加载子表
            var InitSubTable = function (index, row, $detail) {
                var parentid = row.MENU_ID;
                var cur_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);
                    }
                });
            };

            //编辑事件
            var editModel = function (id) {
                //根据当前行的id获取当前的行数据
                var row = $("#tb_user").bootstrapTable(‘getRowByUniqueId‘, id);
                //弹出模态框
                $("#myModal").modal();
                //给弹出框里面的各个文本框赋值
                $("#myModal input[name=‘Name‘]").val(row.Name);
                $("#myModal input[name=‘Age‘]").val(row.Age);
                $("#myModal input[name=‘School‘]").val(row.School);
                $("#myModal input[name=‘Address‘]").val(row.Address);
                $("#myModal textarea[name=‘Remark‘]").val(row.Remark);
            }

            //删除事件
            var deleteModel = 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‘));
            var end = that.options.fixedNumber;
            if (rowspan > 0) {
                --end;
                --rowspan;
            }
            for (var i = 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、扩展

除此之外,还特意做了右边操作列的冻结。

和左边列的冻结一样,最右边列的冻结也是可以做的,最不同的地方莫过于右边列有一些操作按钮,如果在点击冻结列上面的按钮时触发实际表格的按钮事件是难点。如果有这个需求,可以看看。

(function ($) {
    ‘use strict‘;

    $.extend($.fn.bootstrapTable.defaults, {
        leftFixedColumns: false,
        leftFixedNumber: 1,
        rightFixedColumns: false,
        rightFixedNumber: 1
    });

    var BootstrapTable = $.fn.bootstrapTable.Constructor,
        _initHeader = BootstrapTable.prototype.initHeader,
        _initBody = BootstrapTable.prototype.initBody,
        _resetView = BootstrapTable.prototype.resetView;

    BootstrapTable.prototype.initFixedColumns = function () {
        this.timeoutHeaderColumns_ = 0;
        this.timeoutBodyColumns_ = 0;
        if (this.options.leftFixedColumns) {
            this.$fixedBody = $([
                ‘<div class="fixed-table-column" style="position: absolute; background-color: #fff; border-right:1px solid #ddd;">‘,
                ‘<table>‘,
                ‘<thead></thead>‘,
                ‘<tbody></tbody>‘,
                ‘</table>‘,
                ‘</div>‘].join(‘‘));

            this.$fixedBody.find(‘table‘).attr(‘class‘, this.$el.attr(‘class‘));
            this.$fixedHeaderColumns = this.$fixedBody.find(‘thead‘);
            this.$fixedBodyColumns = this.$fixedBody.find(‘tbody‘);
            this.$tableBody.before(this.$fixedBody);
        }
        if (this.options.rightFixedColumns) {
            this.$rightfixedBody = $([
                ‘<div class="fixed-table-column" style="position: absolute;right:0px; background-color: #fff; border-right:1px solid #ddd;">‘,
                ‘<table>‘,
                ‘<thead></thead>‘,
                ‘<tbody></tbody>‘,
                ‘</table>‘,
                ‘</div>‘].join(‘‘));

            this.$rightfixedBody.find(‘table‘).attr(‘class‘, this.$el.attr(‘class‘));
            this.$rightfixedHeaderColumns = this.$rightfixedBody.find(‘thead‘);
            this.$rightfixedBodyColumns = this.$rightfixedBody.find(‘tbody‘);
            this.$tableBody.before(this.$rightfixedBody);
        }
    };

    BootstrapTable.prototype.initHeader = function () {
        _initHeader.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.leftFixedColumns && !this.options.rightFixedColumns){
            return;
        }
        this.initFixedColumns();

        var $tr = this.$header.find(‘tr:eq(0)‘).clone(),
            $ths = $tr.clone().find(‘th‘);

        $tr.html(‘‘);
        //左边列冻结
        if (this.options.leftFixedColumns) {
            for (var i = 0; i < this.options.leftFixedNumber; i++) {
                $tr.append($ths.eq(i).clone());
            }
            this.$fixedHeaderColumns.html(‘‘).append($tr);
        }

        //右边列冻结
        if (this.options.rightFixedColumns) {
            for (var i = 0; i < this.options.rightFixedNumber; i++) {
                $tr.append($ths.eq($ths.length - this.options.rightFixedNumber + i).clone());
            }
            this.$rightfixedHeaderColumns.html(‘‘).append($tr);
        }
    };

    BootstrapTable.prototype.initBody = function () {
        _initBody.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.leftFixedColumns && !this.options.rightFixedColumns) {
            return;
        }

        var that = this;
        if (this.options.leftFixedColumns) {
            this.$fixedBodyColumns.html(‘‘);
            this.$body.find(‘> tr[data-index]‘).each(function () {
                var $tr = $(this).clone(),
                    $tds = $tr.clone().find(‘td‘);

                $tr.html(‘‘);
                for (var i = 0; i < that.options.leftFixedNumber; i++) {
                    $tr.append($tds.eq(i).clone());
                }
                that.$fixedBodyColumns.append($tr);
            });
        }
        if (this.options.rightFixedColumns) {
            this.$rightfixedBodyColumns.html(‘‘);
            this.$body.find(‘> tr[data-index]‘).each(function () {
                var $tr = $(this).clone(),
                    $tds = $tr.clone().find(‘td‘);

                $tr.html(‘‘);
                for (var i = 0; i < that.options.rightFixedNumber; i++) {
                    var indexTd = $tds.length - that.options.rightFixedNumber + i;
                    var oldTd = $tds.eq(indexTd);
                    var fixTd = oldTd.clone();
                    var buttons = fixTd.find(‘button‘);
                    //事件转移:冻结列里面的事件转移到实际按钮的事件
                    buttons.each(function (key, item) {
                        $(item).click(function () {
                            that.$body.find("tr[data-index=" + $tr.attr(‘data-index‘) + "] td:eq(" + indexTd + ") button:eq(" + key + ")").click();
                        });
                    });
                    $tr.append(fixTd);
                }
                that.$rightfixedBodyColumns.append($tr);
            });
        }
    };

    BootstrapTable.prototype.resetView = function () {
        _resetView.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.leftFixedColumns && !this.options.rightFixedColumns) {
            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 () {
        var that = this,
            visibleFields = this.getVisibleFields(),
            headerWidth = 0;
        if (that.options.leftFixedColumns) {
            this.$body.find(‘tr:first-child:not(.no-records-found) > *‘).each(function (i) {
                var $this = $(this),
                    index = i;

                if (i >= that.options.leftFixedNumber) {
                    return false;
                }

                if (that.options.detailView && !that.options.cardView) {
                    index = i - 1;
                }

                that.$fixedBody.find(‘thead th[data-field="‘ + visibleFields[index] + ‘"]‘)
                    .find(‘.fht-cell‘).width($this.innerWidth() - 1);
                headerWidth += $this.outerWidth();
            });
            this.$fixedBody.width(headerWidth - 1).show();
        }
        if (that.options.rightFixedColumns) {
            this.$body.find(‘tr:first-child:not(.no-records-found) > *‘).each(function (i) {
                var $this = $(this),
                    index = i;

                if (i >= visibleFields.length - that.options.rightFixedNumber) {
                    //return false;

                    //if (that.options.detailView && !that.options.cardView) {
                    //    index = i - 1;
                    //}
                    debugger;
                    that.$rightfixedBody.find(‘thead th[data-field="‘ + visibleFields[index] + ‘"]‘)
                        .find(‘.fht-cell‘).width($this.innerWidth() - 1);
                    headerWidth += $this.outerWidth();
                }
            });
            this.$rightfixedBody.width(headerWidth - 1).show();
        }
    };

    BootstrapTable.prototype.fitBodyColumns = function () {
        var that = this,
            top = -(parseInt(this.$el.css(‘margin-top‘)) - 2),
            height = this.$tableBody.height() - 2;

        if (that.options.leftFixedColumns) {
            if (!this.$body.find(‘> tr[data-index]‘).length) {
                this.$fixedBody.hide();
                return;
            }

            this.$body.find(‘> tr‘).each(function (i) {
                that.$fixedBody.find(‘tbody tr:eq(‘ + i + ‘)‘).height($(this).height());
            });

            //// events
            this.$tableBody.on(‘scroll‘, function () {
                that.$fixedBody.find(‘table‘).css(‘top‘, -$(this).scrollTop());
            });
            this.$body.find(‘> tr[data-index]‘).off(‘hover‘).hover(function () {
                var index = $(this).data(‘index‘);
                that.$fixedBody.find(‘tr[data-index="‘ + index + ‘"]‘).addClass(‘hover‘);
            }, function () {
                var index = $(this).data(‘index‘);
                that.$fixedBody.find(‘tr[data-index="‘ + index + ‘"]‘).removeClass(‘hover‘);
            });
            this.$fixedBody.find(‘tr[data-index]‘).off(‘hover‘).hover(function () {
                var index = $(this).data(‘index‘);
                that.$body.find(‘tr[data-index="‘ + index + ‘"]‘).addClass(‘hover‘);
            }, function () {
                var index = $(this).data(‘index‘);
                that.$body.find(‘> tr[data-index="‘ + index + ‘"]‘).removeClass(‘hover‘);
            });
        }
        if (that.options.rightFixedColumns) {
            if (!this.$body.find(‘> tr[data-index]‘).length) {
                this.$rightfixedBody.hide();
                return;
            }

            this.$body.find(‘> tr‘).each(function (i) {
                that.$rightfixedBody.find(‘tbody tr:eq(‘ + i + ‘)‘).height($(this).height());
            });

            //// events
            this.$tableBody.on(‘scroll‘, function () {
                that.$rightfixedBody.find(‘table‘).css(‘top‘, -$(this).scrollTop());
            });
            this.$body.find(‘> tr[data-index]‘).off(‘hover‘).hover(function () {
                var index = $(this).data(‘index‘);
                that.$rightfixedBody.find(‘tr[data-index="‘ + index + ‘"]‘).addClass(‘hover‘);
            }, function () {
                var index = $(this).data(‘index‘);
                that.$rightfixedBody.find(‘tr[data-index="‘ + index + ‘"]‘).removeClass(‘hover‘);
            });
            this.$rightfixedBody.find(‘tr[data-index]‘).off(‘hover‘).hover(function () {
                var index = $(this).data(‘index‘);
                that.$body.find(‘tr[data-index="‘ + index + ‘"]‘).addClass(‘hover‘);
            }, function () {
                var index = $(this).data(‘index‘);
                that.$body.find(‘> tr[data-index="‘ + index + ‘"]‘).removeClass(‘hover‘);
            });
        }
    };

})(jQuery);

bootstrap-table-fixed-columns.js

.fixed-table-container thead th .th-inner, .fixed-table-container tbody td .th-inner {
    line-height: 18px;
}

.fixed-table-pagination .pagination a {
    padding: 5px 10px;
}

.fixed-table-toolbar .bars, .fixed-table-toolbar .search, .fixed-table-toolbar .columns {
    margin-top: 5px;
    margin-bottom: 5px;
}

bootstrap-table-fixed-columns.css

需要说明的是,由于时间问题,右侧固定列的代码和上述解决高度的代码并未合并,所以如果你既想要解决冻结列的高度,又想要右侧列的冻结,需要自己花点时间合并下代码。

四、总结

至此本文就结束了,关于冻结列的课题终于可以暂时告一段落了,这个问题博主纠结了很久,总算是解决了。如果你觉得本文能够帮助你,可以右边随意 打赏 博主,打赏后可以获得博主永久免费的技术支持。

本文原创出处:http://www.cnblogs.com/landeanfen/

欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利

时间: 2024-10-27 12:03:44

JS组件系列——自己动手扩展BootstrapTable的 冻结列 功能:彻底解决高度问题的相关文章

JS组件系列——BootstrapTable 行内编辑解决方案:x-editable

前言:之前介绍bootstrapTable组件的时候有提到它的行内编辑功能,只不过为了展示功能,将此一笔带过了,罪过罪过!最近项目里面还是打算将行内编辑用起来,于是再次研究了下x-editable组件,遇到过一些坑,再此做个采坑记录吧!想要了解bootstrapTable的园友可以移步 JS组件系列——表格组件神器:bootstrap table. 本文原创地址:http://www.cnblogs.com/landeanfen/p/5821192.html 一.x-editable组件介绍 x

JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查

前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来.最近项目打算正式将ko用起来,于是乎对ko和bootstraptable做了一些封装,在此分享出来供园友们参考.封装思路参考博客园大神萧秦,如果园友们有更好的方法,欢迎讨论. KnockoutJS系列文章: JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一) JS组件

JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(二)

前言:上篇 JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一) 介绍了下knockout.js的一些基础用法,由于篇幅的关系,所以只能分成两篇,望见谅!昨天就觉得应该快点完成下篇,要不然有点标题党的感觉,思及此,博主心有不安,于是加班赶出了下篇.如果你也打算用ko去做项目,且看看吧! 一.效果预览 其实也没啥效果,就是简单的增删改查,重点还是在代码上面,使用ko能够大量节省界面DOM数据绑定的操作.下面是整个整个增删改查逻辑的js代码: 页面效果: 二.

JS组件系列——又一款MVVM组件:Vue(二:构建自己的Vue组件)

阅读目录 一.为什么组件很重要 二.Vue里面的组件基础知识 1.组件的概念 2.组件原理 3.组件使用 三.封装自己的Component 1.使用Component封装bootstrapTable 2.封装select 3.查看其他Vue框架源码 四.总结 正文 前言:转眼距离上篇 JS组件系列--又一款MVVM组件:Vue(一:30分钟搞定前端增删改查) 已有好几个月了,今天打算将它捡起来,发现好久不用,Vue相关技术点都生疏不少.经过这几个月的时间,Vue的发展也是异常迅猛,不过这好像和博

JS组件系列——表格组件神器:bootstrap table(三:终结篇,最后的干货福利)

前言:前面介绍了两篇关于bootstrap table的基础用法,这章我们继续来看看它比较常用的一些功能,来个终结篇吧,毛爷爷告诉我们做事要有始有终~~bootstrap table这东西要想所有功能覆盖似乎不太现实,博主挑选了一些自认为比较常用的功能在此分享给各位园友.源码也在这篇统一给出.好了,不多说废话,开始我们的干货之旅吧. bootstrap table系列: JS组件系列——表格组件神器:bootstrap table JS组件系列——表格组件神器:bootstrap table(二

JS组件系列——表格组件神器:bootstrap table(二:父子表和行列调序)

原文:JS组件系列--表格组件神器:bootstrap table(二:父子表和行列调序) 前言:上篇 JS组件系列——表格组件神器:bootstrap table 简单介绍了下Bootstrap Table的基础用法,没想到讨论还挺热烈的.有园友在评论中提到了父子表的用法,今天就结合Bootstrap table的父子表和行列调序的用法再来介绍下它稍微高级点的用法. bootstrap table系列: JS组件系列——表格组件神器:bootstrap table JS组件系列——表格组件神器

JS组件系列——Bootstrap Table 表格行拖拽

原文:JS组件系列--Bootstrap Table 表格行拖拽 前言:之前一直在研究DDD相关知识,好久没更新JS系列文章了.这两天做了一个简单的业务需求,觉得效果还可以,今天在这里分享给大家,欢迎拍砖~~ 一.业务需求及实现效果 项目涉及到订单模块,那天突然接到一个需求,说是两种不同状态的订单之间要实现插单的效果,页面上呈现方式是:左右两个Table,左边Table里面是状态为1的订单,右边Table里面是状态为2订单,左边Table里面的行数据拖动到右边Table里面指定行的位置,拖动完成

JS组件系列——Bootstrap文件上传组件:bootstrap fileinput

原文:JS组件系列--Bootstrap文件上传组件:bootstrap fileinput 前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签,效果不忍直视,于是博主下定决心要找一个好看的上传组件换掉它.既然bootstrap开源,那么社区肯定有很多关于它的组件,肯定也有这种常见的上传组件吧.经过一番查找,功夫不负有心人,还是被博主找到了这个组件:

JS组件系列——Bootstrap寒冬暖身篇:弹出框和提示框效果以及代码展示

原文:JS组件系列--Bootstrap寒冬暖身篇:弹出框和提示框效果以及代码展示 前言:对于Web开发人员,弹出框和提示框的使用肯定不会陌生,比如常见的表格新增和编辑功能,一般常见的主要有两种处理方式:行内编辑和弹出框编辑.在增加用户体验方面,弹出框和提示框起着重要的作用,如果你的系统有一个友好的弹出提示框,自然能给用户很好的页面体验.前面几章介绍了bootstrap的几个常用组件,这章来看看bootstrap里面弹出框和提示框的处理.总的来说,弹出提示主要分为三种:弹出框.确定取消提示框.信