What is the best way to fix multiple dropdown menus opening simultaneously in Vue.js?

I am working on an application that generates todo lists for tasks. These lists can be edited and deleted. I am trying to implement a dropdown menu in each list to provide options for updating or deleting the list individually. However, when I click on the dropdown icon, it expands all the dropdown menus simultaneously. Can someone assist me in resolving this issue? Below is my code in Vue.JS along with a screenshot of the dashboard displaying the lists.

DashboardView.vue:

<template>
  <div class="dashboard">
    <Navbar class="navbar__dash" />
    <h1 class="dashboard__welcome">Welcome {{ name }}</h1>
    <div class="listview">
      <div class="no__lists" v-if="len_list === 0">
        <h2 class="no_lists">There are No lists here tap to add</h2>
      </div>
      <div class="has__lists" v-else>
        <div class="all__lists" v-for="(item, index) in items" :key="index">
          <div class="list">
            <div class="title">
              <div class="title__options">
                <h1 class="list_name">{{ item.list_name }}</h1>
                <div class="dropdown">
                  <button @click="toggleMenu">
                    <fa class="dropdown_icon" icon="fa-solid fa-arrow-down" />
                  </button>
                  <div v-if="showMenu" class="menu">
                    <div
                      class="menu-item"
                      v-for="(item, index) in links_list"
                      :key="index"
                      @click="itemClicked"
                    >
                      {{ item.title }}
                    </div>
                  </div>
                </div>
                <!-- V-menu -->
                <!--menu ends-->
              </div>
            </div>
            <div class="body">
              <div class="no_tasks" v-if="item.no_of_tasks === 0">
                <h3>There are no tasks added yet</h3>
              </div>
              <div class="has_tasks" v-else>
                <h4>Here are your tasks</h4>
              </div>
            </div>
            <div class="footer">
              Number of Completed tasks: {{ item.no_completed }}/{{
                item.no_of_tasks
              }}
            </div>
          </div>
        </div>
      </div>
      <router-link :to="`/createList/${id}`">
        <fa class="plus__icon" icon="fa-solid fa-plus" />
      </router-link>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

import Navbar from '../components/NavBar.vue';
export default {
  name: 'DashboardView',
  data() {
    return {
      name: localStorage['name'],
      len_list: 0,
      items: [],
      id: localStorage['id'],
      links_list: [{ title: 'Click Me' }, { title: 'Click Me' }],
      showMenu: false,
    };
  },
  methods: {
    getTrackers() {
      const path = 'http://localhost:5000/dashboard/' + localStorage['id'];
      axios
        .get(path)
        .then((res) => {
          this.items = res.data.list;
          console.log(res.data.list);
          this.len_list = res.data.list[0]['len_list'];
        })

        .catch((err) => {
          console.log(err);
        });

      console.log(this.items);
    },
    toggleMenu() {
      this.showMenu = !this.showMenu;
    },
    itemClicked() {
      this.toggleMenu();
    },
  },
  components: {
    Navbar,
  },
  created() {
    this.getTrackers();
  },
};
</script>

<style>
.dashboard {
  width: 100%;
  min-height: 100vh;
  color: #ffd700;
  background-image: url('../assets/wood-texture.jpg');
  overflow-x: auto;
  overflow-y: hidden;
}
.dashboard__welcome {
  margin-top: 2rem;
  font-weight: 600;
}
.plus__icon {
  margin-left: 58rem;
  width: 32px;
  height: 32px;
  margin-top: 1rem;
  color: #ffd700;
  padding: 1rem;
  border: 1px solid #ffd700;
  border-radius: 50%;
}
.no_lists {
  text-align: center;
  margin-top: 15rem;
}

.navbar__dash {
  background-color: black;
  color: white;
  max-width: inherit;
}
.has__lists {
  display: flex;
}
.list {
  padding: 10rem;
  border: 1px solid #ffd700;
  border-radius: 2rem;
  width: 20%;
  margin-left: 1rem;
  margin-top: 5rem;
  padding-bottom: 0px;
}
.title {
  background: #ffd700;
  color: black;
  width: 320px;
  text-align: center;
  margin-left: -10rem;
  margin-top: -10rem;
  height: 100px;
  border-top-left-radius: 2rem;
  border-top-right-radius: 2rem;
}
.footer {
  margin-top: 10rem;
  background: #ffd700;
  color: black;
  width: 320px;
  margin-left: -10rem;
  margin-top: 0.01rem;
  margin-bottom: 0rem;
  height: 100px;
  border-bottom-left-radius: 2rem;
  border-bottom-right-radius: 2rem;
  text-align: center;
}
.body {
  background-color: white;
  width: 320px;
  color: grey;
  margin-left: -10rem;
  text-align: center;
  height: 10rem;
}
.list_name {
  margin-top: 0.6rem;
  padding: 2px;
  font-weight: 600;
  font-size: 20px;
  border-top-left-radius: 1rem;
  border-bottom-left-radius: 1rem;
}
.list__icon {
  width: 20px;
  height: 20px;
}
.title__options {
  display: flex;
  justify-content: center;
}
.dropdown_icon {
  padding: 0.2rem;
  color: #ffd700;
  background: black;
  margin-top: 15px;
  transition: var(--transition);
  border-radius: 2rem;
}
.dropdown_icon:hover {
  background: white;
  color: black;
  height: 20px;
}
.menu {
  background: white;
  padding-left: 2rem;
  padding-right: 2rem;
  border-radius: 1rem;
}
</style>

