bootstrap-table-fixed-columns.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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.options.fixedNumber) {
  105. this.$tableContainer.find('.fixed-columns').length == 0 && this.$tableContainer.append('<div class="fixed-columns"></div>');
  106. this.$fixedColumns = this.$tableContainer.find('.fixed-columns');
  107. }
  108. if (this.options.fixedRightNumber) {
  109. this.$tableContainer.find('.fixed-columns-right').length == 0 && this.$tableContainer.append('<div class="fixed-columns-right"></div>');
  110. this.$fixedColumnsRight = this.$tableContainer.find('.fixed-columns-right');
  111. }
  112. };
  113. BootstrapTable.prototype.initContainer = function () {
  114. _initContainer.apply(this, Array.prototype.slice.apply(arguments));
  115. this.initFixedContainer();
  116. };
  117. BootstrapTable.prototype.initBody = function () {
  118. _initBody.apply(this, Array.prototype.slice.apply(arguments));
  119. if (!this.fixedColumnsSupported()) {
  120. return;
  121. }
  122. if (this.options.showHeader && this.options.height) {
  123. return;
  124. }
  125. this.initFixedColumnsBody();
  126. this.initFixedColumnsEvents();
  127. };
  128. BootstrapTable.prototype.trigger = function () {
  129. var that = this;
  130. _trigger.apply(this, Array.prototype.slice.apply(arguments));
  131. if (arguments[0] === 'pre-body') {
  132. //如果上来就是cardView 设置表格高度为auto
  133. if (this.options.cardView) {
  134. this.$tableBody.css("height", "auto");
  135. }
  136. }
  137. //监听cardView 显示/隐藏fixed部分
  138. if (arguments[0] === 'toggle') {
  139. if (arguments[1]) {
  140. this.$tableBody.css("height", "auto");
  141. this.$fixedColumns && this.$fixedColumns.hide();
  142. this.$fixedColumnsRight && this.$fixedColumnsRight.hide();
  143. } else {
  144. this.$tableBody.css("height", "100%");
  145. this.$fixedColumns && this.$fixedColumns.show();
  146. this.$fixedColumnsRight && this.$fixedColumnsRight.show();
  147. this.$fixedHeaderRight && this.$fixedHeaderRight.scrollLeft(this.$tableBody.find('table').width());
  148. this.$fixedBodyRight && this.$fixedBodyRight.scrollLeft(this.$tableBody.find('table').width());
  149. }
  150. }
  151. if (!that.fixedColumnsSupported()) {
  152. return;
  153. }
  154. if (arguments[0] === 'post-header') {
  155. this.initFixedColumnsHeader();
  156. } else if (arguments[0] === 'scroll-body') {
  157. if (this.needFixedColumns && this.options.fixedNumber) {
  158. this.$fixedBody && this.$fixedBody.scrollTop(this.$tableBody.scrollTop());
  159. }
  160. if (this.needFixedColumns && this.options.fixedRightNumber) {
  161. this.$fixedBodyRight && this.$fixedBodyRight.scrollTop(this.$tableBody.scrollTop());
  162. }
  163. } else if (arguments[0] === 'load-success') {
  164. this.hideLoading();
  165. }
  166. };
  167. BootstrapTable.prototype.updateSelected = function () {
  168. var that = this;
  169. _updateSelected.apply(this, Array.prototype.slice.apply(arguments));
  170. if (!this.fixedColumnsSupported()) {
  171. return;
  172. }
  173. this.$tableBody.find('tr').each(function (i, el) {
  174. var $el = $(el);
  175. var index = $el.data('index');
  176. var classes = $el.attr('class');
  177. var inputSelector = '[name="' + that.options.selectItemName + '"]';
  178. var $input = $el.find(inputSelector);
  179. if (typeof index === 'undefined') {
  180. return;
  181. }
  182. var updateFixedBody = function updateFixedBody($fixedHeader, $fixedBody) {
  183. var $tr = $fixedBody.find('tr[data-index="' + index + '"]');
  184. $tr.attr('class', classes);
  185. if ($input.length) {
  186. $tr.find(inputSelector).prop('checked', $input.prop('checked'));
  187. }
  188. if (that.$selectAll.length) {
  189. $fixedHeader.add($fixedBody).find('[name="btSelectAll"]').prop('checked', that.$selectAll.prop('checked'));
  190. }
  191. };
  192. if (that.$fixedBody && that.options.fixedNumber) {
  193. updateFixedBody(that.$fixedHeader, that.$fixedBody);
  194. }
  195. if (that.$fixedBodyRight && that.options.fixedRightNumber) {
  196. updateFixedBody(that.$fixedHeaderRight, that.$fixedBodyRight);
  197. }
  198. });
  199. };
  200. BootstrapTable.prototype.hideLoading = function () {
  201. _hideLoading.apply(this, Array.prototype.slice.apply(arguments));
  202. if (this.needFixedColumns && this.options.fixedNumber) {
  203. this.$fixedColumns.find('.fixed-table-loading').hide();
  204. }
  205. if (this.needFixedColumns && this.options.fixedRightNumber) {
  206. this.$fixedColumnsRight.find('.fixed-table-loading').hide();
  207. }
  208. };
  209. BootstrapTable.prototype.initFixedColumnsHeader = function () {
  210. var that = this;
  211. if (this.options.height) {
  212. this.needFixedColumns = this.$tableHeader.outerWidth(true) < this.$tableHeader.find('table').outerWidth(true);
  213. } else {
  214. this.needFixedColumns = this.$tableBody.outerWidth(true) < this.$tableBody.find('table').outerWidth(true);
  215. }
  216. var initFixedHeader = function initFixedHeader($fixedColumns, isRight) {
  217. $fixedColumns.find('.fixed-table-header').remove();
  218. $fixedColumns.append(that.$tableHeader.clone(true));
  219. $fixedColumns.find('.fixed-table-header').css('margin-right', "");
  220. $fixedColumns.css({
  221. width: that.getFixedColumnsWidth(isRight)
  222. });
  223. return $fixedColumns.find('.fixed-table-header');
  224. };
  225. if (this.needFixedColumns && this.options.fixedNumber) {
  226. this.$fixedHeader = initFixedHeader(this.$fixedColumns);
  227. this.$fixedHeader.css('margin-right', '');
  228. } else if (this.$fixedColumns) {
  229. this.$fixedColumns.html('').css('width', '');
  230. }
  231. if (this.needFixedColumns && this.options.fixedRightNumber) {
  232. this.$fixedHeaderRight = initFixedHeader(this.$fixedColumnsRight, true);
  233. this.$fixedHeaderRight.scrollLeft(this.$fixedHeaderRight.find('table').width());
  234. } else if (this.$fixedColumnsRight) {
  235. this.$fixedColumnsRight.html('').css('width', '');
  236. }
  237. this.initFixedColumnsBody();
  238. this.initFixedColumnsEvents();
  239. };
  240. BootstrapTable.prototype.initFixedColumnsBody = function () {
  241. var that = this;
  242. var initFixedBody = function initFixedBody($fixedColumns, $fixedHeader) {
  243. $fixedColumns.find('.fixed-table-body').remove();
  244. $fixedColumns.append(that.$tableBody.clone(true));
  245. var $fixedBody = $fixedColumns.find('.fixed-table-body');
  246. var tableBody = that.$tableBody.get(0);
  247. var resizeHeight = function (){
  248. var scrollHeight = tableBody.scrollWidth > tableBody.clientWidth ? getScrollBarWidth() : 0;
  249. var paginationHeight = $(".fixed-table-pagination", that.$tableContainer).height();
  250. if (typeof that.options.height !== 'undefined') paginationHeight = 0;
  251. $fixedColumns.css({
  252. "height": "calc(100% - " + (paginationHeight + scrollHeight) + "px)"
  253. });
  254. $fixedBody.css({
  255. "height": "calc(100% - " + $fixedHeader.height() + "px)",
  256. overflow: "hidden"
  257. });
  258. };
  259. $(window).on("resize", resizeHeight);
  260. resizeHeight();
  261. return $fixedBody;
  262. };
  263. if (this.needFixedColumns && this.options.fixedNumber) {
  264. this.$fixedBody = initFixedBody(this.$fixedColumns, this.$fixedHeader);
  265. }
  266. if (this.needFixedColumns && this.options.fixedRightNumber) {
  267. this.$fixedBodyRight = initFixedBody(this.$fixedColumnsRight, this.$fixedHeaderRight);
  268. this.$fixedBodyRight.scrollLeft(this.$fixedBodyRight.find('table').width());
  269. this.$fixedBodyRight.css('overflow-y', 'hidden');
  270. }
  271. };
  272. BootstrapTable.prototype.getFixedColumnsWidth = function (isRight) {
  273. var visibleFields = this.getVisibleFields();
  274. var width = 0;
  275. var fixedNumber = this.options.fixedNumber;
  276. var marginRight = 0;
  277. if (isRight) {
  278. visibleFields = visibleFields.reverse();
  279. fixedNumber = this.options.fixedRightNumber;
  280. //右侧固定列距离
  281. this.$fixedColumnsRight.css('right', getTableBodyScrollBarWidth(this.$tableBody));
  282. }
  283. for (var i = 0; i < fixedNumber; i++) {
  284. width += this.$header.find('th[data-field="' + visibleFields[i] + '"]').outerWidth();
  285. }
  286. return width + 1;
  287. };
  288. BootstrapTable.prototype.initFixedColumnsEvents = function () {
  289. var that = this;
  290. var toggleHover = function toggleHover(e, toggle) {
  291. var tr = 'tr[data-index="' + $(e.currentTarget).data('index') + '"]';
  292. var $trs = that.$tableBody.find(tr);
  293. if (that.$fixedBody) {
  294. $trs = $trs.add(that.$fixedBody.find(tr));
  295. }
  296. if (that.$fixedBodyRight) {
  297. $trs = $trs.add(that.$fixedBodyRight.find(tr));
  298. }
  299. $trs.css('background-color', toggle ? $(e.currentTarget).css('background-color') : '');
  300. };
  301. this.$tableBody.find('tr').hover(function (e) {
  302. toggleHover(e, true);
  303. }, function (e) {
  304. toggleHover(e, false);
  305. });
  306. var isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
  307. var mousewheel = isFirefox ? 'DOMMouseScroll' : 'mousewheel';
  308. var updateScroll = function updateScroll(e, fixedBody) {
  309. var normalized = normalizeWheel(e);
  310. var deltaY = Math.ceil(normalized.pixelY);
  311. var top = that.$tableBody.scrollTop() + deltaY;
  312. if (deltaY < 0 && top > 0 || deltaY > 0 && top < fixedBody.scrollHeight - fixedBody.clientHeight) {
  313. e.preventDefault();
  314. }
  315. that.$tableBody.scrollTop(top);
  316. if (that.$fixedBody) {
  317. that.$fixedBody.scrollTop(top);
  318. }
  319. if (that.$fixedBodyRight) {
  320. that.$fixedBodyRight.scrollTop(top);
  321. }
  322. };
  323. if (this.needFixedColumns && this.options.fixedNumber) {
  324. this.$fixedBody.find('tr').hover(function (e) {
  325. toggleHover(e, true);
  326. }, function (e) {
  327. toggleHover(e, false);
  328. });
  329. this.$fixedBody[0].addEventListener(mousewheel, function (e) {
  330. //给鼠标滑轮绑定事件
  331. updateScroll(e, that.$fixedBody[0]);
  332. });
  333. //给固定表格的checkbox绑定事件
  334. this.$fixedBody.find('input[name="' + this.options.selectItemName + '"]').off("click").on('click', function (e) {
  335. e.stopImmediatePropagation();
  336. var index = $(e.target).data("index");
  337. $(that.$selectItem[index]).trigger("click");
  338. });
  339. //绑定TD点击事件
  340. this.$fixedBody.find('> table > tbody > tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) {
  341. var index = $(this).closest("tr[data-index]").data("index");
  342. $(that.$selectItem[index]).closest("tr[data-index]").find(">td:eq(" + $(this).index() + ")").trigger("click");
  343. });
  344. }
  345. //给原本表格绑定scroll事件
  346. $('div.fixed-table-body').off('scroll'); //给所有的body解绑 scroll
  347. this.$tableBody.off('scroll').on('scroll', function (e) {
  348. that.$tableHeader.scrollLeft(0);
  349. if (that.$tableBody.scrollLeft() > 0) {
  350. that.$tableHeader.scrollLeft(that.$tableBody.scrollLeft());
  351. if (that.options.showFooter && !that.options.cardView) {
  352. that.$tableFooter.scrollLeft(that.$tableBody.scrollLeft());
  353. }
  354. }
  355. var top = that.$tableBody.scrollTop();
  356. if (that.$fixedBody) {
  357. that.$fixedBody.scrollTop(top);
  358. }
  359. if (that.$fixedBodyRight) {
  360. that.$fixedBodyRight.scrollTop(top);
  361. }
  362. });
  363. if (this.needFixedColumns && this.options.fixedRightNumber) {
  364. this.$fixedBodyRight.find('tr').hover(function (e) {
  365. toggleHover(e, true);
  366. }, function (e) {
  367. toggleHover(e, false);
  368. });
  369. this.$fixedBodyRight[0].addEventListener(mousewheel, function (e) {
  370. //给鼠标滑轮绑定事件
  371. updateScroll(e, that.$fixedBodyRight[0]);
  372. });
  373. //给固定表格的checkbox绑定事件
  374. this.$fixedBodyRight.find('input[name="' + this.options.selectItemName + '"]').off("click").on('click', function (e) {
  375. e.stopImmediatePropagation();
  376. var index = $(e.target).data("index");
  377. $(that.$selectItem[index]).trigger("click");
  378. });
  379. //绑定TD点击事件
  380. this.$fixedBodyRight.find('> table > tbody > tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) {
  381. var index = $(this).closest("tr[data-index]").data("index");
  382. $(that.$selectItem[index]).closest("tr[data-index]").find(">td:eq(" + $(this).index() + ")").trigger("click");
  383. });
  384. }
  385. if (this.options.filterControl) {
  386. $(this.$fixedColumns).off('keyup change').on('keyup change', function (e) {
  387. var $target = $(e.target);
  388. var value = $target.val();
  389. var field = $target.parents('th').data('field');
  390. var $coreTh = that.$header.find('th[data-field="' + field + '"]');
  391. if ($target.is('input')) {
  392. $coreTh.find('input').val(value);
  393. } else if ($target.is('select')) {
  394. var $select = $coreTh.find('select');
  395. $select.find('option[selected]').removeAttr('selected');
  396. $select.find('option[value="' + value + '"]').attr('selected', true);
  397. }
  398. that.triggerSearch();
  399. });
  400. }
  401. };
  402. })(jQuery);