Exploring z-indices in event bubbling

JSFiddle: https://jsfiddle.net/uLap7yeq/19/

Issue

Let's examine a scenario where there are two elements, canvas and div, positioned in the same location using CSS. The div has a higher z-index compared to the canvas, but how can we make sure events triggered on the div get passed down to the lower z-indexed element? Is it necessary to use .dispatchEvent() on the canvas?

UPDATE: Just to clarify, I want the div to handle the event first, perform its actions, and then pass the event along to the next element with a lower z-index.

The JSFiddle code provided below:

/*
     How can I pass the event along to #canvas?
    */
$('#container').on('click', function(e) {
  console.log('#container click');
});
$('#canvas').on('click', function(e) {
  console.log('#canvas click');
});
$('#other-div').on('click', function(e) {
  console.log('#other-div click');
});
#other-div {
  z-index: 1;
  position: absolute;
  top: 0;
  left: 0;
}

#canvas {
  z-index: 0;
  position: absolute;
  top: 0;
  left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
  <div id="other-div">
    <p>
      Something
    </p>
  </div>
  <canvas id="canvas" width="200" height="200"></canvas>
</div>

Answer №1

By adding the CSS property pointer-events: none; to the #other-div, you can allow clicks and other pointer-related events to pass through the div, reaching both the canvas and the container.

#other-div {
  z-index: 1;
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
}

For a demonstration, check out Fiddle 1.


If the above solution is not suitable because you require the other-div to also capture the event, as per your comment, then you can programmatically trigger an event on the canvas when the container is clicked.

$('#container').on('click', function(e) {
  console.log('#container click');
  $('#canvas').click(); // <------
});
$('#canvas').on('click', function(e) {
  e.stopImmediatePropagation(); // <------
  console.log('#canvas click');
});
$('#other-div').on('click', function(e) {
  console.log('#other-div click');
});

When the container receives a click, it will automatically trigger a click on the underlying canvas: $('#canvas').click();

It's important to note that when the click reaches the canvas, the event must be stopped from propagating to prevent an infinite loop that would hit both the #other-div and the #container. This is why we have e.stopImmediatePropagation();

For a visual representation, see Fiddle 2.

Answer №2

If you want to create a custom event triggered by clicking on the outer-div, you can have the canvas listen for this event:

$('#container').on('click', function(e) {
  console.log('#container click');
});
$('#canvas').on('click custom', function(e) {
  console.log('#canvas click');
});
$('#other-div').on('click', function(e) {
  console.log('#other-div click');
  $('#canvas').trigger( "custom");
});
#other-div {
  z-index: 1;
  position: absolute;
  top: 0;
  left: 0;
  background:rgba(255,0,0,0.2);
}

#canvas {
  z-index: 0;
  position: absolute;
  top: 0;
  left: 0;
  background:rgba(255,255,0,0.2);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
  <div id="other-div">
  <p>
   Something
  </p>
  </div>
  <canvas id="canvas" width="200" height="200"></canvas>
</div>

Answer №3

One way to implement a dynamic solution is by modifying the Element prototype:

if (!document.elementsFromPoint) {
    document.elementsFromPoint = elementsFromPoint;
}

function elementsFromPoint(x, y) {
    var parents = [];
    var parent = void 0;
    do {
        if (parent !== document.elementFromPoint(x, y)) {
            parent = document.elementFromPoint(x, y);
            parents.push(parent);
            parent.style.pointerEvents = 'none';
        } else {
            parent = false;
        }
    } while (parent);
    parents.forEach(function (parent) {
        parent.style.pointerEvents = 'initial';
    });
    return parents;
}

Element.prototype.makeEventGoThrough = function(eventName) {
  $(this).on(eventName, (e) => {
    var elements = document.elementsFromPoint(e.clientX, e.clientY);
    var children = [].slice.call(this.children);
    elements = elements.filter(element => element !== this && !children.find(el => el === element));
    elements.forEach(element => $(element).trigger(eventName));
  });
}


/*
 How can I pass the event along to #canvas?
*/
document.getElementById('other-div').makeEventGoThrough('click');
$('#other-div').on('click', () => console.log('other-div clicked'));
$('#canvas').on('click', () => console.log('canvas clicked'));
#other-div {
  z-index: 1;
  position: absolute;
  top: 0;
  left: 0;
}

#canvas {
  z-index: 0;
  position: absolute;
  top: 0;
  left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
  <div id="other-div">
  <p>
   Something
  </p>
  </div>
  <canvas id="canvas" width="200" height="200"></canvas>
</div>

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

loading a codeigniter page via AJAX calls

Currently, I am delving into the realm of AJAX using jQuery. To kick things off, I decided to utilize a code snippet from w3school, which performed admirably. Afterwards, I proceeded to incorporate the code into a view page within the Codeigniter framewor ...

Error: Preflight request returned a 405 HTTP status code when querying Ionic + CI backend

