How can I add an event listener back to a dynamically updated array of div elements in Vue 3?

I need assistance with creating a dynamic array of divs that will apply a class to both the div itself and a child element when hovered over. The array is generated by a computed function, as shown in the following code snippet:


 <article
    v-for="(article, index) in filteredArticles"
    :key="article.id"
    ref="articleRefs"
    :ref-key="index"
    @mouseenter="handleClass(index, 'add')"
    @mouseleave="handleClass(index, 'remove')"
>
    <!-- Content -->
</article
const articleRefs = ref<HTMLDivElement[]>([])
const articleList = ref([] as Article[])

const filteredArticles = computed(() => {
    return activeTag.value !== ''
        ? articleList.value.filter((a) => a.tags.some((t) => t.Tag_id.title === activeTag.value))
        : articleList.value
})

watch(
    filteredArticles.value,
    () => articleRefs.value = []
)

const handleClass = (index: number, action: 'add'|'remove') => {
    const hoveredArticle = articleRefs.value[index]
    hoveredArticle.classList[action]('animate')
    hoveredArticle.querySelector('h2')?.classList[action]('glitch')
}

Initially, everything works fine. However, if an article that was previously filtered out is displayed again, hovering over it does not trigger the desired effect.

I would appreciate any insights on what might be causing this issue. Thank you!

Answer №1

If you encounter an issue while trying to map the index of your array looping through articles to the index of the refs, be aware that the order may not remain consistent with your data array as per Vue documentation.

It's important to note that the ref array does not guarantee a matching order to the source array.

This mismatch in indexing can lead to problems when utilizing the for loop index along with the refs array index.

Possible Solution

To address this issue, consider separating your data logic from presentation logic by creating a new Vue component called SingleArticle. This approach ensures that Vue manages the update logic seamlessly without manual intervention.

1. Single Article Component

  • The root class animated is dynamically added based on hover state by the framework
  • If article content is passed as HTML string, utilize querySelector to apply a class to the specified element inside
<!-- Vue SFC template: -->
<article
  ref="articleRef"
  :class="{ animated: isHover }"
  v-html="content"
  @mouseenter="isHover = true"
  @mouseleave="isHover = false"
/>
// Vue SFC script (or script setup)
import { ref, defineComponent } from 'vue';

export default defineComponent({
  name: 'SingleArticle',
  props: {
    // Include any necessary article props here
    // Example props are provided for demonstration purposes
    content: String,
    // ...
  },
  setup(props) {
    var articleRef = ref<HTMLElement>(null);
    var isHover = ref<boolean>(false);

    watch(isHover, (currIsHover) => {
      const action = currIsHover ? 'add' : 'remove';
      articleRef.value?.querySelector('h2')?.classList[action]('glitch');
    });
    
    return {
      articleRef,
      isHover,
    }
  }
});

2. Implement the New Component within v-for

<!-- Inside Vue SFC template -->
<single-article
 v-for="(article, index) in filteredArticles"
 :key="article.id"
 :content="article.content"
/>

