A simple stacking context problem HOWTO

noneinnon

Anton Baranov

Posted on October 22, 2021

A simple stacking context problem HOWTO

So recently i was asked to solve this simple problem.

The problem goes as follows: given this markup, how can we make a click to div on a button area to trigger button's on click event, with out changing the markup (i.e. changing tags order is not allowed)?

<div id="app">
  <button>button</button>
</div>
<div id="overlay"><div>
Enter fullscreen mode Exit fullscreen mode
div#overlay {
  position: absolute;
  background: rgba(255, 85, 23, 0.5);
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

button {
  position: relative;
  margin: 10px;
  width: 100px;
  height: 30px;
  border: none;
}
Enter fullscreen mode Exit fullscreen mode

You might notice that div tag is positioned below button tag in DOM tree and has position: absolute property which means that it will always be displayed on top of a button.
So how can we solve this? There are at least 2 solutions that i know of.

Javascript solution

To solve this with just javascript, we would need to be familiar with couple of basic DOM api's, such as element.getBoundingClientReact() and mouse event.

First we would need to attach an event handler to root element's click event, next we will need to figure out if user is clicking on area, where our button sits under overlay and finally, if user does click in proper area, call button's on click event.

The key thing to note here, is that top left corner of viewport will always have coordinates of x = 0, y = 0, that's why we add width & height to relative axis, when validating click area.

const button = document.querySelector("button");

button.onclick = () => {
  console.log("im clicked");
};

const { x, y, width, height } = button.getBoundingClientRect();

document.onclick = ({ clientX, clientY }) => {
  const validY = clientY <= y + height && clientY >= y;
  const validX = clientX <= x + width && clientX >= x;
  return validX && validY && button.click();
};
Enter fullscreen mode Exit fullscreen mode

CSS solution

If you are familiar with stacking context, you probably guessed already, that this problem can be solved using it. Here's MDN page on stacking context.

Here's what we gonna add to our css:

button:before {
  content: "";
  position: absolute;
  display: block;
  z-index: 1;
  width: 100px;
  height: 30px;
  left: 0;
  top: 0;
}
Enter fullscreen mode Exit fullscreen mode

Since we'r using z-index & position property on :before element, it will create a new stacking context button's before element above the overlay.

One thing to note here: we could use position:relative on :before element, that would also work, but in that case, the :before node would not be positioned "above" button, but rather inside, moving all of its children nodes and we don't want that at all.

EDIT: as mentioned in comment section, we could have change z-index on a button itself, that would have created a new stacking context and would also work.

here's a link to a sandbox with both solutions.

💖 💪 🙅 🚩
noneinnon
Anton Baranov

Posted on October 22, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related