Dynamically showcase dropdown menus within a single component

I am looking to implement multiple dropdowns in a single component, using one variable to control their display and also have them close when clicking away from the dropdown. Here is my code snippet:

<div class="dropdown">
  <button @click.prevent="isOpen = !isOpen"></button>
    <div v-show="isOpen">Content</div>
</div>
// Second dropdown within the same component
<div class="dropdown">
  <button @click.prevent="isOpen = !isOpen"></button>
    <div v-show="isOpen">Content</div>
</div>
data() {
  return {
    isOpen: false
  }
},
watch: {
  isOpen(isOpen) {
    if(isOpen) {
      document.addEventListener('click', this.closeIfClickedOutside)
    }
  }
},
methods: {
  closeIfClickedOutside(event){
    if(! event.target.closest('.dropdown')){
      this.isOpen = false;
    }
  }
}

However, I am encountering an issue where clicking on one dropdown menu displays both of them. As a newcomer to Vue, I am struggling to find a solution to this problem.

Answer №1

To simplify the process and ensure smooth functionality, it is recommended to use a variable that can identify which dropdown is currently open, rather than using a Boolean value. One effective approach is to store an index (such as a number) in the variable and then conditionally render the selected dropdown based on this index:

  1. Begin by declaring a data property to hold the selected index:

    export default {
      data() {
        return {
          selectedIndex: null
        }
      }
    }
    
  2. Next, update the closeIfClickedOutside() method to reset the selected index, effectively closing any open dropdowns:

    export default {
      methods: {
        closeIfClickedOutside() {
          this.selectedIndex = null
        }
      }
    }
    
  3. In the template, adjust the click event handlers to set the selected index accordingly:

    <button @click.stop="selectedIndex = 1">Open 1</button>
    <button @click.stop="selectedIndex = 2">Open 2</button>
    
  4. Additionally, update the conditions for v-show to display content based on the index:

    <div v-show="selectedIndex === 1">Content 1</div>
    <div v-show="selectedIndex === 2">Content 2</div>
    

It's also advisable not to rely on a watcher to handle adding a click event listener to the document, as we need to detect outside clicks when the component is active. A better approach would be to add the event listener in the mounted hook and remove it in the beforeDestroy hook:

export default {
  mounted() {
    document.addEventListener('click', this.closeIfClickedOutside)
  },
  beforeDestroy() {
    document.removeEventListener('click', this.closeIfClickedOutside)
  },
}

Check out the demo here.

Answer №2

Creating an array and iterating through it simplifies the process.

<template>     
<div id="app">
        <div class="dropdown" v-for="(drop, index) in dropData" :key="index">
      <button @click="openDropdown(index);">{{ drop.title }}</button>
        <div v-show="isOpen === index">{{ drop.content }}</div>
    </div>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          isOpen: null,
          dropData: [
            {
              title: "Hey",
              content: "Hey it's content 1"
            },
              {
              title: "Hey 2",
              content: "Hey it's content 2"
            },
              {
              title: "Hey 3",
              content: "Hey it's content 3"
            },
          ]
        };
      },
     
    methods: {
        openDropdown(idx){
         if (this.isOpen === idx) {
              this.isOpen = null;
            } else {
            this.isOpen = idx;
            }
    
        }
    
    }
    };
    </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

Exploring the capabilities of nested WebGL render targets in the three.js library

I am currently developing an application for the Oculus Rift using JavaScript, three.js, and OculusRiftEffect.js. In order to create a transparent portion of a 2D ring for the menu, I am attempting to generate a MeshBasicMaterial.alphaMap texture on a Web ...

Display a list of items in Angular using ng-repeat, and allow the full description to appear in full width

<div ng-controller = "MyController"> <ul class="items" > <div ng-repeat="item in colors" ng-class="{active:isActive(item)}" ng-click="select(item); whattoshow=!whattoshow"> <li class="col-md-3 col-sm-3 col-lg-3 co ...

Ways to divide elements of an array into pairs

I am working with a javascript array containing values [A,B,C,D,E.....] that needs to be split using the following syntax: [ [A, B], [C, D], [E, F] ] This splitting process should always result in pairs. I attempted to achieve this using a loop to retur ...

Having difficulty with Bootstrap buttons functioning correctly within a React environment

I am currently working on creating a responsive navigation bar in React. One issue I am facing is that when I zoom in on the page, the links disappear and are replaced with a button that triggers a drop-down list with the links. However, when I click on t ...