Screenshots:

https://i.sstatic.net/XYHpz.jpg https://i.sstatic.net/IUMq7.jpg

Answer №1

To ensure that each list functions independently, it is important to separate the relevant code. The best approach would be to isolate the section of the code responsible for displaying the items. By isolating the logic, each item can have its own showMenu variable, allowing only one item to be interacted with at a time.

Main Component

<template>
  <div class="dashboard">
    <Navbar class="navbar__dash" />
    <h1 class="dashboard__welcome">Welcome {{ name }}</h1>
    <div class="listview">
      <div class="no__lists" v-if="len_list === 0">
        <h2 class="no_lists">There are No lists here tap to add</h2>
      </div>
      <div class="has__lists" v-else>
        <div class="all__lists" v-for="(item, index) in items" :key="index">
            <Items :item="item"/> //Call Component Items
        </div>
      </div>
      <router-link :to="`/createList/${id}`">
        <fa class="plus__icon" icon="fa-solid fa-plus" />
      </router-link>
    </div>
  </div>
</template>

<script>
import axios from "axios";

import Navbar from "../components/NavBar.vue";
export default {
  name: "DashboardView",
  data() {
    return {
      name: localStorage["name"],
      len_list: 0,
      items: [],
      id: localStorage["id"],
      links_list: [{ title: "Click Me" }, { title: "Click Me" }],
    };
  },
  methods: {
    getTrackers() {
      const path = "http://localhost:5000/dashboard/" + localStorage["id"];
      axios
        .get(path)
        .then((res) => {
          this.items = res.data.list;
          console.log(res.data.list);
          this.len_list = res.data.list[0]["len_list"];
        })

        .catch((err) => {
          console.log(err);
        });

      console.log(this.items);
    }
  },
  components: {
    Navbar, Items
  },
  created() {
    this.getTrackers();
  },
};
</script>

Child Component (Items)

<template>
<div>
  <div class="list">
    <div class="title">
      <div class="title__options">
        <h1 class="list_name">{{ item.list_name }}</h1>
        <div class="dropdown">
          <button @click="toggleMenu">
            <fa class="dropdown_icon" icon="fa-solid fa-arrow-down" />
          </button>
          <div v-if="showMenu" class="menu">
            <div
              class="menu-item"
              v-for="(item, index) in links_list"
              :key="index"
              @click="itemClicked"
            >
              {{ item.title }}
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="body">
      <div class="no_tasks" v-if="item.no_of_tasks === 0">
        <h3>There are no tasks added yet</h3>
      </div>
      <div class="has_tasks" v-else>
        <h4>Here are your tasks</h4>
      </div>
    </div>
    <div class="footer">
      Number of Completed tasks: {{ item.no_completed }}/{{ item.no_of_tasks }}
    </div>
  </div>
</div>
</template>

<script>
export default {

  props: {
    item: Item,
  },

  data() {
    return {
      showMenu: false,
    };
  },
  
  methods: {
    toggleMenu() {
      this.showMenu = !this.showMenu;
    },
    itemClicked() {
      this.toggleMenu();
    },
  },
};
</script>

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

Creating an array dynamically by parsing a JSON object in Javascript