// Main component handles filtering logic
// Hovering behavior is encapsulated within the single article component
export default defineComponent({
  setup() {
    const articleList = ref<Article[]>([]);

    const filteredArticles = computed(() => {
      if (!activeTag.value) return articleList.value;

      return articleList.value.filter(
        (a) => a.tags.some((t) => t.Tag_id.title === activeTag.value)
      );
    }
    
    return {
      filteredArticles,
    };
  }
})

Final Considerations

This implementation has not been tested and is purely conceptual.

The proposed solution effectively segregates filter logic from article presentation, resulting in two distinct components that are easily testable and comprehensible. Vue's reactivity ensures seamless updates without additional effort.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A guide on eliminating repetitions from an array of objects by utilizing the spread operator

I am working with an object array that has a unique key called "id": var test = [ {id: 1, PlaceRef: "*00011", Component: "BATH", SubLocCode: "BAT", BarCode: ""}, {id: 2, PlaceRef: "*00022", Component: "BAXI10R", SubLocCode: "KIT", BarCode:""}, {id: ...

Encountering problem: 'Dom7 is undefined' while initializing a Framework7 - Vue application using Webpack

Initially, I followed the steps outlined on the official website: cloning the repository, installing node dependencies, and running the application. However, upon executing the npm run dev command, an error was encountered when trying to open the app in th ...

Retrieve information from a MongoDB database and present it on a webpage using Handlebars (hbs) or HTML files in

I recently delved into learning node.js and decided to build a "Todo-App". Currently, I am faced with the challenge of transferring data from my mongodb database into my hbs files for display. My understanding is that data moves from server.js (server) t ...

Tips for retrieving data from an API with AJAX

I'm looking to send a post request to an API () using Ajax, but I'm feeling a bit confused about the process. This is the code snippet I've attempted so far: $.ajax({ type: "POST", url: "https://mapp.nairabox.com:84 ...

Breaking down a multidimensional array into individual arrays using Javascript

Having a serious problem here, I have an array structured like this: [[0,50],[0,68],[1,26],[2,9],[2,32]] I am looking to split this array into two separate arrays like so: array1 = [[0,50][1,0][2,9]] array2 = [[0,68][1,26][2,32]] Yes, you are correct g ...

Can you point me in the direction of the jQuery library link?

Can anyone assist me in locating the direct link to the jquery library for inclusion on my website? I have tried searching on the official website, but it doesn't seem to have the information I need or it's not clear enough. I only need the link ...

Use jQuery to swap out images and witness the image loading in real time

Currently, I am using jQuery to dynamically change images by using the code $('img').attr('src','newUrl'); However, whenever I do this, the image is only displayed once it has completely loaded. Due to my slow internet conne ...

Issue with ESLint: Unexpected token found in JavaScript when converting to a dictionary

I've implemented a JavaScript code snippet that loops through an array of fields to find specific properties and then adds them to a dictionary. For another example, you can check out this site. return this.getFields() .reduce((mappings, field) =& ...

Tips for maintaining reactivity in nested object properties in external JavaScript class files without relying on Vue.set()

In my possession is this particular component: <template> <div class="simple-editor"> {{editor.view.toolbarManager.buttons}} <component v-for="(button, name) in editor.view.toolbarManager.buttons" ...

The passport.use method is failing to invoke in Node.js when utilizing the passport-local strategy

Upon calling the login and submitting the form, it seems that the use.authenticate() method is not being executed and no error messages are displayed. Server.js code snippet: const passport=require('passport'); const Strategy=require('pass ...

The phonegap page redirection is failing, as the 'location' property of the object does not function correctly

I'm attempting to implement a straightforward page redirection in PhoneGap using JavaScript. Within an iframe, I have the following code: window.parent.location("event_select.html"); Unfortunately, this approach is unsuccessful and results in the fo ...

Calculating Two Results for Two Target Elements in Vue.js

Is it possible to have two different outcomes for result() in Vue's computed based on the element id? For instance, I'd like to return a result with commas replaced by AND for #and, and another result with commas replaced by - for #dash. https:/ ...

Ways to convert a buffered document into an object

Currently, I am transferring a file to my express server and because I am utilizing a cloud service, my file is pre-processed into a buffer <buffer 2o 7x 52o...>. While I know I can convert this to text by using JSON.stringify(buffer), once it's ...

I encountered an error while setting up Vue.js on my computer

While attempting to install Vue.js on my system using the command npm i -g @vue/cli, I encountered the following error: npm WARN cleanup Failed to remove some directories [ npm WARN cleanup [ npm WARN cleanup 'C:\\Users\\ ...

What is the process for creating two columns with an input box beneath them?

I am facing a challenge with my code. I am struggling to create the desired design where there are two columns and below them an input box that will be displayed when a button is pressed. The design I am aiming for can be viewed here: enter image descripti ...

The ExpressJS Req.method TypeError occurs when attempting to read the 'method' property of an undefined object

My node express server is throwing an error: Error in index.js. const bodyParser = require('body-parser'), express = require('express'), path = require('path'); const config = require('./config'); con ...

Selecting a URL from a website

While learning to create a link sharing system, I hit a roadblock. I wanted to grab the URL of the current webpage I had open in my browser. For example, if I was on "google.com", I wanted the link to be captured. Additionally, I aimed to paste this link ...

PHP - Determine the value based on the button that was clicked by the user and return either true

I am attempting to create a PHP alternative for JavaScript's confirm() function: <!DOCTYPE html> <?php function customConfirm($title, $text) { $html = '<div id="alert" style="background-color:white; text-align:center; hei ...

Ensure that user-provided text is securely stored within a div element to prevent

Users can input their email address in a textbox. When they press the "space" key, a new div is created with this text. An issue arises when users enter harmful scripts like <script>alert(1);</script>. If the value of the textbox is accessed u ...

exciting command: templateUrl

I am in need of assistance with a particular issue: I am trying to configure settings for an application and would like to utilize the UI-Bootstrap accordion. This is the HTML code I have so far: <accordion close-others="oneAtATime"> <accor ...