Home > other >  In Vue SFC link click is not triggered first time
In Vue SFC link click is not triggered first time

Time:10-22

I am using Vue 3 to show a set of links for which I am assigning event handlers dynamically(based on link id). The issues is: The first time when any link is clicked, the corresponding event is not triggered. But subsequently clicks are perfectly working.

The updated code is below:

<script setup>
  const makeSizer = ([...sizes]) => {
    sizes.map((size) =>{
      console.log('size-'   size);
       document.getElementById('size-'   size).style.display = "";
       document.getElementById('size-'   size).onclick = ((e) =>{ 
        e.preventDefault();    
        document.body.style.fontSize =  e.target.text   'px';
        e.target.style.display = "none";
      });
       });
  };

   function zoomIt(){
    return {
      zoom: makeSizer([12,14,16,18])
    }
   } 

</script>
<template>
<div > {{zoom}}
   <p>Some paragraph text</p>
    <h1>some heading 1 text</h1>
    <h2>some heading 2 text</h2>
    <div >
    <a href="#" @click="zoomIt()" id="size-12">12</a>
    </div>
    <div >
    <a href="#" @click="zoomIt()" id="size-14">14</a>
    </div>
    <div >
    <a href="#" @click="zoomIt()" id="size-16">16</a>
    </div>
    <div >
    <a href="#" @click="zoomIt()" id="size-18">18</a> 
    </div>
 </div>
</template>

<style>
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}
h2 {
  font-size: 1.2em;
}
.link{
   padding:5px; display:inline-table;
 }
.greeting {
  color: red;
  font-weight: bold;
}
.greeting a{
   border:2px solid blue;
   padding:3px;
   color:white;
   background-color:blue;
}
#size-12{ font-size:12px;}
#size-14{ font-size:14px;}
#size-16{ font-size:16px;}
#size-18{ font-size:18px;}
</style>

CodePudding user response:

The bad news is, the way you approach it is an anti-pattern in Vue. The good news is, with some small changes you will end up with code that is much more simple to read and maintain!

You are doubling your event listeners by calling onclick() inside makeSizer() and defining click events via @click.

However, let us not just fix the bug by altering the existing code. What we want to do is to get rid of the anti-patern. So instead, we try passing the desired value of 'zoom' to the handler directly and avoid the beforementioned duplications altogether.

// Script
// We define a function that adjusts zoom value using only the value that is being passed to it as an argument
setZoom(size) {*code*}

// Template
<button @click.prevent="setZoomTo(12)">

This is a general idea. I modified your code a bit more to make it more maintainable and added comments where changes were made. I hope this helps.

Script

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

const currentZoom = ref(12); // Let us set default zoom to 12
const zoomOptions = [12, 14, 16, 18]; // We define zoom options as an array to dynamically generate buttons

function setZoomTo(size) {
  currentZoom.value = size; // Set current zoom value
  document.body.style.fontSize = currentZoom.value   "px";  // Adjust fontSize on body
}
</script>

Template

<div >
  <button // We use button tag for semantic correctness
    v-for="zoom in zoomOptions" // For every value in zoomOptions a button is created
    :key="zoom"
    :disabled="zoom === currentZoom" // If zoom value represented by the button is also currentZoom value => add disabled attribute to the button
    @click.prevent="setZoomTo(zoom)" // Adjust currentZoom value according to the zoom value represented by the button
  >
    {{ zoom }} // Button's zoom value
  </button>
</div>

Style

.links {
  display: flex;
  gap: 16px;
}

.links button {
  border: 2px solid blue;
  padding: 3px;
  color: white;
  background-color: blue;
  cursor: pointer;
}

.links button:disabled {
  opacity: 0.7; // For better UX we change button's opacity instead of hiding it
}
  • Related