Extract the sub-element within a parent element using Vue event handling

Objective

The main aim is to add a class to the dropdown element .menu-item-dropdown whenever the user clicks on the .menu-item element.

Issue

Currently, when clicking on the .menu-item element, the returned value from the console.log inside the showMobileNavDropdown() function is null. Upon clicking, the event.target refers to .menu-item-main instead of .menu-item. This happens specifically when I click on the text of the li. Otherwise, it works as expected.

What would be the most effective approach to include the text within the li so that the .menu-item-dropdown class can still be targeted?

Vue.js

<template>
    <div>
        <nav class="nav">
            <div class="nav-left">
                <img class="logo" src="/images/logo.svg" alt="" />
            </div>
            <div class="nav-center">
                <ul class="menu-items">
                    <li
                        class="menu-item"
                        @click="showMobileNavDropdown($event)"
                    >
                        <!-- Main Title On Nav -->
                        <a class="menu-item-main" href="#">Company</a>

                        <!-- Dropdown -->
                        <div class="menu-item-dropdown">
                            <ul>
                                <li class="">
                                    <a href="">About</a>
                                </li>
                                <li class="">
                                    <a href="">Contact</a>
                                </li>
                            </ul>
                        </div>
                    </li>
                </ul>
            </div>

            <div class="nav-right">
                <a href="" class="btn">Contact us</a>
            </div>

            <div class="hamburger" @click="openMobileNav">
                <span class="ham-line"></span>
                <span class="ham-line ham-line-2"></span>
            </div>
        </nav>

        <div class="mobile-nav"></div>
    </div>
</template>

<script>
export default {
    methods: {
        openMobileNav() {
            var mobileNav = document.querySelector(".mobile-nav");
            mobileNav.classList.toggle("showMobileNav");
        },

        // This function
        showMobileNavDropdown(event) {
            console.log(event); // The element 'menu-item-main' is retrieved instead of 'menu-item'
            console.log(event.target.querySelector(".menu-item-dropdown"));
        },
    },
};
</script>

Answer №1

If you are dealing with a lengthy list of potential click-targets and cannot or do not want to simply utilize refs, the recommended approach would be to employ event-delegation. In the click-handler function, ascertain if the clicked element resides within a clickable parent. Based on this, toggle a specific class on the parent element. This class can then be used in CSS to target the child element, for instance, altering its opacity.

Vue.component('foo-bar', {      
  methods: {
    toggleSubItemVisibility({ target }) {
      const item = target.closest('.nav-item');
      // check is important as user could click on surrounding ul
      if (item) {
        item.classList.toggle('active');
      }
    }
  },
  template: `
  <nav>
    <ul @click="toggleSubItemVisibility">
      <li class="nav-item">
        <span class="nav-label">Foo</span>
        <div class="nav-sub-item">More details...</div>
      </li>
      <li class="nav-item">
        <span class="nav-label">Bar</span>
        <div class="nav-sub-item">More details...</div>
      </li>
    </ul>  
  </nav>`
})

new Vue({
  el: '#app'
});
nav ul { 
  list-style-type: none;
  display: flex;
  flex-direction: row;
  gap: 12px;
}

.nav-label {
  cursor: pointer;
}

.nav-sub-item {
  opacity: 0;
  transition: opacity 1s ease;
}