I am currently working on my first app using ionic with a codeigniter backend. However, I am encountering the "Response for preflight has invalid HTTP status code 405" issue in ionic + CI backend. Can anyone help me solve this problem? This is my controll ...

Only allow scrolling if the number of child elements exceeds a certain limit

I am looking to implement a scroll feature on the <ul> element when the number of <li>s exceeds a certain threshold. For example, if we have 12 children, I want to display only 7 of them and then scroll through the rest. This is my current app ...

Angular 4: Unhandled error occurred: TypeError - X does not exist as a constructor

I am currently developing a project in Angular 4, and I encountered an error while running the application. The specific error message is as follows - ERROR Error: Uncaught (in promise): TypeError: index_1.EmployeeBase is not a constructor TypeError: in ...

"Utilizing multiple callbacks with the jQuery .post method for AJAX requests

Can I receive multiple callbacks with jQuery when using .post for an ajax image upload to PHP? I want the front end to display status updates after the image has been uploaded and while it is being processed. Currently, I am using standard .post method, ...

"Proceeding without waiting for resolution from a Promise's `.then`

I am currently integrating Google Identity Services for login on my website. I am facing an issue where the .then function is being executed before the Promise returned by the async function is resolved. I have temporarily used setTimeout to manage this, b ...

How can I make my navbar stay fixed in place and also activate the transform scale functionality?

After fixing the position of my navbar with the class "top," I noticed that the transform property scale does not work on the div element I applied. The hover effect on the box only works when I remove the position from the navbar. This is the HTML code: ...

What is the method for creating a new array of objects in Typescript with no initial elements?

After retrieving a collection of data documents, I am iterating through them to form an object named 'Item'; each Item comprises keys for 'amount' and 'id'. My goal is to add each created Item object to an array called ' ...

Auto Suggest: How can I display all the attributes from a JSON object in the options list using material-ui (@mui) and emphasize the corresponding text in the selected option?

Currently, I am facing a requirement where I need to display both the name and email address in the options list. However, at the moment, I am only able to render one parameter. How can I modify my code to render all the options with both name and email? ...

Is there a convenient HTML parser that is compatible with Nativescript?

I have tested various libraries like Jquery, Parse5, and JsDom, but unfortunately they are not compatible with nativescript. Jquery relies on the DOM, while Parse5 and JsDom require Node.js which is currently not supported by nativescript. I am in need of ...

Firebase Error: Trying to access properties of null object (indexOf)

Whenever I try to console.log(docSnap), a Firebase error shows up as seen in the image below. Despite attempting various solutions, none have proved effective. useEffect(() => { if (folderId === null) { dispatch({ ...

The error message received states: "materialize-css Uncaught TypeError: Vel is not defined as

My current setup involves webpack as the bundler/loader, and I've successfully loaded materialize css (js/css). However, when attempting to use the toast feature, an error is thrown: Uncaught TypeError: Vel is not a function The library is being inc ...

Having trouble clicking or interacting with JavaScript using Selenium and Python

Issue: Unable to interact with elements on a specific webpage after opening a new tab. Using WebDriver Chrome. I can successfully navigate the initial webpage until I click a link that opens a new tab, at which point I am unable to perform any actions. ...

The values of variables persist even after refreshing the page

let quote; let author; // Establishing the Get Method for the Root Route in ExpressJS app.get('/', (req, res)=>{ res.render('home', { quote: quote, writer: author }); }); // Configuring the Post Method for t ...

Next.js 13 app directory experiences 404 Not Found error due to dynamic routing issues

I recently built a straightforward to-do app using Next.js 13 paired with TypeScript. The process involved creating an array of objects, each comprising an id string and a name string. Subsequently, I iterated through the list and showcased the names withi ...

Ways to verify if TypeScript declaration files successfully compile with local JavaScript library

I have recently updated the typescript definitions in HunterLarco/twitter-v2, which now follows this structure: package.json src/ twitter.js twitter.d.ts Credentials.js Credentials.d.ts My goal is to verify that the .js files correspond correctly ...

Troubleshooting Node.js TypeScript breakpoints in Visual Studio Code

I've attempted multiple solutions, but none seem to be working for me. Although the code is running, I'm having trouble setting breakpoints and debugging it. Can you offer any assistance? Below is the configuration script I've tried in VSCo ...

Using jQuery Datatables with Asp.Net

Having encountered an issue with displaying two jQuery datatables in separate menu tabs, I am seeking assistance to rectify the problem. The first datatable (id=gvSchedule) appends successfully while the second one (id=gvMySchedule) loses its formatting up ...

Utilizing ReactJS to creatively overlay a video on top of an image with the option to

Is there a method to superimpose a video onto an image while having the ability to select the blending mode for them? Similar to how Photoshop and Final Cut provide various blending modes such as Overlay, Multiply, Add, etc. Could there possibly be a libr ...

Can the parcel bundler dist folder be customized for decoration?

After compiling code with parcel using the command parcel src/index.html, all files are generated inside the dist folder. However, I am looking for a more organized way to manage these files once my website is complete. Ideally, I want separate folders suc ...