實現雙向固定表頭的表格

實現目標

  1. 包含橫向表頭和縱向表頭的表格
  2. 在橫向滾動軸滾動時,橫向表頭隨之滾動,縱向表頭固定不變
  3. 在縱向滾動軸滾動時,縱向表頭隨之滾動,橫向表頭固定不變
  4. 效果圖

    這裏寫圖片描述

使用資源

  1. angularjs
  2. jquery
  3. sublime

實現

1.基本結構
由於滾動軸會佔據一定位置所以在橫向表頭和縱向表頭計算時需要排除掉滾動條的寬動,如果希望使用插件scrollbar.js。

scrollbar.js是由jQuery寫成的自定義樣式的滾動條插件,特點是不會佔用位置,並且在鼠標移出時隱藏顯示,移入時正常顯示,具有較好的瀏覽器兼容性。如果在angular中使用的話,可以通過directive對其進行封裝。

這裏寫圖片描述

由圖中展示,整體結構分爲四塊,固定標題,橫向表頭,縱向表頭和表格內容,滾動條只存在於表格內容塊中。使用CSS的絕對定位便可達到如圖的效果。

html代碼,如下所示

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>雙表頭固定表格實現</title>
    <link rel="stylesheet" type="text/css" href="fixHeaderTable.css">
    <link rel="stylesheet" type="text/css" href="base.css">
</head>

<body ng-app="app">
    <div class="fixed-header-table" ng-controller="tableController">
        <div class="first-row">
            <!-- 固定標題 -->
            <div class="fixed-title">
            </div>
            <!-- 橫向表頭 -->
            <div class="fixed-row-header">
                <!-- 循環生成橫向表頭的標題 -->
                <ul id="rowHeader" class="row-header-list">
                    <li class="row-header-item" ng-repeat="row in mockData.rowHeader">
                        <span class="row-header-text">{{row}}</span>
                    </li>
                </ul>
            </div>
        </div>
        <div class="second-row">
            <!-- 縱向表頭 -->
            <div class="fixed-col-header">
                <!-- 循環生成總向表頭的標題 -->
                <ul id="colHeader" class="col-header-list">
                    <li class="col-header-item" ng-repeat="col in mockData.colHeader">
                        <span class="col-header-text">{{col}}</span>
                    </li>
                </ul>
            </div>
            <!-- 表格內容 -->
            <div id="scrollPanel" class="content-wrapper">
                <!-- 循環生成模擬的表格數據 -->
                <ul class="content-list" ng-repeat="col in mockData.content">
                    <li class="content-item" ng-repeat="row in col">
                        <span class="content-text">{{row}}</span>
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <!-- 類庫文件加載 -->
    <script src="node_modules/angular/angular.min.js"></script>
    <script src="node_modules/jQuery/tmp/jquery.js"></script>
    <!-- 自定義腳本文件加載 -->
    <script src="fixHeaderTable.js"></script>
</body>
</html>

說明:將整體結構分成兩行first-rowsecond-row,並在first-row中實現固定標題和橫向表頭的結構,在second-row中實現縱向表頭和表格內容結構。

注意:在生成表格內容,橫向表頭標題和縱向表頭標題時,使用angular的ng-repeat指令,根據數據循環生成dom展示結構,詳情將官方API

CSS代碼,如下所示

/*整體結構樣式*/
.fixed-header-table {
    width: 600px;
    height: 600px;
    margin: 80px auto;
    background: #eee;
    position: relative;
}
/*第一行樣式*/
.first-row {
    width: calc(100% - 15px);
    background: red;
    height: 40px;
    margin-right: 15px;
}
/*固定標題樣式*/
.fixed-title {
    position: absolute;
    top: 0;
    left: 0;
    width: 40px;
    height: 40px;
    background: yellow;
    z-index: 2;
}
/*第二行樣式*/
.second-row {
    background: green;
    width: 100%;
    height: calc(100% - 40px);
    position: relative;
}
/*橫向表頭樣式*/
.fixed-row-header {
    height: 40px;
    padding-left: 40px;
    z-index: 0;
    overflow: hidden;
    /*在這裏設置overflow屬性,已隱藏超出的標題*/
}
.row-header-list {
    white-space: nowrap;
    /*因爲標題橫向排列時爲inline-block樣式,此屬性使其不自動換行*/
    height: 100%;
}
.row-header-item {
    width: calc(100% / 5);
    display: inline-block;
    line-height: 40px;
    height: 100%;
    text-align: center;
    border-left: 1px solid black;
}
.row-header-item:first-child {
    border-left: none;
}
/*縱向表頭樣式*/
.fixed-col-header {
    position: absolute;
    top: 0;
    left: 0;
    width: 40px;
    height: calc(100% - 15px);
    background: blue;
    overflow: hidden;
    /*在這裏設置overflow屬性,已隱藏超出的標題*/
}
.col-header-list {}
/*縱向時不涉及li的橫向排列問題,無需設置white-space*/
.col-header-item {
    height: 30px;
    line-height: 30px;
    text-align: center;
    border-bottom: 1px solid #fff;
}
.col-header-item:first-child {
    border-bottom: none;
}
.col-header-text {
    color: #fff;
}
/*表格內容樣式*/
.content-wrapper {
    position: absolute;
    top: 0;
    left: 40px;
    width: calc(100% - 40px);
    height: 100%;
    background: pink;
    white-space: nowrap;
    /*因爲表格內容分爲縱向和橫向,橫向爲inline-block樣式,此屬性使其不自動換行*/
    overflow: auto;
    /*用於展示橫縱滾動條*/
}
.content-list {
    display: inline-block;
    width: calc(100% / 5);
}
.content-item {
    height: 30px;
    line-height: 30px;
    text-align: center;
    border: 1px solid black;
}
.content-text {
    font-size: 12px;
}

