bootstrap-table-sticky-header.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /**
  2. * @author vincent loh <vincent.ml@gmail.com>
  3. * @update J Manuel Corona <jmcg92@gmail.com>
  4. * @update zhixin wen <wenzhixin2010@gmail.com>
  5. */
  6. const Utils = $.fn.bootstrapTable.utils
  7. Object.assign($.fn.bootstrapTable.defaults, {
  8. stickyHeader: false,
  9. stickyHeaderOffsetY: 0,
  10. stickyHeaderOffsetLeft: 0,
  11. stickyHeaderOffsetRight: 0
  12. })
  13. $.BootstrapTable = class extends $.BootstrapTable {
  14. initHeader (...args) {
  15. super.initHeader(...args)
  16. if (!this.options.stickyHeader) {
  17. return
  18. }
  19. this.$tableBody.find('.sticky-header-container,.sticky_anchor_begin,.sticky_anchor_end').remove()
  20. this.$el.before('<div class="sticky-header-container"></div>')
  21. this.$el.before('<div class="sticky_anchor_begin"></div>')
  22. this.$el.after('<div class="sticky_anchor_end"></div>')
  23. this.$header.addClass('sticky-header')
  24. // clone header just once, to be used as sticky header
  25. // deep clone header, using source header affects tbody>td width
  26. this.$stickyContainer = this.$tableBody.find('.sticky-header-container')
  27. this.$stickyBegin = this.$tableBody.find('.sticky_anchor_begin')
  28. this.$stickyEnd = this.$tableBody.find('.sticky_anchor_end')
  29. this.$stickyHeader = this.$header.clone(true, true)
  30. // render sticky on window scroll or resize
  31. const resizeEvent = Utils.getEventName('resize.sticky-header-table', this.$el.attr('id'))
  32. const scrollEvent = Utils.getEventName('scroll.sticky-header-table', this.$el.attr('id'))
  33. $(window).off(resizeEvent).on(resizeEvent, () => this.renderStickyHeader())
  34. $(window).off(scrollEvent).on(scrollEvent, () => this.renderStickyHeader())
  35. this.$tableBody.off('scroll').on('scroll', () => this.matchPositionX())
  36. }
  37. onColumnSearch ({ currentTarget, keyCode }) {
  38. super.onColumnSearch({ currentTarget, keyCode })
  39. this.renderStickyHeader()
  40. }
  41. resetView (...args) {
  42. super.resetView(...args)
  43. $('.bootstrap-table.fullscreen').off('scroll')
  44. .on('scroll', () => this.renderStickyHeader())
  45. }
  46. getCaret (...args) {
  47. super.getCaret(...args)
  48. if (this.$stickyHeader) {
  49. const $ths = this.$stickyHeader.find('th')
  50. this.$header.find('th').each((i, th) => {
  51. $ths.eq(i).find('.sortable').attr('class', $(th).find('.sortable').attr('class'))
  52. })
  53. }
  54. }
  55. horizontalScroll () {
  56. super.horizontalScroll()
  57. this.$tableBody.on('scroll', () => this.matchPositionX())
  58. }
  59. renderStickyHeader () {
  60. const that = this
  61. this.$stickyHeader = this.$header.clone(true, true)
  62. if (this.options.filterControl) {
  63. $(this.$stickyHeader).off('keyup change mouseup').on('keyup change mouse', function (e) {
  64. const $target = $(e.target)
  65. const value = $target.val()
  66. const field = $target.parents('th').data('field')
  67. const $coreTh = that.$header.find(`th[data-field="${ field }"]`)
  68. if ($target.is('input')) {
  69. $coreTh.find('input').val(value)
  70. } else if ($target.is('select')) {
  71. const $select = $coreTh.find('select')
  72. $select.find('option[selected]').removeAttr('selected')
  73. $select.find(`option[value="${ value }"]`).attr('selected', true)
  74. }
  75. that.triggerSearch()
  76. })
  77. }
  78. const top = $(window).scrollTop()
  79. // top anchor scroll position, minus header height
  80. const start = this.$stickyBegin.offset().top - this.options.stickyHeaderOffsetY
  81. // bottom anchor scroll position, minus header height, minus sticky height
  82. const end = this.$stickyEnd.offset().top - this.options.stickyHeaderOffsetY - this.$header.height()
  83. // show sticky when top anchor touches header, and when bottom anchor not exceeded
  84. if (top > start && top <= end) {
  85. // ensure clone and source column widths are the same
  86. this.$stickyHeader.find('tr').each((indexRows, rows) => {
  87. const columns = $(rows).find('th')
  88. columns.each((indexColumns, celd) => {
  89. $(celd).css('min-width', this.$header.find(`tr:eq(${indexRows})`).find(`th:eq(${indexColumns})`).css('width'))
  90. })
  91. })
  92. // match bootstrap table style
  93. this.$stickyContainer.show().addClass('fix-sticky fixed-table-container')
  94. // stick it in position
  95. const coords = this.$tableBody[0].getBoundingClientRect()
  96. let width = '100%'
  97. let stickyHeaderOffsetLeft = this.options.stickyHeaderOffsetLeft
  98. let stickyHeaderOffsetRight = this.options.stickyHeaderOffsetRight
  99. if (!stickyHeaderOffsetLeft) {
  100. stickyHeaderOffsetLeft = coords.left
  101. }
  102. if (!stickyHeaderOffsetRight) {
  103. width = `${coords.width}px`
  104. }
  105. if (this.$el.closest('.bootstrap-table').hasClass('fullscreen')) {
  106. stickyHeaderOffsetLeft = 0
  107. stickyHeaderOffsetRight = 0
  108. width = '100%'
  109. }
  110. this.$stickyContainer.css('top', `${this.options.stickyHeaderOffsetY}px`)
  111. this.$stickyContainer.css('left', `${stickyHeaderOffsetLeft}px`)
  112. this.$stickyContainer.css('right', `${stickyHeaderOffsetRight}px`)
  113. this.$stickyContainer.css('width', `${width}`)
  114. // create scrollable container for header
  115. this.$stickyTable = $('<table/>')
  116. this.$stickyTable.addClass(this.options.classes)
  117. // append cloned header to dom
  118. this.$stickyContainer.html(this.$stickyTable.append(this.$stickyHeader))
  119. // match clone and source header positions when left-right scroll
  120. this.matchPositionX()
  121. } else {
  122. this.$stickyContainer.removeClass('fix-sticky').hide()
  123. }
  124. }
  125. matchPositionX () {
  126. this.$stickyContainer.scrollLeft(this.$tableBody.scrollLeft())
  127. }
  128. }