Jeremy Walters
Jeremy Walters

Reputation: 2152

Vue text highlight event

I have a data table containing clickable rows. All is working well, I encountered one problem. If I want to highlight text on the row, the click event is triggered. The only thing I found that could help is the .exact modifier. Hoping it will ignore the click handler if text is highlighted. But the click event is still triggered.

My Question: Is there a way I can highlight text on an item without triggering the click event.

Expected Result: Using @click.exact wont fire click event when highlighting text

Actual Result: Click event is fired when highlighting text, event using @click.exact

Side Note: It manages to hightlight the text, but as soon as you let the mouse button go, it triggers the click event.

<v-data-table
    v-show="model.id && !editMode"
    :headers="bagHeaders"
    :items="bags"
    class="elevation-1"
    item-key="id"
    :loading="bagsStatus.loading"
    :pagination.sync="pagination"
    >
        <template slot="items" slot-scope="props">
            <tr @click.exact="onBagClick(props.item.id)">
                <td class="text-xs-left" v-for="header in bagHeaders" :key="header.id">{{ formatColumn(header, props.item) }}</td>
            </tr>
        </template>
</v-data-table>

Edit: Other Attempts: @click.prevent also not working

Best work around so far: https://codepen.io/anon/pen/YBNLLy

Upvotes: 0

Views: 3382

Answers (1)

Yom T.
Yom T.

Reputation: 9180

OK, try @click.stop on the TDs which stops the event from propagating to the parent TR.

Since you want to preserve the normal row clicking behavior on certain condition, you could add a method for inspecting if any text-selection is being made while clicking and proceed with stopping the event propagation, otherwise invoke the onBagClick() method of the parent TR:

new Vue({
  el: '#app',

  methods: {
    onBagClick(id) {
      alert('Bag Click');
    },

    checkMouseAction(e) {
      const isTextHighlighting = window.getSelection().toString().trim() !== '';

      if (!isTextHighlighting) {
        e.target.parentElement.click();

        // Or call the this.onBagClick() instead
      }
    }
  }
})
table {
  border-collapse: collapse;
}

table td {
  border: 1px solid;
  padding: 10px;
}

table td:first-child {
  background-color: lavender;
}

table td:last-child {
  background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <table>
    <tr @click="onBagClick">
      <td @click.stop="checkMouseAction">Selectable text. Left-click and drag these lines with your mouse.</td>
      <td>Hardly selectable text. An alert dialog will get in the way by popping up.</td>
    </tr>
  </table>
</div>


Edits

The above will work too, but actually I figured out another obvious workaround: Do the text-selection checking on the table row level:

Working demo

new Vue({
  el: '#app',
  data() {
    return {
      headers: [
        {
          text: 'Dessert (100g serving)',
          align: 'left',
          sortable: false,
          value: 'name'
        }, 
        {
          text: 'Calories',
          value: 'calories'
        }, 
        {
          text: 'Fat (g)',
          value: 'fat'
        }, 
        {
          text: 'Carbs (g)',
          value: 'carbs'
        }, 
        {
          text: 'Protein (g)',
          value: 'protein'
        }, 
        {
          text: 'Iron (%)',
          value: 'iron'
        }
      ],
      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%'
        }
      ]
    }
  },
  methods: {
    onBagClick(id) {
      const isTextHighlighting = window.getSelection().toString().trim() !== '';

      if (!isTextHighlighting) {
        alert("Bag Click");
      }
    }
  }
})
@import url('https://fonts.googleapis.com/css?family=Roboto|Material+Icons');
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.js"></script>

<div id="app">
  <v-app id="inspire">
    <v-data-table :headers="headers" :items="desserts" class="elevation-1">
      <template slot="items" slot-scope="props">
        <tr @click="onBagClick(props.item.id)"> 
          <td class="text-xs-left" 
            v-for="header in props.item" 
            :key="header.id">{{header}}</td>
        </tr>
      </template>
    </v-data-table>
  </v-app>
</div>

I'm using window.getSelection() for reading selected texts. If you care about supporting IE 8 (and below), have a look at this post for a fallback text-selection-acquiring approach.

Upvotes: 1

Related Questions