Hold off on continuing the script until the AJAX response has been received

In my script, I have two AJAX calls. The first one checks if a record exists in a database and should stop the script if it does. The second call submits a job. However, I am facing an issue where the job is being submitted before the first AJAX call retu ...

AJAX: Send a value, perform a query based on this value, and retrieve a JSON array

Can anyone explain why this code isn't functioning properly? I am attempting to submit a value from a form to a PHP file that performs a select query in my database using that value. The goal is to return a JSON array to Ajax and display all the resul ...

Displaying JSON data in HTML proves to be a challenge

I have collected JSON data and organized it into two arrays, then displayed it in JSON format. Now I'm trying to showcase this data in HTML using two loops by correlating the IDs of one array with the userIds of another array. {"personaldetails":[{"i ...

Creating a function in React to be passed as a prop

Recently, I was given the task of enhancing a user interface as a small challenge to help me dive into the world of programming. My goal is to incorporate a date picker into my search bar to allow users to filter their search results by selecting specific ...

Tips for altering the appearance of a button when moving to a related page

I have a master page with four buttons that have a mouse hover CSS property. Each button's corresponding response page is defined on the same master page. Now, I want to change the button style when the user is on the corresponding page. How can this ...

Unable to access a nested JSON object that has a repeated name

I'm relatively new to working with JSON, so the issue I'm facing may be simple, but I haven't been able to find a similar problem on stackoverflow. Here's my question: My goal is to access a nested JSON object like: pizza.topping.ratin ...

Trying to add a single value to a specific index in a JavaScript array, but it is mistakenly assigning multiple values at once

Currently tackling a matrix algorithm with an early roadblock. The array at hand is: [ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ] The goal is to convert it into this: [ [ 0, 0, 0 ], [ 0, 9, 0 ], [ 0, 0, 0 ] ] My plan was to alter the middle value like so ...

Type-safe Immutable.js Records with TypeScript

I'm struggling to find a suitable solution for my query. I am aiming to define data types using an interface in TypeScript, but my data consists of Immutable.js records making it more complex. Please refer to the example provided below. interface tre ...

What could be the reason behind the unexpected outcome of process.argv[3] when its value is set to '*'?

My calculator app is very straightforward: if (process.argv[3]==='+') console.log(parseInt(process.argv[2]) + parseInt(process.argv[4])); if (process.argv[3]==='-') console.log(parseInt(process.argv[2]) - parseInt(process.argv[4])); i ...

Issue in Jquery: Unable to load the corresponding pages when toggling the checkbox on and off

I am facing an issue with a checkbox and calling different php pages based on the status of the checkbox. Currently, the code works only for checked checkboxes. I'm not sure why it's not working for unchecked checkboxes as well. <script type ...

Utilizing the Kraken.com API to create a message signature with AngularJS

I'm currently tackling Angular2 and for my first project, I want to tap into the Kraken.com API. (I know, I could have chosen an easier task :) The "Public" calls are working smoothly, but I've hit a snag with the "Private" methods. (Those requi ...

Leveraging the power of Discord.js to verify the image URL within an embed

The concept My goal is to create a bot that can detect embedded images in messages and send a predetermined response if the image URL matches a specific URL provided. I am looking for a way to trigger this check whenever a message is sent with an embed. ...

Get rid of empty spaces in gridstack js

My latest project involves using gridstack js In the image displayed, I have highlighted the excess white space (in blue) that needs to be removed. Take a look at this visual: https://i.sstatic.net/Qgt62.jpg Any suggestions on how to eliminate this ...

Difficulty understanding D3 Angular: color fill remains unchanged

While developing an angular application with D3 JS, I encountered an issue with changing the fill color of an SVG. Upon reviewing the code, I noticed that I created one SVG and attempted to insert another one from a vendor: function link (scope, element, ...

Using JavaScript to implement CSS3 with multiple background images

Is there a way to programmatically define multiple background images in CSS3 using JavaScript? While the common approach would be: var element = document.createElement( 'div' ); element.style.backgroundImage = "url('a.png') 0 100%, ur ...

Bug in Click Behavior of HTML5 Canvas on Webkit Browser Versions Below 535 for Android Native and iOS5 Safari

Recently, I encountered a peculiar bug in Android where positioning a canvas element on the Native Android Browser (WebKit 534.4) using css position:absolute; top:10px; left:100px; resulted in the duplicated Canvas being rendered to the browser while ignor ...