.nav-item.active .nav-sub-item {
  opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<foo-bar></foo-bar>
</div>

Answer №2

In order to address the issue, the solution involved first attempting to select the element itself. If this selection returned null, then the script would attempt to select the sibling instead. This approach ensured that the .menu-item-dropdown element could always be selected, regardless of whether it was a child or sibling element.

Key Component of the Resolution

var targetElement =
        event.target.querySelector(".menu-item-dropdown") ||
        event.target.nextElementSibling;

targetElement.classList.toggle("hide_drop");

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

Methods for updating the value of a `<select>` element in an AngularJS controller

Within my HTML code, I have a select element with options ranging from 1 to 10: <select id="selVal" ng-model="product.quantity" ng-options="o as o for o in quantityValues" ng-change="updateDelta(product.quantity, {{product.quantity}}, product.selec ...

How to share information between ES6 classes?

Recently, I decided to create a node/express app just for fun. One of the components I built is an ES6 class called 'TwitterClient.es6' that interfaces with the Twitter API to fetch data. Now, in my 'server.es6', which handles the route ...

Chronological Overview (Highcharts)

Can you customize a timeline in Highcharts to resemble the image? I have a functional example of the timeline I desire, but the color coding and filtering aspects are challenging for me. I am attempting to apply a filter that will decrease the opacity of ...

Highcharts is experiencing a duplication issue with the Y-Axis Series

This is a snippet from my code: $.get('https://dl.dropboxusercontent.com/u/75734877/data.csv', function (data) { var lines = data.split('\n'); $.each(lines, function (lineNo, line) { var items = line.split(',& ...

Node(Meteor) experiencing a memory leak due to setTimeout

I have encountered an unusual memory leak associated with the use of setTimeout. Every 15 seconds, I execute the following code using an async function that returns an array of promises (Promise.all). The code is supposed to run again 15 seconds after all ...

React and the turn.js library (please note that turn is not a predefined function)

I'm trying to integrate turn.js with React by following an example I found here: https://codesandbox.io/s/005xlk45mn After adapting the code to my project, I encountered the following error: TypeError: jquery__WEBPACK_IMPORTED_MODULE_6___default(... ...

Issue with jqGrid when filtering small numerical values

Happy Holidays! I recently came across an issue while trying to filter small numbers using jqGrid. I am filtering numbers that can vary from 10 to 1, down to values as small as 10^(-8) or even smaller. The filtering works well for these numbers until they ...

Creating a standalone executable for Node.js without including the entire Node.js framework

Trying to pack a simple hello world script into an executable has led to some challenges. Both pkg and nexe seem to include the entirety of Node.js in the output file, resulting in quite larger files than necessary (around 30 MB). Although EncloseJS was fo ...

jQuery does not have the capability to access the href attribute through DOM manipulation

I've been trying to extract the href attribute from a link in my code and create a new link using that attribute. However, I'm facing an issue where the created link doesn't seem to work properly - it keeps showing a 404 error message like t ...

The PHP script's header() function is failing to execute

Recently, I encountered an issue with my JavaScript code that calls a backend PHP script using AJAX. The main function of the AJAX request is to send user login data (username and password) to the PHP script, which in turn queries this information on the S ...

An issue has been discovered with the Search function as JavaScript's Array.filter() and .map() methods are not functioning properly, resulting in

Currently, I'm working on integrating a search feature into my Flask application that will display the cities entered by users and are present in the JSON API results of a weather API. I am following a tutorial and have used a code similar to this: h ...

Employing a pair of interdependent v-select components to prevent any duplicate entries

I am currently working with two v-select boxes that share similar data. In my scenario, I extract attachments from an email and load them into an array. The issue I encountered is that the first select box should only allow the selection of one document, w ...

Problem with Ionic App crashing

Currently, I am developing an Ionic app that relies on local storage for offline data storage. The app consists of approximately 30 different templates and can accommodate any number of users. Local storage is primarily used to store three key pieces of i ...

How can a single item from each row be chosen by selecting the last item in the list with the radio button?

How can I ensure that only one item is selected from each row in the list when using radio buttons? <?php $i = 1; ?> @foreach ($products as $product) <tr> <td scope="row">{{ $i++ }}</td> <td>{{ ...

What is the process for executing JavaScript code that is stored as a string?

After making an AJAX call, I receive a random string (constructed dynamically on the server) that contains JavaScript code like: Plugins.add('test', function() { return { html: '<div>test</div&g ...

Ways to eliminate an item from an array when the value of the object is not present

I am facing an issue with removing objects from an array that do not contain the data object. I have attempted to use the filter method but have not been successful. results.filter(obj => obj.data === undefined) results = [ {id: 1, location: 'a&a ...

Vue ESLint error: Missing space before function parentheses issue

I just finished setting up a project using npm and vuejs/vue-cli. Inside my package.json file, I have eslint configurations specified. However, when I tried running my code, I encountered a warning: WARNING Compiled with 1 warnings 5:57:37 AM ✘ ...

Creating a variable to store the data retrieved from a package

Imagine you have a functioning code snippet like this: const myPackage = require('myPackage'); myPackage.internal_func(parameter).then(console.log); This outputs a JSON object, for example: { x: 'valX', y: 'valY' } ...

Capture a snapshot of a webpage that includes an embedded iframe

Currently, we have a nodeJS/angular 4 website that contains an iframe from a third party (powerBI Emebdded). Our goal is to develop a feature that allows the end user to capture a screenshot of the entire page, including the content within the iframe. We ...

Tips for transferring the id from the url to a php function seamlessly without causing a page refresh

I have a div that includes a button (Book it). When the button is clicked, I want to append the id of the item I clicked on to the current URL. Then, use that id to display a popup box with the details of the clicked item without refreshing the page, as it ...