0

I have a hard time to make tootips working in loop. I am using vuejs3, tailwindcss3, and flowbite. I would like to use tooltip on my button for additional information.

When I used hard coded id, tooltip works great, but when I tired to use the tooltip with id and data-tooltip-target assigned pragmatically in a loops, tooltip is not working when first time page loaded.

<li v-for="(shift, idx) in shiftsMatrix" :key="idx" class="relative flex cursor-pointer" :class="`col-start-${shift.x}`" :style="`grid-row: ${shift.y}`" @click="showUpdateSchedule(shift.self)" >

  <div 
    :data-tooltip-target="`tooltip-${shift.self.id}`" data-tooltip-style="light" 
    class="group absolute inset-1 z-30 flex items-center rounded px-1 text-xs truncate overflow-x-hidden leading-5"
    :class="['TO', 'SL'].includes(shift.shift_role) ? 'bg-gray-100' : storeStore.branchColors[shift.branch % 11]"
  >
    <p class="text-gray-900 font-medium group-hover:text-red-900 mx-auto">
      <span>{{ shift.time }}</span>
      <span v-if="shift.shift_role==='OW'">&#10029;</span>
    </p>
  </div>
  <div v-if="shift.self.note" :id="`tooltip-${shift.self.id}`" role="tooltip" class="absolute z-50 invisible inline-block px-3 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg shadow-sm opacity-0 tooltip">
      {{ shift.self.note }}
      <!-- <div class="tooltip-arrow" data-popper-arrow></div> -->
  </div>

</li>

Weird thing is that it starts to work after I have been to different pages even though lots of errors in a console, saying index.ts:243 The tooltip element with id "tooltip-519" does not exist. Please check the data-tooltip-target attribute.

How can I make this works in a loop?

Thanks in advance...

1 Answers1

0

The problem is that flowbite gets initialised before the loop is rendered. You need to make sure flowbite is initialised after the loop has rendered so that the data attributes are taken into account.

Solution 1

You can initialise flowbite manually on mounted

<script setup>
import {initFlowbite} from "flowbite";

// ...

onMounted(() => {
    initFlowbite()
})
</script>

Or

<script setup>
import {initFlowbite} from "flowbite";
import {nextTick} from "vue";

// ...

onMounted(() => {
    nextTick(() => {
        initFlowbite()
    })
})
</script>

Solution 2

If you are fetching your data for example from an endpoint, this may take time and the loop won't still be rendered after the component is mounted, hence the init flowbite will still be called before your loop is rendered. So you need to make sure to call initFlowbite after your loop has rendered.

For example:

<script setup>
import {initFlowbite} from "flowbite";
import {ref} from "vue";

const shiftsMatrix = ref([])

// ...

return axios.get(route('my.route')).then((response) => {
    shiftsMatrix.value = response.data.data
}).finally(() => {
    // Here
    initFlowbite()
})
</script>

Solution 3

You can show the tooltips programmatically.

<li v-for="(shift, idx) in shiftsMatrix" :key="idx" class="relative flex cursor-pointer" :class="`col-start-${shift.x}`" :style="`grid-row: ${shift.y}`" @click="showUpdateSchedule(shift.self)" >

  <div
    data-tooltip-style="light" 
    @mouseover="showTooltip(`tooltip-${shift.self.id}`, 'hover', $event)"
    class="..."
  >
    <p class="text-gray-900 font-medium group-hover:text-red-900 mx-auto">
      ...
    </p>
  </div>
  <div v-if="shift.self.note" :id="`tooltip-${shift.self.id}`" role="tooltip" class="absolute z-50 invisible inline-block px-3 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg shadow-sm opacity-0 tooltip">
      {{ shift.self.note }}
      <div class="tooltip-arrow" data-popper-arrow></div>
  </div>

</li>

And in your script:

<script setup>
import {Tooltip} from "flowbite";

// ...

const showTooltip = (targetElId, triggerType, event) => {
    const targetEl = document.getElementById(targetElId)
    const tooltip = new Tooltip(targetEl, event.target, {triggerType})
}
</script>
Prince Dorcis
  • 955
  • 7
  • 7