實現雙向固定表頭的表格
實現目標
- 包含橫向表頭和縱向表頭的表格
- 在橫向滾動軸滾動時,橫向表頭隨之滾動,縱向表頭固定不變
- 在縱向滾動軸滾動時,縱向表頭隨之滾動,橫向表頭固定不變
效果圖
使用資源
- angularjs
- jquery
- 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-row
和second-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-block
,white-space
,overflow
等屬性的使用,在其中還是用calc
,CSS樣式計算表示式,來計算相應的寬度和高度。
2.JS聯動滾動處理
基本結構和樣式編寫完成後,已經可以實現表格內容的滾動,但是無法做到橫向表頭和縱向表頭相對於內容滾動式的對應聯動效果,這就需要通過js進行實現,在這裡將主要使用jQuery的.css()
方法和CSS中的transform-translate3d
屬性,以及js中event.target.scrollTop
和event.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.scrollTop
和event.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獲取對應元素,監聽表格內容的滾動事件,根據滾動的左邊距和右邊距對橫向和縱向表頭進行位移操作,即可實現最終效果。
再會,呵呵,祝好!!