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

Posted 懒得安分

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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: 以上是关于JS组件系列——自己动手扩展BootstrapTable的 冻结列 功能:彻底解决高度问题的主要内容,如果未能解决你的问题,请参考以下文章

自己动手丰衣足食系列の自定义下拉框 vue 组件

JS组件系列——封装自己的JS组件

ASP.NET Core快速入门Middleware管道介绍自己动手构建RequestDelegate管道

ASP.NET Core快速入门Middleware管道介绍自己动手构建RequestDelegate管道

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

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