Two way fixed scrollable table

Vue powered table with fixed column width

by leonsaysHi

HTML

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<div id="table">

  <div class="flex-table">

    <!-- header -->
    <div class="flex-table__header">
      <div class="-fixed">
        <div v-for="cell in tableDatas.header.fixedcols" v-html="cell.label" class="cell"></div>
      </div>
      <div class="-scrolled" ref="hcells">
        <div v-for="cell in tableDatas.header.cols" v-html="cell.label" class="cell"></div>
      </div>
    </div>

    <!-- body -->
    <div class="flex-table__rows" ref="rows">



      <div v-for="row in tableDatas.rows" class="flex-table__row">
        <div class="-fixed">
          <div v-for="cell in row.fixedcols" v-html="cell.label" class="cell"></div>
        </div>
        <div class="-scrolled">
          <div v-for="cell in row.cols" v-html="cell.label" class="cell"></div>
        </div>
      </div>
    </div>

  </div>

</div>

SCSS

.flex-table {
  $fixed-w: 300px;
  $scrolled-w: 200px;
  margin: 1rem;
  overflow: hidden;
  &,
  * {
    box-sizing: border-box;
  }
  &__header {
    display: flex;
    background-color: #ccc;
    .-scrolled {
      flex-grow: 1;
    }
  }
  &__rows {
    height: 200px;
    overflow: auto;
  }
  &__row {
    display: flex;
  }
  .-scrolled,
  .-fixed {
    position: relative;
    display: flex;
  }
  .-fixed {
    position: relative;
    z-index: 2;
    flex: 0 0 $fixed-w;
    background-color: #ccc;
  }
  .-scrolled .cell {
    flex: 0 0 $scrolled-w;
  }
  .-fixed .cell {
    flex: 1 0 0;
  }
  .cell {
    padding: .5em .5em;
  }
}

Vue

new Vue({
  el: "#table",
  data: {
  	table: {
      header: [
        { label: 'col1', fixed: true },
        { label: 'col2', fixed: true },
        { label: 'col3', fixed: true },
        { label: 'col4' },
        { label: 'col5' },
        { label: 'col6' },
        { label: 'col7' },
        { label: 'col8' }
      ],
      rows: [
        [{label: '1'}, {label: '2'}, {label: '3'}, {label: '4'}, {label: '5'}, {label: '6'}],
        [{label: '1'}, {label: '2'}, {label: '3'}, {label: '4'}, {label: '5'}, {label: '6'}],
        [{label: '1'}, {label: '2'}, {label: '3'}, {label: '4'}, {label: '5'}, {label: '6'}],
        [{label: '1'}, {label: '2'}, {label: '3'}, {label: '4'}, {label: '5'}, {label: '6'}],
        [{label: '1'}, {label: '2'}, {label: '3'}, {label: '4'}, {label: '5'}, {label: '6'}],
        [{label: '1'}, {label: '2'}, {label: '3'}, {label: '4'}, {label: '5'}, {label: '6'}],
        [{label: '1'}, {label: '2'}, {label: '3'}, {label: '4'}, {label: '5'}, {label: '6'}],
        [{label: '1'}, {label: '2'}, {label: '3'}, {label: '4'}, {label: '5'}, {label: '6'}]
      ]
    }
  },
  computed: {
  	tableDatas: function() {    
    	const obj = {
       header:{
      		fixedcols: this.table.header.filter(i => i.fixed),
          cols: this.table.header.filter(i => !i.fixed)
        },
        rows: this.table.rows.map(r => {
        	const _r = {
          	fixedcols: [],
            cols: []
          }
          this.table.header.every((c, idx) => {
            const cell = r[idx] || { label: '-' }
            if (c.fixed) {
              _r.fixedcols.push(cell)
            }
            else {
              _r.cols.push(cell)
            }
            return true
          })
          return _r
        })
      }
      console.log(obj)
      return obj
    }
  },
  mounted: function() { 
  	const 
    	$tbody = $(this.$refs.rows),
    	$tbody_cells = $tbody.find('.-fixed'),
    	$thead_cells = $(this.$refs.hcells)
  	$tbody.scroll(function(e) { 
 ...