0

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 class="greeting"> {{zoom}}
   <p>Some paragraph text</p>
    <h1>some heading 1 text</h1>
    <h2>some heading 2 text</h2>
    <div class="link">
    <a href="#" @click="zoomIt()" id="size-12">12</a>
    </div>
    <div class="link">
    <a href="#" @click="zoomIt()" id="size-14">14</a>
    </div>
    <div class="link">
    <a href="#" @click="zoomIt()" id="size-16">16</a>
    </div>
    <div class="link">
    <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>
prov
  • 3
  • 3

1 Answers1

0

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 class="links">
  <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
}
3tw
  • 146
  • 5
  • Perfect. Your code is perfectly working. I am new to vue and this is the firstever code I wrote using it. Thank you very much for detailed explanation. Keep going. But one thing is I can not see any text in zoom action unless I add it before – prov Oct 22 '22 at 05:07
  • Yes, you should add the text before it in the template. I have only posted the part of the template I changed, the buttons, the rest remains the same. (But instead of {{zoom}}, you now need to write {{currentZoom}} as this is the name of the variable in my example. Please upvote the comment if you find it useful for beginners like yourself. Good luck! – 3tw Oct 22 '22 at 07:37