本文转自:http://pointblankdevelopment.com.au/blog/angularjs-fixed-header-scrollable-table-directive

This post contains a custom AngularJS directive you can use to give your html table a fixed header and footer with a scrollable body, we developed it for a law firm marketing website recently, it uses a pure CSS approach and doesn't touch any of the html tags, leaving the html table completely intact and happily semantic :)

Creating a fixed header scrollable table using purely CSS turns out to be a surprisingly tricky thing to do, in an ideal world I thought it would just be a matter of setting the height of the table body and "overflow:hidden", but there turns out to be a bit more to it than that, especially if you have a table that contains dynamic content because the width of each column in the thead and tbody need to be set in order for it to continue looking like a table and not just a big mess.

In a nutshell the CSS changes that need to happen are:

  • Set the width of each column in the thead and tbody, making sure they match up so the columns aren't wonky
  • Set the thead and tbody to "display:block;"
  • Set the tbody height and "overflow:auto;" to add the scrollbar
  • When there's a scrollbar (when the tbody content overflows it's height), reduce the width of the final column in the tbody by the width of the scrollbar

Here's the solution I came up with:

The fixedHeader AngularJS directive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
 * AngularJS fixed header scrollable table directive
 * @author Jason Watmore <jason@pointblankdevelopment.com.au> (http://jasonwatmore.com)
 * @version 1.2.0
 */
(function () {
    angular
        .module('anguFixedHeaderTable', [])
        .directive('fixedHeader', fixedHeader);
 
    fixedHeader.$inject = ['$timeout'];
 
    function fixedHeader($timeout) {
        return {
            restrict: 'A',
            link: link
        };
 
        function link($scope, $elem, $attrs, $ctrl) {
            var elem = $elem[0];
 
            // wait for data to load and then transform the table
            $scope.$watch(tableDataLoaded, function(isTableDataLoaded) {
                if (isTableDataLoaded) {
                    transformTable();
                }
            });
 
            function tableDataLoaded() {
                // first cell in the tbody exists when data is loaded but doesn't have a width
                // until after the table is transformed
                var firstCell = elem.querySelector('tbody tr:first-child td:first-child');
                return firstCell && !firstCell.style.width;
            }
 
            function transformTable() {
                // reset display styles so column widths are correct when measured below
                angular.element(elem.querySelectorAll('thead, tbody, tfoot')).css('display', '');
 
                // wrap in $timeout to give table a chance to finish rendering
                $timeout(function () {
                    // set widths of columns
                    angular.forEach(elem.querySelectorAll('tr:first-child th'), function (thElem, i) {
 
                        var tdElems = elem.querySelector('tbody tr:first-child td:nth-child(' + (i + 1) + ')');
                        var tfElems = elem.querySelector('tfoot tr:first-child td:nth-child(' + (i + 1) + ')');
 
                        var columnWidth = tdElems ? tdElems.offsetWidth : thElem.offsetWidth;
                        if (tdElems) {
                            tdElems.style.width = columnWidth + 'px';
                        }
                        if (thElem) {
                            thElem.style.width = columnWidth + 'px';
                        }
                        if (tfElems) {
                            tfElems.style.width = columnWidth + 'px';
                        }
                    });
 
                    // set css styles on thead and tbody
                    angular.element(elem.querySelectorAll('thead, tfoot')).css('display', 'block');
 
                    angular.element(elem.querySelectorAll('tbody')).css({
                        'display': 'block',
                        'height': $attrs.tableHeight || 'inherit',
                        'overflow': 'auto'
                    });
 
                    // reduce width of last column by width of scrollbar
                    var tbody = elem.querySelector('tbody');
                    var scrollBarWidth = tbody.offsetWidth - tbody.clientWidth;
                    if (scrollBarWidth > 0) {
                        // for some reason trimming the width by 2px lines everything up better
                        scrollBarWidth -= 2;
                        var lastColumn = elem.querySelector('tbody tr:first-child td:last-child');
                        lastColumn.style.width = (lastColumn.offsetWidth - scrollBarWidth) + 'px';
                    }
                });
            }
        }
    }
})();

Sample HTML that uses the fixed-header directive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<table class="table table-bordered" fixed-header>
    <thead>
        <tr>
            <th>Header 1</th>
            <th>Header 2</th>
            <th>Header 3</th>
            <th>Header 4</th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="item in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]">
            <td>Row {{item}} Col 1</td>
            <td>Row {{item}} Col 2</td>
            <td>Row {{item}} Col 3</td>
            <td>Row {{item}} Col 4</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td>Footer 1</td>
            <td>Footer 2</td>
            <td>Footer 3</td>
            <td>Footer 4</td>
        </tr>
    </tfoot>
</table>

The default height of the table body is 400px, to change this add a table-height attribute to the table element eg: table-height="500px".

UPDATE 08/10/2014: Added support for fixed footer (tfoot) element.

[转]AngularJS fixed header scrollable table directive的更多相关文章

  1. AngularJS:何时应该使用Directive、Controller、Service?

    AngularJS:何时应该使用Directive.Controller.Service? (这篇文章你们一定要看,尤其初学的人,好吗亲?) 大漠穷秋 译 AngularJS是一款非常强大的前端MVC ...

  2. Part 10 AngularJS sort rows by table header

    Here is what we want to do 1. The data should be sorted when the table column header is clicked 2. T ...

  3. AngularJS Best Practices: ng-include vs directive

    For building an HTML template with reusable widgets like header, sidebar, footer, etc. Basically the ...

  4. AngularJS:何时应该使用Directive、Controller、Service?【新手必看】

    (这篇文章你们一定要看,尤其初学的人,好吗亲?) 大漠穷秋 译 AngularJS是一款非常强大的前端MVC框架.同时,它也引入了相当多的概念,这些概念我们可能不是太熟悉.(译者注:老外真谦虚,我大天 ...

  5. AngularJs学习——何时应该使用Directive、Controller、Service?

    翻译:大漠穷秋 原文链接:http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/ 一.简述 A ...

  6. [转]AngularJS:何时应该使用Directive、Controller、Service?

    AngularJS是一款非常强大的前端MVC框架.同时,它也引入了相当多的概念,这些概念我们可能不是太熟悉.(译者注:老外真谦虚,我大天朝的码农对这些概念那是相当熟悉啊!)这些概念有: Directi ...

  7. [AngularJS] Build Your Own ng-controller Directive

    /** * Created by Answer1215 on 12/21/2014. */ angular.module('app', []) .controller('FirstCtrl' , fu ...

  8. angularJS+requireJS实现controller及directive的按需加载

    最近因为项目的比较大,需要加载的js文件较多,为了提高首屏页面的加载速度,需要对js文件进行按需加载,然后网上参考了一些资料,自己也深入研究一番之后,实现了按需加载控制器js文件及指令js文件的效果: ...

  9. AngularJS之Directive,scope,$parse

    AngularJS内幕详解之 Directive AngularJS内幕详解之 Scope AngularJS的指令(Directive) compile和link的区别及使用示例 浅谈Angular ...

随机推荐

  1. Spring4学习笔记 - SpEL表达式

  2. sass的四种css编译风格

    sass的改变编译风格的语句: sass style expanded assets/sass/style1.sass:assets/css/style1.css 1.nested:默认 #main ...

  3. 开发中可能会用到的几个 jQuery 小提示和技巧

    今天,我们将分享一些很有用的技巧和窍门给 jQuery 开发人员.jQuery 是最好的 JavaScript 库之一,用于简化动画,事件处理,支持 Ajax 和 HTML 的客户端脚本.网络中有大量 ...

  4. CSS实现DIV水平 垂直居中-1

    水平大家都知道,一般固定宽度给个margin:0 auto:就可以了.下面实现水平垂直都居中 HTML <div class="parent"> </div> ...

  5. babel 无法解析jsx (webpack react )

    webpack.config.js的配置如下图: 报错: 修改webpack.config.js文件,如下即可:

  6. go语言最新版本 下载地址

    国内官方网站无法打开.放在了百度云中,定期会更新: 链接:http://pan.baidu.com/s/1dD59duh 密码:46ek 备用地址:http://pan.baidu.com/s/1hq ...

  7. git怎么创建本地版本仓库

    git怎么创建本地版本仓库 安装git我就不用说了吧!下载地址:https://github.com/msysgit/msysgit/releases/download/Git-1.9.4-previ ...

  8. SAP 中的用户类型

    在使用 SU01 维护用户时,在登录数据选项卡中会要求选择用户类型,一般我们都选择第一项 Dialog,但是其他几个选项有什么作用呢?下面我们就一一解释. 对话用户顾名思义,就是需要通过 SAP GU ...

  9. 360和QQ大战之我见

    腾讯最大的产品QQ,占有了全国6亿活跃用户.在中国互联网拥有不可多得的主导权. 360的产品想了一个办法,以安全为名,做了一个QQ保镖,那么他的步骤如下: 1.我保护你嘛.只要QQ启动,保镖就跟着启动 ...

  10. Android 抽屉效果的导航菜单实现

    Android 抽屉效果的导航菜单实现 抽屉效果的导航菜单 看了很多应用,觉得这种侧滑的抽屉效果的菜单很好. 不用切换到另一个页面,也不用去按菜单的硬件按钮,直接在界面上一个按钮点击,菜单就滑出来,而 ...