Is it possible to trigger a click event exclusively on a parent element?

One feature I'm trying to implement in my app is the ability to click on an item, such as changing the background color or text, and have a modal pop up with a color picker to customize the color of that specific item. However, I've run into an issue where my onClick handler for the parent element triggers even when other elements within it are clicked.

If you take a look at the example I created using Chakra-ui in Codesandbox, you'll notice that clicking anywhere activates the color picker, whereas I want it to only appear when clicking the background.

This is part of the code snippet I'm working with:

const Navbar = () => {
  // Code here...
};

At this point, I'm wondering if there's a way to restrict the color picker to show only when the background is clicked. If you'd like to see the live version of the app, it's also hosted on Netlify. Additionally, feel free to check out the complete codebase on GitHub.

Answer №1

When an event is triggered, the event object contains a property called target, which represents the specific element that was interacted with by the user to initiate the event. By checking if the target element is the parent element, you can determine whether the interaction occurred directly with the parent or one of its child elements.

An example implementation:

if (e.target.classList.contains('navbar') && !showColorPicker) {
  setShowColorPicker((showColorPicker) => !showColorPicker);
}

A more reliable approach involves storing the parent element in a React ref and comparing the event target with this reference. This scenario justifies the use of a ref.

Here's a complete demonstration utilizing a ref. (Note: The code snippet may not execute on StackOverflow due to library loading issues.)

import "./styles.css";
import React, { useState, useRef } from "react";
import { ChromePicker } from "react-color";

export default function App() {
  const [display, setDisplay] = useState("none");
  const [color, setColor] = useState("#1A202C");
  const [showColorPicker, setShowColorPicker] = useState(false);
  const navBarRef = useRef();

  const handleModalClick = e => {
    if (e.target === navBarRef.current && !showColorPicker) {
      setShowColorPicker((showColorPicker) => !showColorPicker);
    }
  };

  return (
    <>
      <div
        className="navbar"
        ref={navBarRef}
        style={{ backgroundColor: `${color}`, color: "white" }}
        onClick={handleModalClick}
      >
        <button style={{ padding: "10px 15px 10px 15px", margin: "20px" }}>
          Left
        </button>
        <button style={{ padding: "10px 15px 10px 15px", margin: "20px" }}>
          Right
        </button>
      </div>
      {showColorPicker && (
        <ChromePicker
          color={color}
          onChange={(updatedColor) => setColor(updatedColor.hex)}
        />
      )}
    </>
  );
}

Answer №2

Event Bubbling is what's happening here, and it is actually the desired behavior (you can learn more about it here). Over time, you'll come to realize how beneficial it can be.

If you only want to handle events originating from the same element where the event handler is attached, you can use a method like this:

const parent = document.getElementById('parent');

const handler = (e) => {
  if (e.target !== e.currentTarget) {
      return;
  }
  
  console.log('PARENT CLICKED!');
};

parent.addEventListener('click', handler);
#parent {
  background-color: #123ff0;
}

.box {
  display: inline-block;
  height: 50px;
  width: 50px;
  background-color: #000000;
}

p {
  color: #ffffff;
  background-color: #000000;
}
<div id='parent'>
  <span class='box'></span>
  <span class='box'></span>
  <p>This is a paragraph.</p>
  <span class='box'></span>
  <span class='box'></span>
</div>

Answer №3

The event object provides default preventions that can be utilized.

For example:

const handleClick = (event) => {
  event.stopPropagation();
}

This should be included on clickable elements within the parent that should not trigger the event, preventing it from propagating to the parent element.

I have created a fork of your codesandbox and implemented my solution: https://codesandbox.io/s/infallible-hellman-66sln?file=/src/App.js

In my opinion, both solutions mentioned above are correct, and the approach will vary based on the context.

For more information, refer to: https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

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

Bug Found in AngularJS v1.3.15: Text Color Rendering Glitch on Page Load with WebKit

It appears that the text colors (which should be blue) are not displaying correctly until a user hovers over the text or resizes the window. I attempted to resolve this issue by adjusting the transition property so that it triggers on hover/active states ...

Using JavaScript to show content in a textarea field

In my index.jsp file, I have implemented the following code to populate two textareas, INPUT_TEXT and INPUT_TEXT2, with processed data. This involves passing the text through a servlet, a Java class, and finally displaying the preprocessed results in the s ...

What is the best way to display the absent months in a mongo aggregate query with a value of 0?

Is it possible to ensure that the missing months in the aggregate result have a BruteAmount value of zero? Purpose This code aims to generate two arrays containing the month names and their respective BruteAmounts. The data should be ordered by the curre ...

