2

I have a pretty big table in one of my Vue.js component. I want the current line and column to change color when the mouse is hover a cell.

For the row hover effect, it's pretty easy in CSS. But for the column, I have to do it in javascript (for various reason, I cannot use this trick in my current app).

Now, my problem is that using vue.js reactiveness to do this gets pretty slow when the table is too big. See the included snippet (I recommend opening it in full page) where the row hover effect is pretty reactive (done in CSS) and the column hover effect is very sluggish (done with vue.js).

I guess my solution is far from being the best so my question is: Is there a solution to get the column hover effect to be (almost) as reactive as the line hover effect ?

new Vue({
  el: "#app",
  data: {
    hovered: {
      col: -1,
    },
    columns: [...(new Array(50).keys())].map(c => ({
      label: c,
      index: c
    })),
    rows: [...(new Array(200).keys())].map(r => ({
      label: r,
      index: r
    }))
  },
  methods: {
    hoverCol(colIndex) {
      this.hovered.col = colIndex
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

table {
  width: 100%;
}

table tr:hover {
  background-color: green;
}

.bg-green {
  background-color: green;
}

.bg-grey {
  background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <table>
    <thead>
      <tr>
        <th v-for="col in columns" :key="`header-col-${col.index}`" :class="{
          'bg-grey': col.index === hovered.col
        }">
          col-{{col.label}}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in rows" :key="`row-${row.index}`">
        <td v-for="col in columns" :key="`cell-${col.index}-${row.index}`" @mouseenter="hoverCol(col.index)" :class="{
          'bg-green': col.index === hovered.col
        }">
          cell-{{col.label}}-{{row.label}}
        </td>
      </tr>
    </tbody>
  </table>
</div>

2
  • Out of curiosity, why can't you style both rows and columns with CSS? Commented Feb 23, 2021 at 14:46
  • The only CSS solution I found is the one I linked in the post and my vue component displaying the table is part of a more complex GUI, it's displayed in a popin, with some other sticky elements and what not, and I couldn't get the CSS trick to work in all that with its top: -5000px; height: 10000px; z-index: -1; style. But I'd love have a CSS solution, yes Commented Feb 23, 2021 at 14:50

1 Answer 1

2

Okay I got my solution, instead of using vue reactivity to change the hover class, I can set it directly on the element using refs. Speed is way better.

Snippet example :

new Vue({
  el: "#app",
  data: {
    colHover: null,
    columns: [...(new Array(50).keys())].map(c => ({
      label: c,
      index: c
    })),
    rows: [...(new Array(200).keys())].map(r => ({
      label: r,
      index: r
    }))
  },
  methods: {
    hoverCol(colIndex) {
        this.rows.forEach(r => {
        if (this.colHover !== null) {
            this.$refs[`cell_${this.colHover}_${r.index}`][0].classList = []
        }
        this.$refs[`cell_${colIndex}_${r.index}`][0].classList.add('bg-green')
      })
      this.colHover = colIndex
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

table {
  width: 100%;
}

table tr:hover {
  background-color: green;
}

.bg-green {
  background-color: green;
}

.bg-grey {
  background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <table>
    <thead>
      <tr>
        <th v-for="col in columns" :key="`header-col-${col.index}`">
          col-{{col.label}}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in rows" :key="`row-${row.index}`">
        <td
        v-for="col in columns"
        :key="`cell-${col.index}-${row.index}`"
        @mouseenter="hoverCol(col.index)"
        :ref="`cell_${col.index}_${row.index}`"
        >
          cell-{{col.label}}-{{row.label}}
        </td>
      </tr>
    </tbody>
  </table>
</div>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.