Home > Software engineering >  Have a horizontal scrollbar on the top and bottom of a v-data-table (Vuetify)
Have a horizontal scrollbar on the top and bottom of a v-data-table (Vuetify)

Time:08-09

what happens is that I have a v-data-table, by default I have a horizontal scrollbar at the bottom, but I want this scrollbar both below and above the table.

I tried to research in several forums and articles but there is no information about this when it comes to a v-data-table.

Here a example of what I want to do

Here is the code:

    <template>
      <v-container>
    
         <v-data-table
        :headers="headers"
        :items="desserts"
        :items-per-page="5"
        
      ></v-data-table>
    
      </v-container>
    </template>
    
    <script>
      export default {
        name: 'HelloWorld',
    
        data () {
          return {
            headers: [
              {
                text: 'Dessert (100g serving)',
                align: 'start',
                sortable: false,
                value: 'name',
                width: 300
              },
              { text: 'Calories', value: 'calories', width: 300},
              { text: 'Fat (g)', value: 'fat', width: 300 },
              { text: 'Carbs (g)', value: 'carbs', width: 300 },
              { text: 'Protein (g)', value: 'protein', width: 300 },
              { text: 'Iron (%)', value: 'iron', width: 300 },
            ],
            desserts: [
              {
                name: 'Frozen Yogurt',
                calories: 159,
                fat: 6.0,
                carbs: 24,
                protein: 4.0,
                iron: '1%',
              },
              {
                name: 'Ice cream sandwich',
                calories: 237,
                fat: 9.0,
                carbs: 37,
                protein: 4.3,
                iron: '1%',
              },
              {
                name: 'Eclair',
                calories: 262,
                fat: 16.0,
                carbs: 23,
                protein: 6.0,
                iron: '7%',
              },
              {
                name: 'Cupcake',
                calories: 305,
                fat: 3.7,
                carbs: 67,
                protein: 4.3,
                iron: '8%',
              },
              {
                name: 'Gingerbread',
                calories: 356,
                fat: 16.0,
                carbs: 49,
                protein: 3.9,
                iron: '16%',
              },
              {
                name: 'Jelly bean',
                calories: 375,
                fat: 0.0,
                carbs: 94,
                protein: 0.0,
                iron: '0%',
              },
              {
                name: 'Lollipop',
                calories: 392,
                fat: 0.2,
                carbs: 98,
                protein: 0,
                iron: '2%',
              },
              {
                name: 'Honeycomb',
                calories: 408,
                fat: 3.2,
                carbs: 87,
                protein: 6.5,
                iron: '45%',
              },
              {
                name: 'Donut',
                calories: 452,
                

fat: 25.0,
            carbs: 51,
            protein: 4.9,
            iron: '22%',
          },
          {
            name: 'KitKat',
            calories: 518,
            fat: 26.0,
            carbs: 65,
            protein: 7,
            iron: '6%',
          },
        ],
      }
    },
  }
</script>

CodePudding user response:

Technically, a div cannot have more than one scrollbar on the same direction.

What we can do is create a second element, with dummy content of the same size as the content of the element we want to scroll, and then link the scrolling functions between the two elements. Simply put, when we scroll one, we also scroll the other.

Here's a vanilla implementation, demonstrating the principle:

window.addEventListener("DOMContentLoaded", () => {
  const updateScrollers = () => {
    dummy.style.width = _b.scrollWidth   "px";
  };
  updateScrollers();
  window.addEventListener("resize", updateScrollers);

  const linkScroller = (a, b) => {
    a.addEventListener("scroll", (e) => {
      b.scrollLeft = e.target.scrollLeft;
    });
  };
  [
    [_a, _b],
    [_b, _a],
  ].forEach((args) => linkScroller(...args));
});
#_a,
#_b {
  overflow-x: auto;
}

.content {
  height: 100px;
  width: 200vw;
}
#_a > div {
  height: 1px;
}
#_b .content {
  background: repeating-linear-gradient(
    45deg,
    #fff,
    #fff 50px,
    #f5f5f5 50px,
    #f5f5f5 80px
  );
}
<div id="_a">
  <div id="dummy"></div>
</div>
<div id="_b">
  <div ></div>
</div>

If you need help implementing it in your project, you'll have to create a runnable minimal, reproducible example first.


With Vue, all we need to do is update the model value (onScroll) and let Vue handle the DOM updates (e.g: :scroll-left.camel="scrollLeft").

const { createApp, onMounted, onBeforeUnmount, reactive, toRefs } = Vue;
createApp({
  setup: () => {
    const state = reactive({
      scrolled: null,
      dummy: null,
      scrollLeft: 0,
      onScroll: (e) => (state.scrollLeft = e.target.scrollLeft),
    });
    const updateScroller = () => {
      state.dummy.style.width = state.scrolled.scrollWidth   "px";
    };
    onMounted(() => {
      window.addEventListener("resize", updateScroller);
      updateScroller();
    });
    onBeforeUnmount(() => {
      window.removeEventListener("resize", updateScroller);
    });

    return toRefs(state);
  },
}).mount("#app");
.scroller,
.scrolled {
  overflow-x: auto;
}
.scroller > div {
  height: 1px;
}
.scrolled span {
  height: 100px;
  display: inline-block;
  width: 200vw;
  background: repeating-linear-gradient(
    45deg,
    #fff,
    #fff 50px,
    #f5f5f5 50px,
    #f5f5f5 80px
  );
}
<script src="https://unpkg.com/vue/dist/vue.global.prod.js"></script>
<div id="app">
  <div
    
    @scroll.passive="onScroll"
    :scroll-left.camel="scrollLeft"
  >
    <div ref="dummy"></div>
  </div>
  <div
    ref="scrolled"
    
    @scroll.passive="onScroll"
    :scroll-left.camel="scrollLeft"
  >
    <span />
  </div>
</div>

  • Related