bootstrap-table-sticky-header.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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. $.extend($.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:eq(0)').find('th').each((index, el) => {
  87. $(el).css('min-width', this.$header.find('tr:eq(0)').find('th').eq(index).css('width'))
  88. })
  89. // match bootstrap table style
  90. this.$stickyContainer.show().addClass('fix-sticky fixed-table-container')
  91. // stick it in position
  92. let stickyHeaderOffsetLeft = this.options.stickyHeaderOffsetLeft
  93. let stickyHeaderOffsetRight = this.options.stickyHeaderOffsetRight
  94. if (this.$el.closest('.bootstrap-table').hasClass('fullscreen')) {
  95. stickyHeaderOffsetLeft = 0
  96. stickyHeaderOffsetRight = 0
  97. }
  98. this.$stickyContainer.css('top', `${this.options.stickyHeaderOffsetY}px`)
  99. this.$stickyContainer.css('left', `${stickyHeaderOffsetLeft}`)
  100. this.$stickyContainer.css('right', `${stickyHeaderOffsetRight}`)
  101. // create scrollable container for header
  102. this.$stickyTable = $('<table/>')
  103. this.$stickyTable.addClass(this.options.classes)
  104. // append cloned header to dom
  105. this.$stickyContainer.html(this.$stickyTable.append(this.$stickyHeader))
  106. // match clone and source header positions when left-right scroll
  107. this.matchPositionX()
  108. } else {
  109. this.$stickyContainer.removeClass('fix-sticky').hide()
  110. }
  111. }
  112. matchPositionX () {
  113. this.$stickyContainer.scrollLeft(this.$tableBody.scrollLeft())
  114. }
  115. }