How to get a Vue b-table to scroll the body with a fixed header

15,114

Solution 1

Since v2.0.0-rc.28, optional props sticky-header has been added to support fixed headers.

new Vue({
  el: '#app',
  data: {
    items: [
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' },
      { heading1: 'table cell', heading2: 'table cell', heading3: 'table cell' }
    ],
  }
});
<html>

<header>
  <script src="//unpkg.com/[email protected]/dist/vue.min.js"></script>
  <script src="//unpkg.com/[email protected]/dist/bootstrap-vue.js"></script>

  <link type="text/css" rel="stylesheet" href="//unpkg.com/[email protected]/dist/css/bootstrap.min.css" />
  <link type="text/css" rel="stylesheet" href="//unpkg.com/[email protected]/dist/bootstrap-vue.min.css" />
</header>

<body>
  <div id="app">
    <b-table sticky-header :items="items" head-variant="light"></b-table>
  </div>
</body>

</html>

Solution 2

I need to find a way to be able to scroll just the table body while leaving the column headers in place, and I'd prefer to be able to do it within the bounds of the element itself instead of rigging up something with completely separate header and body with a bunch of Javascript rigging in the background

I made an example repository for you to see how to make it work (also has hidden scrollbar).

There are probably other ways to accomplish the same thing, but this is what I did.

<!-- wrap table in container with class. -->
    <b-container fluid class="table-container">
      <b-table
        :items="items" 
        :fields="fields" 
        caption-top
      >
      </b-table>
    </b-container>

And here is the CSS with hidden scrollbar.

.table-container {
     height: calc(100vh - 450px);
}
 .table-container table {
     display: flex;
     flex-flow: column;
     height: 100%;
     width: 100%;
}
 .table-container table thead {
     flex: 0 0 auto;
     width: 100%;
}
 .table-container table tbody {
     flex: 1 1 auto;
     display: block;
     width: 100%;
     overflow-y: auto;
}
 .table-container table tbody tr {
     width: 100%;
}
 .table-container table thead, .table-container table tbody tr {
     display: table;
     table-layout: fixed;
}
 .table-container table {
     border-collapse: collapse;
}
 .table-container table td, .table-container table th {
     padding: 0.4em;
}
 .table-container table th {
     border: 1px solid black;
     font-size: 0.7vw;
}
 .table-container table td {
     border: 1px solid #e7e1e1;
     font-size: 0.85em;
    /* necessary to set for proper "showing row x of y" calculations if infinate scoll */
     white-space: nowrap;
     text-align: center;
     padding: 10px 5px;
     white-space: nowrap;
     text-overflow: ellipsis;
}
 .table-container table thead {
     border: 2px solid #0F0FA3;
}
 .table-container th {
     background-color: #0F0FA3;
     color: #b5aba4;
     cursor: pointer;
     -webkit-user-select: none;
   -moz-user-select: none !important;
     -ms-user-select: none;
     user-select: none;
}
 .table-container table tbody td {
     padding: 8px;
     cursor: pointer;
}
 .table-container table tbody tr:hover {
     background-color: rgba(168, 168, 239, .5);
}
 .table-container table thead td {
     padding: 10px 5px;
}
 .table-container tr:nth-child(even) {
     background-color: rgba(168, 168, 239, 1);
}

/* START Adjustments for width and scrollbar hidden */
 .table-container th.table-action, .table-container td.table-action {
     width: 5.8vw;
}
 .table-container table thead {
     width: calc(100% - 1px);
}
 .table-container table tbody::-webkit-scrollbar {
     width: 1px;
}
 .table-container table tbody::-webkit-scrollbar {
     width: 1px;
}
 .table-container table tbody::-webkit-scrollbar-thumb {
     width: 1px;
}
/* END Adjustments for width and scrollbar */

Update, another way to have fixed header but very restricted (ref):

There are a few ways you can do this... and cross browser is not always guaranteed:

create a class, say, my-table-scroll

table.my-table-scroll,
table.my-table-scroll > thead,
table.my-table-scroll > tbody,
table.my-table-scroll > tfoot,
table.my-table-scroll > tbody > tr,
table.my-table-scroll > thead > tr {
  width: 100%;
  display: block
}
table.my-table-scroll > thead,
table.my-table-scroll > tbody,
table.my-table-scroll > tfoot {
  display: block;
  width: 100%;
  overflow-y: scroll;
}
table.my-table-scroll > thead,
table.my-table-scroll > tfoot {
  height: auto;
}
/* these last two rules you will need to tweak on an as needed basis */
table.my-table-scroll > tbody {
  /* set max height for tbody before it scrolls */
  max-height: 200px;
}
table.my-table-scroll > thead > tr > th,
table.my-table-scroll > thead > tr > td,
table.my-table-scroll > tbody > tr > th,
table.my-table-scroll > tbody > tr > td,
table.my-table-scroll > tfoot > tr > th,
table.my-table-scroll > tfoot > tr > td {
  display: inline-block;
  /* you need to explicitly specify the width of the table cells */
  /* either equal, or specify individual widths based on col index. */
  /* Here, we assume you have 4 columns of data */
  width: 25%;
}
/*
  could use sibling selectors to select specific columns for widths:
  td + td (second column)
  td + td + td (3rd column, etc)
*/

And then place the class onto your b-table.

Share:
15,114
Abion47
Author by

Abion47

Updated on June 23, 2022

Comments

  • Abion47
    Abion47 almost 2 years

    I've got a b-table element on a page that currently displays a bunch of data from a database. At the moment it is paginated, but feedback has indicated that they would rather have all the information displayed in a single scrolling view. I can do this, but the problem is that if I set a containing div to scroll the whole table, it also scrolls the column headers away. I need to find a way to be able to scroll just the table body while leaving the column headers in place, and I'd prefer to be able to do it within the bounds of the element itself instead of rigging up something with completely separate header and body with a bunch of Javascript rigging in the background.

    Under the component reference of the bootstrap-vue table component, it says that there is a property called tbody-class which looks like it's a way to specify a custom class for the tbody (crazily enough). The page doesn't give any indication of how to use it, though, and my experimentation hasn't produced any results:

    <b-table 
        tbody-class="my-class"   <- Applies prop to table but not to tbody
        :tbody-class="my-class"  <- Seemingly ignored entirely
    >
    

    It sounds like this sort of issue was solved on this issue thread, but it doesn't really detail how it was solved. It mentions that the functionality was added to "the next update", but neither the patch notes of the version released following that comment nor the documentation mentions that addition at all (unless it means the properties I mentioned in the previous paragraph). It does talk about using CSS selectors to apply the style in a roundabout fashion, but I wasn't able to get that to work either. (In the following example, the tbody in the Chrome inspector did not have the applied style.)

    .table.my-table > tbody {
        height: 100px;
    }
    

    The version of Vue I'm using is 2.2.6.