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

Having trouble figuring out the reason my JavaScript code isn't functioning properly. Any ideas?

Just starting out with javascript and running into an issue, This snippet of code seems to be working as expected: function test(args){ return "12345 - "+args; } console.log(test("678910")); However, this other piece of code is ...

Retrieve information about the clicked item using the Backbone framework

I have a unique webpage that showcases an impressive collection of books. Each book listed on the page comes with essential information such as the title, price, and description. This data is imported from a JSON file. Excitingly, when users click on any ...

Vue's beforeRouteEnter hook patiently awaits for the child component to complete its request

My Vue app utilizes beforeRouteEnter to load data and prevent the undesirable "flash of unloaded content." Loading data on some pages is straightforward: async beforeRouteEnter(to, from, next) { const newestPosts = await getNewestPosts(); next(vm ...

Using JavaScript to interact with elements inside an iframe on a webpage

In a simple scenario, I am experiencing incorrect results. Code snippet from MyFrame.HTML: <!DOCTYPE html> <html> <head> <title>My Frame</title> </head> <body> <a href="https://www.google.com& ...

Difficulty commencing a background operation in a node.js application

I have a setup using node.js/express and React for the client side code. This setup allows users to query any number of players by making fetch requests to my express server, which then sends requests to Riot Games public API. The issue I'm encounteri ...

Create personalized CustomElements in real-time

I developed a helper function to dynamically set up all CustomElements: let moduleDefaults = new Map(); let customElementsMap = new Map(); const registerComponents = () => { // ^ Check for .ce files -> then register components for (const [ke ...

How come when you add ({}+{}) it equals to "[object Object][object Object]"?

I ran the following code: {}+{} = NaN; ({}+{}) = "[object Object][object Object]"; What is the reason behind the difference in result when adding ()? ...

Encountering an error: [nsIWebProgressListener::onStatusChange] when utilizing jQuery AJAX within a click event?

Greetings! I am currently learning how to implement AJAX with jQuery to load an HTML document into a div element within another HTML document. Here is the approach I am using: function pageload() { $.ajax({ url: 'Marker.aspx', ...

Developing an object that displays animated effects when modifying its properties, such as visibility, and more

Exploring the realm of animations in JavaScript and AngularJS has led me to the idea of creating a unique JavaScript object. Imagine having the ability to define an object where setting an attribute like obj.visible = true Would automatically make the ...

Troubleshooting problems with the guildMemberAdd event handler

As I continue on my journey with discord.js v12, I've encountered yet another issue with an event handler. While most events have been working fine so far, such as message or ready, I am currently facing a challenge with guildMemberAdd event. My goal ...

Trouble fetching data for my controller in AngularJS using UI Router resolve

My attempts to inject a resolve object containing loaded data into my controller are resulting in an Unknown Provider error : Error message: Unknown provider: configServiceProvider <- configService Below is the code I am working with: StateProvider ...

The argument type 'string' does not match the parameter type 'keyof Chainable' in Cypress JavaScript

Trying to incorporate a unique custom command in Cypress (commands.js file) as follows: Cypress.Commands.add("login", (email, password) => { cy.intercept('POST', '**/auth').as('login'); cy.visit(& ...

How can one break down enum values in typescript?

I've defined an enum in TypeScript as shown below: export enum XMPPElementName { state = "state", presence = "presence", iq = "iq", unreadCount = "uc", otherUserUnreadCount = "ouc", sequenc ...

Sending data between Angular and Python using both strings and JSON formats

Seeking assistance with a Python script that sends events to a server. Here is the code snippet: LOGGER = logging.getLogger("send_event") POST_EVENT_URL = "http://localhost:3000/event/" def send(name, data): url = POST_EVENT_URL + name headers = {& ...

Adjusting color with the .on method in Event Listener

What's wrong with this code? html <!DOCTYPE html> <html> <head> <title>Ending Project</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> &l ...

Implementing a feature in ReactJS that enables users to select a minimum and maximum limit for checkboxes

I have developed a unique React application that incorporates JSON values into checkbox elements. The JSON data includes both minimum and maximum required values. I successfully implemented a function to set the checkboxes' maximum value based on the ...

Utilizing JavaScript Objects within DWR Method Invocation

Having trouble passing a JavaScript Object to the server side using a DWR method call and encountering a JS error. Here is the JavaScript code: var referenceFieldValues = new Object(); var refFieldArray = referenceFields.split(","); for(var i=0;i<refF ...

It seems that the `to` required prop was missing in the `Link` component of React-Router

Currently, I am facing an issue while trying to integrate react-router. The error message I'm encountering is: Failed propType: Required prop to was not specified in Link. Check the render method of app. Unfortunately, I am unable to pinpoint ex ...

Update the image links to automatically refresh every half a second based on the values inputted in the text bars

Is there a better and simpler way to define AAAAAAAAAA, BBBBBBBBBB, and CCCCCCCCCC using the links provided in the text bars named chart1, chart2, and chart3? This learning project is being done through Notepad++ for personal use only, with no need to sa ...

Unable to define the color of icons path using CSS in Vue 3

When using the iconify library for VueJS, the icons' paths automatically have "currentColor" as their fill color. The issue arises when trying to set a path's color via CSS, as it seems to be ineffective. Even with "!important", the color won&apo ...