本文转自: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. Webform(文件上传)

    1.HTML编码: <input type="file" /> 2.控件:FileUpload 它是用来选择要上传的文件,还需要一个按钮来将选中的文件上传到服务器上 s ...

  2. [AngularJS] 使用AngularAMD动态加载Service

    [AngularJS] 使用AngularAMD动态加载Service 前言 「使用AngularAMD动态加载Controller」:这篇文章里介绍如何使用AngularAMD来动态加载Contro ...

  3. angular学习的一些小笔记(中)之ng-init

    ng-init是给angular执行给定的表达式,初始化变量的值 <!DOCTYPE html> <html> <head> <meta charset='U ...

  4. 避开unity的坑(转摘)

    避开unity的坑(转摘) 以下总结一部分来自经验之谈,一部分来自其他人的分享.总的来讲,unity开发原型和效果.验证想法,确实是无比便利.可能一个月就把核心玩法做得差不多.强大的编辑器功能让我们也 ...

  5. 批量另存mxd

    在GIS数据处理中,批量操作是经常遇到的问题,Python脚本是解决问题的最好方法.现在需要将arcgis10.1的mxd另存为10.0,不仅数量较多,而且每个mxd要素和标注非常多,手动来操作确实慢 ...

  6. 给view添加类似系统上拉快捷菜单的手势

    iOS7以后从屏幕最下方上划会滑出快捷菜单,感觉这个效果不错,就想做个类似的效果,这个东西技术含量不高,每次都写一遍的话就太浪费时间了,所以就把它写成了一个分类,用起来会方便一点. demo地址:ht ...

  7. ASP和ASP.NET发送邮件笔记

    这两天因公司网站邮件发不出去,然后研究了在asp网站发送邮件和在asp.net网站发送邮件的代码,把碰到的问题这里记录一下. 1.先说在asp.net中发送邮件吧, 刚开始只有126邮箱可以发出邮件, ...

  8. 【iOS】使用CoreText实现图文混排

    iOS没有现成的支持图文混排的控件,而要用多个基础控件组合拼成图文混排这样复杂的排版,是件很苦逼的事情.对此的解决方案有使用CoreText进行绘制,或者使用TextKit.本文主要讲解对于CoreT ...

  9. c中的基本运算

    一. 算术运算 C语言一共有34种运算符,包括了常见的加减乘除运算 1. 加法运算+ l 除开能做加法运算,还能表示正号:+5.+90 2. 减法运算- l 除开能做减法运算,还能表示符号:-10.- ...

  10. GET/POST请求(NSURLSession)

    步骤 使用NSURLSession创建task,然后执行task Task a.NSURLSessionTask是一个抽象类,本身不能使用,只能使用它的子类 b.NSURLSessionDataTas ...