Vuetify - v-tooltip-Not displaying with keyboard tap button

148 Views Asked by At

I have a shared component using Vuetify's v-tooltip, and it works perfectly when hovering with the mouse. However, when I use the tap button to access a button that has the tooltip, it doesn't work. Can someone help me troubleshoot this issue?

Here's the code for my tooltip component:

<template>
  <v-tooltip
    v-bind="$attrs"
    :open-on-focus="true"
    :content-class="`${tooltipClass} custom-tooltip ${color} darken-2`"
    open-delay="250"
    max-width="288px"
  >
    <template v-slot:activator="{ on, attrs }">
      <div v-bind="attrs" v-on="on">
        <slot />
      </div>
    </template>
    <span class="tooltip-text">{{ getTooltipText() }}</span>
  </v-tooltip>
</template>

And here's how I use it:

<tooltip
  tooltip-text="edit metadata"
  bottom
  position="bottom"
>
  <EditExternalButton
    :external-link="item.editLink"
    :data-test="`result-list-item-edit-external-${i + 1}`"
    event-name-string="metadata_edit clicked"
  />
</tooltip>

I tried to add :open-on-focus="true"

in props but it didn't work

1

There are 1 best solutions below

2
On BEST ANSWER

A regular div usually cannot be focused, so you'll never reach it through the keyboard and the handler won't be triggered.

There are several ways around it:

  • add tabindex="0" to the div to make it tabbable:
<template v-slot:activator="{ on, attrs }">
  <div v-bind="attrs" v-on="on" tabindex="0">
    <slot />
  </div>
</template>

const tooltip = {
  template: `
    <v-tooltip
      v-bind="$attrs"
      :open-on-focus="true"
      open-delay="250"
      max-width="288px"
    >
      <template v-slot:activator="{ on, attrs }">
        <div v-bind="attrs" v-on="on" tabindex="0">
          <slot />
        </div>
      </template>
      <span class="tooltip-text">getTooltipText()</span>
    </v-tooltip>
  `
}

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  components: {tooltip},
  template: `
    <v-app>
      <v-main>
        <v-container>
          <tooltip>
            <div style="background: pink;">activator content</div>
          </tooltip>
        
        </v-container>
      </v-main>
    </v-app>
  `,
  setup(){
  
  }
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">

<div id="app"></div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>

  • use a natively tabbable element (like button) instead of a div as activator:
<template v-slot:activator="{ on, attrs }">
  <button v-bind="attrs" v-on="on">
    <slot />
  </button>
</template>

const tooltip = {
  template: `
    <v-tooltip
      v-bind="$attrs"
      :open-on-focus="true"
      open-delay="250"
      max-width="288px"
    >
      <template v-slot:activator="{ on, attrs }">
        <v-btn v-bind="attrs" v-on="on">
          <slot />
        </v-btn>
      </template>
      <span class="tooltip-text">getTooltipText()</span>
    </v-tooltip>
  `
}

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  components: {tooltip},
  template: `
    <v-app>
      <v-main>
        <v-container>
          <tooltip>
            <div style="background: pink;">activator content</div>
          </tooltip>
        
        </v-container>
      </v-main>
    </v-app>
  `,
  setup(){
  
  }
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">

<div id="app"></div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>

  • pass the focus-related handlers on to the slot content and use it there:
<template v-slot:activator="{ attrs, on: {mouseenter, mouseleave, focus, blur, keydown} }">
  <div v-bind="attrs" v-on="{mouseenter, mouseleave}">
    <slot :on="{focus, blur, keydown}"/>
  </div>
</template>

const tooltip = {
  template: `
    <v-tooltip
      v-bind="$attrs"
      :open-on-focus="true"
      open-delay="250"
      max-width="288px"
    >
      <template v-slot:activator="{ attrs, on: {mouseenter, mouseleave, focus, blur, keydown} }">
        <div v-bind="attrs" v-on="{mouseenter, mouseleave}">
          <slot :on="{focus, blur, keydown}"/>
        </div>
      </template>
      <span class="tooltip-text">getTooltipText()</span>
    </v-tooltip>
  `
}

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  components: {tooltip},
  template: `
    <v-app>
      <v-main>
        <v-container>

          <tooltip v-slot="{on}">
            <v-checkbox v-on="on"/>
          </tooltip>
      
        </v-container>
      </v-main>
    </v-app>
  `,
  setup(){
  
  }
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">

<div id="app"></div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>