forked from wingmeng/fixedThead
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfixedThead.js
346 lines (312 loc) · 11.9 KB
/
fixedThead.js
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/**
* jquery 表头固定插件 v0.1.0,fixedThead.js
* @auther wingmeng
* @update: 2017/05/06
*/
; (function ($) {
$.fn.fixedThead = function (options) {
var defaults = {
height: 'auto', // 表格最大高度,默认高度占满可视区
vspace: 0, // 垂直预留空间,为可视区其他组件空出空间,只有当height为auto时生效
top: 0, // 表格距离顶部的距离
bottom: 0, // 表格距离底部的距离
row: 1, // 要固定的行数
col: 0, // 要固定的列数
distorted: 0
};
var opts = $.extend(defaults, $.fn.fixedThead.option, options);
return this.each(function () {
var _this = $(this);
opts = verifyOpts(opts, defaults); // 校验参数
if (typeof options == 'string') {
if (options === 'refresh') { // 刷新
refresh(_this, opts);
} else if (options === 'destroy') { // 销毁实例
destroy(_this);
}
return;
}
if (_this.parent().parent('div.fixed-thead').length) {
refresh(_this, opts);
return; // 防止重复创建DOM
}
// 存储、移除自身内嵌样式
if (_this.attr('style')) {
_this.attr('data-style', _this.attr('style'));
_this.removeAttr('style');
}
// 构建HTML结构
var configs = buildHTML(_this, opts);
guideBuild(configs);
configs = null;
});
}
$.fn.fixedThead.option = {};
// 校验并确保自定义参数的合法性
function verifyOpts(opts, defaults) {
var result = defaults;
var ckFun = function (str) {
if (opts[str] == 0 || opts[str]) {
if (!isNaN(opts[str]) && opts[str] >= 0) {
result[str] = opts[str];
}
}
};
for (var key in result) {
ckFun(key);
}
return result;
}
// 刷新数据(动态更新表格)
function refresh(tableObj, opts) {
if (tableObj.parent().parent('div.fixed-thead').length) {
var configs = {
table: tableObj,
bodyWrap: tableObj.parent(),
wrapper: tableObj.parent().parent(),
scrollBarWt: getScrollWidth(),
opts: opts
}
guideBuild(configs, true);
}
}
// 销毁实例
function destroy(tableObj) {
var parent = tableObj.parent().parent('div.fixed-thead'),
oldStyle = tableObj.data('style');
if (oldStyle) {
tableObj.attr('style', oldStyle).removeAttr('data-style'); // 还原之前的style样式
} else {
tableObj.removeAttr('style');
}
parent.before(tableObj);
parent.empty().remove();
}
// 克隆table HTML
function cloneTable(table, flag) {
var tClone = table.clone(), // 是否复制事件?(待完善)
tId = table.attr('id');
if (tId) {
tClone.attr('id', 'clone_' + tId + '_' + flag);
}
tClone.removeAttr('style'); // 移除复制的内嵌样式
return tClone;
}
// 构建HTML向导
function guideBuild(configs, flag) {
var opts = configs.opts,
tableWrap = {};
if (opts.row > 0 && opts.col > 0) {
tableWrap.head = buildHTML_head(configs);
tableWrap.side = buildHTML_side(configs);
tableWrap.corner = buildHTML_corner(configs);
} else {
if (opts.row > 0) {
tableWrap.head = buildHTML_head(configs);
}
if (opts.col > 0) {
tableWrap.side = buildHTML_side(configs);
}
}
calcLayout(configs, tableWrap);
if (!flag) {
configs.bodyWrap.scroll(function () {
if (tableWrap.side) {
tableWrap.side.scrollTop($(this).scrollTop());
}
if (tableWrap.head) {
tableWrap.head.scrollLeft($(this).scrollLeft());
}
});
$(window).resize(function () {
calcLayout(configs, tableWrap);
});
}
}
// 构建HTML结构
function buildHTML(tableObj, opts) {
var wrapper = $('<div class="fixed-thead"></div>'),
bodyWrap = $('<div class="fixed-thead-body"></div>');
tableObj.wrap(wrapper).wrap(bodyWrap);
var configs = {
table: tableObj,
bodyWrap: tableObj.parent(),
wrapper: tableObj.parent().parent(),
scrollBarWt: getScrollWidth(),
opts: opts
};
if (opts.top) {
configs.wrapper.css('margin-top', opts.top);
}
if (opts.bottom) {
configs.wrapper.css('margin-bottom', opts.bottom);
}
return configs;
}
// 构建头部
function buildHTML_head(configs) {
var wrap = configs.wrapper.find('div.fixed-thead-head');
var headTable = cloneTable(configs.table, 'head');
if (wrap.length == 0) {
wrap = $('<div class="fixed-thead-head"></div>');
configs.bodyWrap.before(wrap.append(headTable));
} else {
wrap.empty().append(headTable); // empty()一下释放内存
}
wrap = distoredUnimportant(wrap, configs, 'head');
return wrap;
}
// 构建侧边
function buildHTML_side(configs) {
var wrap = configs.wrapper.find('div.fixed-thead-side');
var sideTable = cloneTable(configs.table, 'side');
if (wrap.length == 0) {
wrap = $('<div class="fixed-thead-side"></div>');
configs.bodyWrap.before(wrap.append(sideTable));
} else {
wrap.empty().append(sideTable);
}
wrap = distoredUnimportant(wrap, configs, 'side');
return wrap;
}
// 构建左上角
function buildHTML_corner(configs) {
var wrap = configs.wrapper.find('div.fixed-thead-corner');
var cornerTable = cloneTable(configs.table, 'corner');
if (wrap.length == 0) {
wrap = $('<div class="fixed-thead-corner"></div>');
configs.bodyWrap.before(wrap.append(cornerTable));
} else {
wrap.empty().append(cornerTable);
}
wrap = distoredUnimportant(wrap, configs, 'corner');
return wrap;
}
// 布局计算
function calcLayout(configs, tableWrap) {
// 计算固定的行数高度总和
var maxHt = 0,
headHt = 0,
sideWt = 0;
if (tableWrap.head) {
for (var i = 0; i < configs.opts.row; i++) {
headHt += configs.table.find('tr').eq(i).outerHeight();
}
++headHt; // 让固定head露出1px底边框线
configs.table.css('margin-top', -headHt + 'px');
tableWrap.head.height(headHt);
maxHt = calcLayout_resize_head(configs, tableWrap.head, headHt);
}
if (tableWrap.side) {
for (var i = 0; i < configs.opts.col; i++) {
sideWt += configs.table.find('tr:eq(0)').children().eq(i).outerWidth();
}
++sideWt; // 让固定side露出1px右边框线
// 如果表格未溢出可视区(未出现水平滚动条)
if (configs.table.width() <= configs.bodyWrap.width()) {
sideWt = 0;
}
configs.table.css('margin-left', -sideWt + 'px');
configs.bodyWrap.css('margin-left', sideWt + 'px');
if (tableWrap.head) {
tableWrap.head.css('margin-left', sideWt + 'px')
.children('table').css('margin-left', -sideWt + 'px');
}
tableWrap.side.css({
top: headHt - 1 + 'px',
width: sideWt + 'px',
maxHeight: maxHt - configs.scrollBarWt + 1 + 'px'
}).children('table').css('margin-top', -(headHt - 1) + 'px');
}
if (tableWrap.corner) {
tableWrap.corner.css({
width: sideWt + 'px',
height: headHt + 'px'
});
}
}
// 计算并应用头部固定区域与滚动区域的布局样式
function calcLayout_resize_head(configs, headWrap, headHt) {
var maxHt = 0;
if (configs.opts.height == 'auto') { // 占满可视区
maxHt = $(window).height() - configs.opts.vspace - headHt - configs.opts.top - configs.opts.bottom;
} else {
maxHt = configs.opts.height - headHt;
}
configs.bodyWrap.css('max-height', maxHt + 'px');
var headTable = headWrap.children('table'),
rowfix = headTable.find('.fixed-thead-rowfix');
if (configs.bodyWrap.height() < configs.table.height() - headHt) { // 出现垂直滚动条
if (!rowfix.length) {
calcLayout_fix_head(configs, headWrap, headTable);
}
} else {
rowfix.remove();
}
return maxHt;
}
// 溢出修补
function calcLayout_fix_head(configs, headWrap, headTable) {
var scrollBarWt = configs.scrollBarWt - 1 + 'px'; // 滚动条宽度
// 添加空单元格来修复滚动条占据的宽度
for (var i = 0; i < configs.opts.row; i++) {
var curRow = headTable.find('tr').eq(i),
fixRow = curRow.children().last().clone();
fixRow.empty().addClass('fixed-thead-rowfix');
if (headTable.width() > headWrap.width()) { // 出现水平滚动条的情况
var innerDiv = $('<div style="width:' + scrollBarWt + '"></div>');
fixRow.css({
width: 0,
padding: 0
}).append(innerDiv);
} else {
fixRow.css({
width: scrollBarWt,
padding: 0
});
}
curRow.append(fixRow);
}
}
// 获取浏览器滚动条宽度
function getScrollWidth() {
var noScroll, scroll;
var oDiv = document.createElement('div'),
$oDiv = $(oDiv);
$oDiv.css({
position: 'absolute',
top: '-50px',
zIndex: '-1',
width: '50px',
height: '50px',
overflow: 'hidden'
});
document.body.appendChild(oDiv);
noScroll = oDiv.clientWidth;
oDiv.style.overflowY = 'scroll';
scroll = oDiv.clientWidth;
$oDiv.remove();
return noScroll - scroll;
}
// Make random string to destroy the 'id' and 'class' attributes value to the unimportant cloned tabled
function makeSalt() {
return Math.random().toString(36).substr(2, 8);
}
function distoredUnimportant(wrap, configs, flag) {
__salt = makeSalt();
if (configs.opts.distorted == 1) {
if (flag == 'head' || flag == 'corner') {
wrap.find('input, button').prop('disabled', true);
}
else if (flag == 'side') {
// Side
wrap.find('tbody tr td:nth-child(n+' + (configs.opts.col + 1) + ')').find('input, button').prop('disabled', true);
}
else {
// Body
wrap.find('tbody tr td:nth-child(-n+' + configs.opts.col + ')').find('input, button').prop('disabled', true);
}
}
return wrap;
}
})(jQuery);