Display HTML tags on an HTML page using TypeScript

In my angular application, I encountered an issue where I needed to call one component inside another component. Initially, I was able to achieve this by simply using the second component's selector in the HTML of the first component: html: <div&g ...

Expanding VS 2013: Effective management of classes through item customization

Trying to accomplish a somewhat complex task, I have dedicated hours to searching for the appropriate documentation with no success. The goal is for Visual Studio to generate some JavaScript code and place it in a specific folder, starting from: Performi ...

Unable to eliminate the overlapping box-shadow

Specifically, I am utilizing polymer paper-shadow for my project. I am attempting to eliminate two sides of a paper-shadow box in order to create a basic arrow box, but I am facing difficulties in achieving this. Removing the position: absolute did not re ...

Stopping the sound when its' Div is hovered over

I've managed to successfully trigger the audio to play on hover, but I'm having trouble getting it to pause when my mouse leaves the area. Check out the code in action here: https://jsfiddle.net/jzhang172/nLm9bxnw/1/ $(document).ready(functio ...

How can I incorporate varying textures into an object using `TextureLoader.load`?

I'm interested in adding various textures to each side of a box, but I'm uncertain if using loader.load is the correct approach. Currently, my code looks like this: loader.load('img/brick.jpg', function ( texture ){ var boxGeometry ...

Unable to update due to outdated response call causing issues

I am currently in the process of updating outdated response calls and have encountered a peculiar issue where the response is not being properly ended. Typically, I would use : res.send(200, {message: 'Location Updated'}); However, this method ...

Adding a CSS class to an HTML element using JQuery

I have a collection of tabs, and I want to dynamically add an <hr> element with the class "break-sec" when a certain class is active. This element should be placed within a <span> element with the class "vc_tta-title-text" after the text. Here ...

Retrieving JQuery Results Based on List Items

Once I have obtained the list id from the navigation, my next step is to send the id to a PHP file that will return a JSON list. Below is the jQuery syntax I am using: $("#ul_navigation li").click(function(e) { idsec = this.id; alert(idse ...

Tips for preventing divs from sliding to the next row when adding margin in Bootstrap

{% for plant in plants %} {% if forloop.counter0|divisibleby:3 %} <div class="row row-cols-3"> {% endif %} {% include 'included/_plant_cell.html' %} {% if forloop.counter|divisibleby:3 %} </div&g ...

Finding out the nature of nullable attributes within an object: How can it be done?

I'm facing an issue with saving incomplete forms where I have a form being filled out by a user and I wish to allow the form to be saved even if it's not fully complete. Before sending the object to my API, I need to set any null attributes to e ...

Arrange the array first by a specific data type and then by the intended sorting sequence

I am trying to manipulate an array and move specific elements to the beginning while sorting the remaining elements in alphanumeric order. Here is an example of my initial array: const array1 = ['x123', 'y123', 'z123', 'a ...

The title attribute in Vue3 is updated through props, but computed properties remain unaffected

I incorporated an external library into my project using Vue3. The component I am utilizing is sourced from a third-party library [Edit: Upon realizing that the GitHub repository for this library is no longer being maintained, I have updated the code to re ...

What is the process for utilizing the Bootstrap required field for a type of "button" rather than "submit"?

Check out my code below: <form> <div class="input-group"> <span class="input-group-addon"><i class="fa fa-envelope fa-10x"></i></span> <input type="email" class="for ...

What is the process for modifying a Gist on GitHub?

Trying to update my Gist from another website using my gist token has been unsuccessful. Retrieving a gist with GET was successful, but updating it with PATCH is not working. I don't believe authentication is the issue since retrieving the gist displ ...

Choose Status Menu DiscordJS version 14

Is there a way to get help with changing the bot's status if it's not working properly? The value of the variable "statuses" is set as status, but the status itself does not change. Using client.user.setStatus('dnd'); can sometimes work ...

WebVR - lack of orientation and position data from PositionSensorVRDevice

I'm attempting to enable Oculus Rift DK2 orientation input for WebVR, using either Three.js' VRControls (examples/js/controls/VRControls.js) or directly from the PositionSensorVRDevice. However, the values of orientation and position in the Posi ...

A guide on implementing multiline properties in a property file for Selenium WebDriver

At the moment, I am focusing on utilizing Selenium WebDriver with code that is developed in Java. In the snippet below, I have successfully verified if the dropdown values match the UI for one dropdown. Now, I aim to extend this capability to check multip ...