bootstrap-table-fixed-columns.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. 'use strict';
  2. (function ($) {
  3. 'use strict';
  4. // Reasonable defaults
  5. var PIXEL_STEP = 10;
  6. var LINE_HEIGHT = 40;
  7. var PAGE_HEIGHT = 800;
  8. function normalizeWheel(event) {
  9. var sX = 0; // spinX
  10. var sY = 0; // spinY
  11. var pX = 0; // pixelX
  12. var pY = 0; // pixelY
  13. // Legacy
  14. if ('detail' in event) {
  15. sY = event.detail;
  16. }
  17. if ('wheelDelta' in event) {
  18. sY = -event.wheelDelta / 120;
  19. }
  20. if ('wheelDeltaY' in event) {
  21. sY = -event.wheelDeltaY / 120;
  22. }
  23. if ('wheelDeltaX' in event) {
  24. sX = -event.wheelDeltaX / 120;
  25. }
  26. // side scrolling on FF with DOMMouseScroll
  27. if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) {
  28. sX = sY;
  29. sY = 0;
  30. }
  31. pX = sX * PIXEL_STEP;
  32. pY = sY * PIXEL_STEP;
  33. if ('deltaY' in event) {
  34. pY = event.deltaY;
  35. }
  36. if ('deltaX' in event) {
  37. pX = event.deltaX;
  38. }
  39. if ((pX || pY) && event.deltaMode) {
  40. if (event.deltaMode === 1) {
  41. // delta in LINE units
  42. pX *= LINE_HEIGHT;
  43. pY *= LINE_HEIGHT;
  44. } else {
  45. // delta in PAGE units
  46. pX *= PAGE_HEIGHT;
  47. pY *= PAGE_HEIGHT;
  48. }
  49. }
  50. // Fall-back if spin cannot be determined
  51. if (pX && !sX) {
  52. sX = pX < 1 ? -1 : 1;
  53. }
  54. if (pY && !sY) {
  55. sY = pY < 1 ? -1 : 1;
  56. }
  57. return {
  58. spinX: sX,
  59. spinY: sY,
  60. pixelX: pX,
  61. pixelY: pY
  62. };
  63. }
  64. var cachedWidth = null;
  65. var getScrollBarWidth = function getScrollBarWidth() {
  66. if (cachedWidth === null) {
  67. var inner = $('<p/>').addClass('fixed-table-scroll-inner'),
  68. outer = $('<div/>').addClass('fixed-table-scroll-outer'),
  69. w1 = void 0,
  70. w2 = void 0;
  71. outer.append(inner);
  72. $('body').append(outer);
  73. w1 = inner[0].offsetWidth;
  74. outer.css('overflow', 'scroll');
  75. w2 = inner[0].offsetWidth;
  76. if (w1 === w2) {
  77. w2 = outer[0].clientWidth;
  78. }
  79. outer.remove();
  80. cachedWidth = w1 - w2;
  81. }
  82. return cachedWidth;
  83. };
  84. //获取原本表格体的滑块宽度
  85. var getTableBodyScrollBarWidth = function getTableBodyScrollBarWidth(tableBody) {
  86. return tableBody[0].scrollHeight > tableBody[0].clientHeight ? 15 : 0;
  87. };
  88. $.extend($.fn.bootstrapTable.defaults, {
  89. fixedColumns: false,
  90. fixedNumber: 0,
  91. fixedRightNumber: 0
  92. });
  93. var BootstrapTable = $.fn.bootstrapTable.Constructor,
  94. _initBody = BootstrapTable.prototype.initBody,
  95. _initContainer = BootstrapTable.prototype.initContainer,
  96. _trigger = BootstrapTable.prototype.trigger,
  97. _hideLoading = BootstrapTable.prototype.hideLoading,
  98. _updateSelected = BootstrapTable.prototype.updateSelected;
  99. BootstrapTable.prototype.fixedColumnsSupported = function () {
  100. var that = this;
  101. return that.options.fixedColumns && !that.options.detailView && !that.options.cardView;
  102. };
  103. BootstrapTable.prototype.initFixedContainer = function () {
  104. if (!this.fixedColumnsSupported()) {
  105. return;
  106. }
  107. if (this.options.fixedNumber) {
  108. this.$tableContainer.find('.fixed-columns').length == 0 && this.$tableContainer.append('<div class="fixed-columns"></div>');
  109. this.$fixedColumns = this.$tableContainer.find('.fixed-columns');
  110. }
  111. if (this.options.fixedRightNumber) {
  112. this.$tableContainer.find('.fixed-columns-right').length == 0 && this.$tableContainer.append('<div class="fixed-columns-right"></div>');
  113. this.$fixedColumnsRight = this.$tableContainer.find('.fixed-columns-right');
  114. }
  115. };
  116. BootstrapTable.prototype.initContainer = function () {
  117. _initContainer.apply(this, Array.prototype.slice.apply(arguments));
  118. this.initFixedContainer();
  119. };
  120. BootstrapTable.prototype.initBody = function () {
  121. _initBody.apply(this, Array.prototype.slice.apply(arguments));
  122. if (!this.fixedColumnsSupported()) {
  123. return;
  124. }
  125. if (this.options.showHeader && this.options.height) {
  126. return;
  127. }
  128. this.initFixedColumnsBody();
  129. this.initFixedColumnsEvents();
  130. };
  131. BootstrapTable.prototype.trigger = function () {
  132. var that = this;
  133. _trigger.apply(this, Array.prototype.slice.apply(arguments));
  134. if (arguments[0] === 'pre-body') {
  135. //如果上来就是cardView 设置表格高度为auto
  136. if (this.options.cardView) {
  137. this.$tableBody.css("height", "auto");
  138. }
  139. }
  140. //监听cardView 显示/隐藏fixed部分
  141. if (arguments[0] === 'toggle') {
  142. if (arguments[1]) {
  143. this.$tableBody.css("height", "auto");
  144. this.$fixedColumns && this.$fixedColumns.hide();
  145. this.$fixedColumnsRight && this.$fixedColumnsRight.hide();
  146. } else {
  147. this.$tableBody.css("height", "100%");
  148. this.$fixedColumns && this.$fixedColumns.show();
  149. this.$fixedColumnsRight && this.$fixedColumnsRight.show();
  150. this.$fixedHeaderRight && this.$fixedHeaderRight.scrollLeft(this.$tableBody.find('table').width());
  151. this.$fixedBodyRight && this.$fixedBodyRight.scrollLeft(this.$tableBody.find('table').width());
  152. }
  153. }
  154. if (!that.fixedColumnsSupported()) {
  155. return;
  156. }
  157. if (arguments[0] === 'post-header') {
  158. this.initFixedColumnsHeader();
  159. } else if (arguments[0] === 'scroll-body') {
  160. if (this.needFixedColumns && this.options.fixedNumber) {
  161. this.$fixedBody && this.$fixedBody.scrollTop(this.$tableBody.scrollTop());
  162. }
  163. if (this.needFixedColumns && this.options.fixedRightNumber) {
  164. this.$fixedBodyRight && this.$fixedBodyRight.scrollTop(this.$tableBody.scrollTop());
  165. }
  166. } else if (arguments[0] === 'load-success') {
  167. this.hideLoading();
  168. }
  169. };
  170. BootstrapTable.prototype.updateSelected = function () {
  171. var that = this;
  172. _updateSelected.apply(this, Array.prototype.slice.apply(arguments));
  173. if (!this.fixedColumnsSupported()) {
  174. return;
  175. }
  176. this.$tableBody.find('tr').each(function (i, el) {
  177. var $el = $(el);
  178. var index = $el.data('index');
  179. var classes = $el.attr('class');
  180. var inputSelector = '[name="' + that.options.selectItemName + '"]';
  181. var $input = $el.find(inputSelector);
  182. if (typeof index === 'undefined') {
  183. return;
  184. }
  185. var updateFixedBody = function updateFixedBody($fixedHeader, $fixedBody) {
  186. var $tr = $fixedBody.find('tr[data-index="' + index + '"]');
  187. $tr.attr('class', classes);
  188. if ($input.length) {
  189. $tr.find(inputSelector).prop('checked', $input.prop('checked'));
  190. }
  191. if (that.$selectAll.length) {
  192. $fixedHeader.add($fixedBody).find('[name="btSelectAll"]').prop('checked', that.$selectAll.prop('checked'));
  193. }
  194. };
  195. if (that.$fixedBody && that.options.fixedNumber) {
  196. updateFixedBody(that.$fixedHeader, that.$fixedBody);
  197. }
  198. if (that.$fixedBodyRight && that.options.fixedRightNumber) {
  199. updateFixedBody(that.$fixedHeaderRight, that.$fixedBodyRight);
  200. }
  201. });
  202. };
  203. BootstrapTable.prototype.hideLoading = function () {
  204. _hideLoading.apply(this, Array.prototype.slice.apply(arguments));
  205. if (this.needFixedColumns && this.options.fixedNumber) {
  206. this.$fixedColumns.find('.fixed-table-loading').hide();
  207. }
  208. if (this.needFixedColumns && this.options.fixedRightNumber) {
  209. this.$fixedColumnsRight.find('.fixed-table-loading').hide();
  210. }
  211. };
  212. BootstrapTable.prototype.initFixedColumnsHeader = function () {
  213. var that = this;
  214. if (this.options.height) {
  215. this.needFixedColumns = this.$tableHeader.outerWidth(true) < this.$tableHeader.find('table').outerWidth(true);
  216. } else {
  217. this.needFixedColumns = this.$tableBody.outerWidth(true) < this.$tableBody.find('table').outerWidth(true);
  218. }
  219. var initFixedHeader = function initFixedHeader($fixedColumns, isRight) {
  220. $fixedColumns.find('.fixed-table-header').remove();
  221. $fixedColumns.append(that.$tableHeader.clone(true));
  222. $fixedColumns.find('.fixed-table-header').css('margin-right', "");
  223. $fixedColumns.css({
  224. width: that.getFixedColumnsWidth(isRight)
  225. });
  226. return $fixedColumns.find('.fixed-table-header');
  227. };
  228. if (this.needFixedColumns && this.options.fixedNumber) {
  229. this.$fixedHeader = initFixedHeader(this.$fixedColumns);
  230. this.$fixedHeader.css('margin-right', '');
  231. } else if (this.$fixedColumns) {
  232. this.$fixedColumns.html('').css('width', '');
  233. }
  234. if (this.needFixedColumns && this.options.fixedRightNumber) {
  235. this.$fixedHeaderRight = initFixedHeader(this.$fixedColumnsRight, true);
  236. this.$fixedHeaderRight.scrollLeft(this.$fixedHeaderRight.find('table').width());
  237. } else if (this.$fixedColumnsRight) {
  238. this.$fixedColumnsRight.html('').css('width', '');
  239. }
  240. this.initFixedColumnsBody();
  241. this.initFixedColumnsEvents();
  242. };
  243. BootstrapTable.prototype.initFixedColumnsBody = function () {
  244. var that = this;
  245. var initFixedBody = function initFixedBody($fixedColumns, $fixedHeader) {
  246. $fixedColumns.find('.fixed-table-body').remove();
  247. $fixedColumns.append(that.$tableBody.clone(true));
  248. var $fixedBody = $fixedColumns.find('.fixed-table-body');
  249. var tableBody = that.$tableBody.get(0);
  250. var scrollHeight = tableBody.scrollWidth > tableBody.clientWidth ? getScrollBarWidth() : 0;
  251. var paginationHeight = $(".fixed-table-pagination", that.$tableContainer).height();
  252. if (typeof that.options.height !== 'undefined') paginationHeight = 0;
  253. var height = that.$tableContainer.outerHeight(true) - scrollHeight - paginationHeight + 1;
  254. $fixedColumns.css({
  255. height: height,
  256. "min-height": "calc(100% - " + (paginationHeight + scrollHeight) + "px)"
  257. });
  258. $fixedBody.css({
  259. height: height - $fixedHeader.height(),
  260. "min-height": "calc(100% - " + $fixedHeader.height() + "px)",
  261. overflow: "hidden"
  262. });
  263. return $fixedBody;
  264. };
  265. if (this.needFixedColumns && this.options.fixedNumber) {
  266. this.$fixedBody = initFixedBody(this.$fixedColumns, this.$fixedHeader);
  267. }
  268. if (this.needFixedColumns && this.options.fixedRightNumber) {
  269. this.$fixedBodyRight = initFixedBody(this.$fixedColumnsRight, this.$fixedHeaderRight);
  270. this.$fixedBodyRight.scrollLeft(this.$fixedBodyRight.find('table').width());
  271. this.$fixedBodyRight.css('overflow-y', 'hidden');
  272. }
  273. };
  274. BootstrapTable.prototype.getFixedColumnsWidth = function (isRight) {
  275. var visibleFields = this.getVisibleFields();
  276. var width = 0;
  277. var fixedNumber = this.options.fixedNumber;
  278. var marginRight = 0;
  279. if (isRight) {
  280. visibleFields = visibleFields.reverse();
  281. fixedNumber = this.options.fixedRightNumber;
  282. //右侧固定列距离
  283. this.$fixedColumnsRight.css('right', getTableBodyScrollBarWidth(this.$tableBody));
  284. }
  285. for (var i = 0; i < fixedNumber; i++) {
  286. width += this.$header.find('th[data-field="' + visibleFields[i] + '"]').outerWidth();
  287. }
  288. return width + 1;
  289. };
  290. BootstrapTable.prototype.initFixedColumnsEvents = function () {
  291. var that = this;
  292. var toggleHover = function toggleHover(e, toggle) {
  293. var tr = 'tr[data-index="' + $(e.currentTarget).data('index') + '"]';
  294. var $trs = that.$tableBody.find(tr);
  295. if (that.$fixedBody) {
  296. $trs = $trs.add(that.$fixedBody.find(tr));
  297. }
  298. if (that.$fixedBodyRight) {
  299. $trs = $trs.add(that.$fixedBodyRight.find(tr));
  300. }
  301. $trs.css('background-color', toggle ? $(e.currentTarget).css('background-color') : '');
  302. };
  303. this.$tableBody.find('tr').hover(function (e) {
  304. toggleHover(e, true);
  305. }, function (e) {
  306. toggleHover(e, false);
  307. });
  308. var isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
  309. var mousewheel = isFirefox ? 'DOMMouseScroll' : 'mousewheel';
  310. var updateScroll = function updateScroll(e, fixedBody) {
  311. var normalized = normalizeWheel(e);
  312. var deltaY = Math.ceil(normalized.pixelY);
  313. var top = that.$tableBody.scrollTop() + deltaY;
  314. if (deltaY < 0 && top > 0 || deltaY > 0 && top < fixedBody.scrollHeight - fixedBody.clientHeight) {
  315. e.preventDefault();
  316. }
  317. that.$tableBody.scrollTop(top);
  318. if (that.$fixedBody) {
  319. that.$fixedBody.scrollTop(top);
  320. }
  321. if (that.$fixedBodyRight) {
  322. that.$fixedBodyRight.scrollTop(top);
  323. }
  324. };
  325. if (this.needFixedColumns && this.options.fixedNumber) {
  326. this.$fixedBody.find('tr').hover(function (e) {
  327. toggleHover(e, true);
  328. }, function (e) {
  329. toggleHover(e, false);
  330. });
  331. this.$fixedBody[0].addEventListener(mousewheel, function (e) {
  332. //给鼠标滑轮绑定事件
  333. updateScroll(e, that.$fixedBody[0]);
  334. });
  335. //给固定表格的checkbox绑定事件
  336. this.$fixedBody.find('input[name="' + this.options.selectItemName + '"]').off("click").on('click', function (e) {
  337. e.stopImmediatePropagation();
  338. var index = $(e.target).data("index");
  339. $(that.$selectItem[index]).trigger("click");
  340. });
  341. //绑定TD点击事件
  342. this.$fixedBody.find('> table > tbody > tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) {
  343. var index = $(this).closest("tr[data-index]").data("index");
  344. $(that.$selectItem[index]).closest("tr[data-index]").find(">td:eq(" + $(this).index() + ")").trigger("click");
  345. });
  346. }
  347. //给原本表格绑定scroll事件
  348. $('div.fixed-table-body').off('scroll'); //给所有的body解绑 scroll
  349. this.$tableBody.off('scroll').on('scroll', function (e) {
  350. that.$tableHeader.scrollLeft(0);
  351. if (that.$tableBody.scrollLeft() > 0) {
  352. that.$tableHeader.scrollLeft(that.$tableBody.scrollLeft());
  353. if (that.options.showFooter && !that.options.cardView) {
  354. that.$tableFooter.scrollLeft(that.$tableBody.scrollLeft());
  355. }
  356. }
  357. var top = that.$tableBody.scrollTop();
  358. if (that.$fixedBody) {
  359. that.$fixedBody.scrollTop(top);
  360. }
  361. if (that.$fixedBodyRight) {
  362. that.$fixedBodyRight.scrollTop(top);
  363. }
  364. });
  365. if (this.needFixedColumns && this.options.fixedRightNumber) {
  366. this.$fixedBodyRight.find('tr').hover(function (e) {
  367. toggleHover(e, true);
  368. }, function (e) {
  369. toggleHover(e, false);
  370. });
  371. this.$fixedBodyRight[0].addEventListener(mousewheel, function (e) {
  372. //给鼠标滑轮绑定事件
  373. updateScroll(e, that.$fixedBodyRight[0]);
  374. });
  375. //给固定表格的checkbox绑定事件
  376. this.$fixedBodyRight.find('input[name="' + this.options.selectItemName + '"]').off("click").on('click', function (e) {
  377. e.stopImmediatePropagation();
  378. var index = $(e.target).data("index");
  379. $(that.$selectItem[index]).trigger("click");
  380. });
  381. //绑定TD点击事件
  382. this.$fixedBodyRight.find('> table > tbody > tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) {
  383. var index = $(this).closest("tr[data-index]").data("index");
  384. $(that.$selectItem[index]).closest("tr[data-index]").find(">td:eq(" + $(this).index() + ")").trigger("click");
  385. });
  386. }
  387. if (this.options.filterControl) {
  388. $(this.$fixedColumns).off('keyup change').on('keyup change', function (e) {
  389. var $target = $(e.target);
  390. var value = $target.val();
  391. var field = $target.parents('th').data('field');
  392. var $coreTh = that.$header.find('th[data-field="' + field + '"]');
  393. if ($target.is('input')) {
  394. $coreTh.find('input').val(value);
  395. } else if ($target.is('select')) {
  396. var $select = $coreTh.find('select');
  397. $select.find('option[selected]').removeAttr('selected');
  398. $select.find('option[value="' + value + '"]').attr('selected', true);
  399. }
  400. that.triggerSearch();
  401. });
  402. }
  403. };
  404. })(jQuery);