注意:在編寫樣式時,需要注意代碼中註釋的幾個位置,包括inline-blockwhite-spaceoverflow等屬性的使用,在其中還是用calc,CSS樣式計算表達式,來計算相應的寬度和高度。


2.JS聯動滾動處理

基本結構和樣式編寫完成後,已經可以實現表格內容的滾動,但是無法做到橫向表頭和縱向表頭相對於內容滾動式的對應聯動效果,這就需要通過js進行實現,在這裏將主要使用jQuery的.css()方法和CSS中的transform-translate3d屬性,以及js中event.target.scrollTopevent.target.scrollLeft屬性。

a..css()方法:jQuery的內置方法,通過對象(key-value)在JavaScript文件中對指定的DOM元素的樣式進行變更;
b.transform-translate3d:爲CSS樣式,標準寫法爲

transform:translate3d(x,y,z);

其中x,y,z爲在橫向,縱向和前後的移動距離,詳情見transform詳解。在這裏使用translate3d是爲了強制開啓瀏覽器的3d加速效果,以保證滾動式的平滑過渡效果,在這裏需要注意的是,連帶滾動的DOM元素(在這裏指橫向表頭和縱向表頭)需要在發生滾動的元素之外,否則在safari中會出現閃爍、搖擺的現象,在Chrome中如果數據量較大時同時會出項相應問題。
另外,如果在angular中使用此方式進行聯動時,儘量避免表格中的雙向綁定數量控制在一定範圍內,並且不要在監聽事件時使用$apply()$digest等深層檢測方法,否則會出現嚴重卡頓現象。

c. event.target.scrollTopevent.target.scrollLeft:是當前時間對象中包含的元素的滾動上邊距和滾動左邊距,詳見

JavaScript代碼,如下所示。

angular.module('app', [])
    .controller('tableController', ['$scope', '$timeout', function($scope, $timeout) {
        /**
         * 生成虛擬數據
         * @return {[type]} [description]
         */
        function mockData() {
            var _mockData = {
                rowHeader: [],
                colHeader: [],
                content: []
            };

            var NUM = 50;

            var i = NUM;
            while (i) {
                _mockData.rowHeader.push('row' + ((NUM + 1 - i) < 10 ? '0' + (NUM + 1 - i) : (NUM + 1 - i)));
                _mockData.colHeader.push('col' + ((NUM + 1 - i) < 10 ? '0' + (NUM + 1 - i) : (NUM + 1 - i)));
                var j = NUM;
                while (j) {
                    _mockData.content[(NUM - i)] = _mockData.content[(NUM - i)] ? _mockData.content[(NUM - i)] : [];
                    _mockData.content[(NUM - i)].push('content' + ((NUM + 1 - i) < 10 ? '0' + (NUM + 1 - i) : (NUM + 1 - i)) + '-' + ((NUM + 1 - j) < 10 ? '0' + (NUM + 1 - j) : (NUM + 1 - j)));
                    j--;
                }
                i--;
            }
            return _mockData;
        }

        $scope.mockData = mockData();

        $timeout(function() {
            var _scrollPanel = $('#scrollPanel');
            var _rowHeader = $('#rowHeader');
            var _colHeader = $('#colHeader');
            _scrollPanel.on('scroll', function(event) {
                // 根據表格內容橫向滾動的距離,將橫向表頭在x方向進行translate
                _rowHeader.css({
                    transform: 'translate3d(-' + event.target.scrollLeft + 'px,0,0)'
                });
                // 根據表格內容縱向滾動的距離,將縱向表頭在y方向進行translate
                _colHeader.css({
                    transform: 'translate3d(0,-' + event.target.scrollTop + 'px,0)'
                });
            });
        });
    }]);

文件中虛擬數據生成函數基本無需關注,最主要的部分爲:

$timeout(function() {
            var _scrollPanel = $('#scrollPanel');
            var _rowHeader = $('#rowHeader');
            var _colHeader = $('#colHeader');
            _scrollPanel.on('scroll', function(event) {
                // 根據表格內容橫向滾動的距離,將橫向表頭在x方向進行translate
                _rowHeader.css({
                    transform: 'translate3d(-' + event.target.scrollLeft + 'px,0,0)'
                });
                // 根據表格內容縱向滾動的距離,將縱向表頭在y方向進行translate
                _colHeader.css({
                    transform: 'translate3d(0,-' + event.target.scrollTop + 'px,0)'
                });
            });
        });

在angular中使用jQuery的方法需要在外部包裹$timeout,以確保angular雙向綁定數據的及時更新。之後通過id獲取對應元素,監聽表格內容的滾動事件,根據滾動的左邊距和右邊距對橫向和縱向表頭進行位移操作,即可實現最終效果。

再會,呵呵,祝好!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章