I am currently working with a JSON object that looks like this: var testJSON = [ { "AssetA": "asset_a", "AssetB": "asset_b" }, { "AssetA": "asset_a", "AssetB": "asset_b" }, { "AssetA": "asset_c", "AssetB": "asset_d" }, { "AssetA": "asset_c", " ...

A guide on extracting the text content from an anchor tag by using xPath() with a combination of selenium and Mocha

I have successfully chosen an <a> tag. My goal is to display the text of the anchor tag, but I am facing difficulties. The technologies being used are selenium, mocha, javascript, and phantomJS This is the detailed script: var assert = require(&ap ...

Learn how to implement the PATCH method specifically for the scenario when the user clicks on the "forgot password"

After clicking on the forgot password link, I want to display the password change form. However, I'm facing issues with calling the API to update the user's password. Below are the files I'm utilizing to develop the API, trigger events, mana ...

Execute JavaScript using "matches" without the need for the page to refresh

I have been developing a school project which involves creating a Chrome extension to streamline the Checkout process on a website. In my manifest.json file, I specified that a content.js file should run when it matches a specific URL. "content_script ...

Simple methods for ensuring a minimum time interval between observable emittance

My RxJS observable is set to emit values at random intervals ranging from 0 to 1000ms. Is there a way to confirm that there is always a minimum gap of 200ms between each emission without skipping or dropping any values, while ensuring they are emitted in ...

Utilizing a Static Image Path Prop in React JS: A Step-by-Step Guide

My Main Component import React from "react"; import TopModal from "./components/topModal"; // Passing productImage to the Child Component import productImage from "../../../../../../assets/images/juices.jpg"; import { makeS ...

Replacing an array element by a specific index number in a React application

I am dealing with an array of [false,true,false,false,true] and I am looking to update the boolean values based on their index numbers. I have been trying to solve this problem in react, but haven't found a workable solution yet. this.setState({ ...

Mastering the art of transitioning between DIV elements

I am looking to implement a rotating three-card display on click, and have come up with the following code: $('.box1').click(function(){ $('.box1').toggleClass('removeanimate'); $(this).toggleClass('go'); ...

Quickly remove items from a list without any keywords from the given keywords list

This spreadsheet contains two sheets named "RemoveRecords" and "KeywordsList". I need to use app scripts to remove any records that are not included in the "KeywordsList" sheet. This should be done by searching through the "ArticleLink" column. Although ...

Instant Access to a Precise Slide

Using a slider named mySlider or SL_Slider, which is powered by the MooTools library. When on the page with the slider, there is a simple script for the href to call the appropriate slide: <a href="javascript:mySlider.numPress(3);">link</a> ...

Using jQuery's toggle function with a double click event to change the display to none

A div I created has the ability to expand to full screen upon double click, and I now wish to implement a toggle function so it can return to its original size when double clicked again. Initially, the code successfully increased the size of the div. Howe ...

Issue with translating grid header names in Ag-Grid Vue using i18n without requiring a refresh after changing the language

Localization in my project is done using i18n, but I'm facing an issue where changing the language causes all page translations to display correctly except for the grid header names. The problem resolves itself when I refresh the page. How can I addre ...

Is there a way to properly direct to an internal page while utilizing [routerLink] and setting target="_blank" without triggering a full page reload?

I'm attempting to use [routerLink] along with target="_blank" to direct to an internal page without triggering a full app reload. Currently, the page opens in a new tab, but the whole application is refreshed. Here is the HTML: <a [routerLink]=" ...

How can we efficiently add a new node to a polyline link in gojs while maintaining the original positions of points in the links adjacent to the inserted node

After posting a question on StackOverflow (Seeking Javascript library for displaying and editing networks of nodes and edges), I was directed to the gojs splice sample. The sample has been helpful, but I've hit a roadblock trying to achieve the speci ...

What is the best way to showcase the chosen items in a dropdown menu?

There seems to be an issue with the selected item not appearing in the input field. export default function BasicSelect() { const [sortBy, setSortBy] = useState(""); const [condition, setCondition] = useState(""); const [delivery, ...

Unable to make changes to the AWS Dynamo array

I am currently attempting to update a JSON array. I have a table in DynamoDB where I successfully inserted the JSON data using the PUT method. Partition key : _id , Type : S Below is the JSON data that has been saved into the database: { "_id": ...

Attempting to place the search bar within the navigation bar

Having trouble positioning a search bar in my nav bar, I'd like it to show on the right side without overlapping any other elements. Tried relative and absolute positioning with no success so far. Any assistance would be greatly appreciated, thank yo ...

Develop an HTML table using either JSON or jQuery in Javascript

JavaScript is a bit of a mystery to me right now - I could really use some assistance in overcoming this obstacle that has me pulling my hair out! I'm struggling with constructing HTML code using JSON data. It's just not clicking for me at the m ...

I am attempting to establish a connection with the Converge Pro 2 system from Clearone using NodeJS node-telnet-client, but unfortunately, my efforts to connect have been unsuccessful

My connection settings are as follows: { host: '192.168.10.28', port: 23, shellPrompt: '=>', timeout: 1500, loginPrompt: '/Username[: ]*$/i', passwordPrompt: '/Password: /i', username: 'clearone ...

React - assigning a value to an input using JavaScript does not fire the 'onChange' event

In my React application with version 15.4.2, I am facing an issue where updating the value of a text input field using JavaScript does not trigger the associated onChange event listener. Despite the content being correctly updated, the handler is not being ...