3

This is a Q/A on how to handle the opacity of overlapping elements and make it consistent while hover, with a JS solution.

Requirement

The requirement is to develop two elements, which are transparent and overlapping, like the two red boxes below. These need to be transparent so that the background contents are visible.

enter image description here

Now, while hover on any of these elements, the particular element should turn opaque like below.

enter image description here

Gibin Ealias
  • 2,751
  • 5
  • 21
  • 39

4 Answers4

11

There's a CSS only solution, which makes it a bit more efficient. Like this for example:

body {
  background-image: linear-gradient(45deg, transparent 50%, #aaa 75%);
  background-size: 20px 20px;
}

#a {
  position: absolute;
  width: 150px;
  height: 150px;
  top: 50px;
  left: 50px;
  background: rgba(255, 0, 0, 1);
}

#b {
  position: absolute;
  width: 150px;
  height: 150px;
  top: 125px;
  left: 125px;
  background: rgba(255, 0, 0, 1);
}

#wrapper {
  opacity: 0.5;
}

/* instead of changing the classes,  
you can use selectors like this:*/

#wrapper:hover #a:hover,
#wrapper:hover #b:hover {
  opacity: 1;
  z-index: 10;
}

#wrapper:hover {
  opacity: 1;
}

#wrapper:hover #b,
#wrapper:hover #a {
  opacity: 0.5;
  z-index: -1;
}
<div id=wrapper>
  <div id="a">
  </div>
  <div id="b">
  </div>
</div>
Julien Grégoire
  • 16,864
  • 4
  • 32
  • 57
2

I tried with elements at absolute positions. In this case I'm able to move each of them on top the wrapper on mouse enter, then back to their position on mouse leave, via pure JavaScript.

I have expanded the example to 3 elements, where your code shows the artifacts, and added borders to actually see the overlapping.

CODEPEN

The HTML:

<div id=top>
  <div id=wrapper>
    <div class="first" onMouseEnter="hover3b(event)" onMouseLeave="hover3e(event)"></div>
    <div class="second" onMouseEnter="hover3b(event)" onMouseLeave="hover3e(event)"></div>
    <div class="third" onMouseEnter="hover3b(event)" onMouseLeave="hover3e(event)"></div>
  </div>
</div>

The CSS:

body {
  background-image: linear-gradient(45deg, transparent 50%, #aaa 75%);
  background-size: 20px 20px;
}

.first, .second, .third {
  width: 100px;
  height: 100px;
  background-color: red;
  position: absolute;
  border: 3px solid black;
}

#wrapper {
  width: 200px;
  height: 200px;
  background-color: yellow;
  border: 3px solid green;
  opacity: 0.6;
}

.first { left: 0px; top: 0px; }
.second { left: 80px; top: 80px; }
.third { left: 160px; top: 160px; }

The JavaScript:

var from = null; // remember where to put back the element
function hover3b(e) {
  var t = e.target;
  from = t.nextElementSibling;
  if (!from)
    from = null;
  document.getElementById("top").appendChild(t);
}
function hover3e(e) {
  document.getElementById("wrapper").insertBefore(e.target, from);
}
edixon
  • 991
  • 6
  • 16
1

Solution

As described in the requirement, the solution starts with creating two elements and probably a wrapper to those.

<div class="wrapper">
  <div class="first"></div>
  <div class="second"></div>
</div>

Now, we style them to match the design.

.first,
.second {
  width: 100px;
  height: 100px;
  background-color: red;
  opacity: 0.6;
}

And the below lines of code for the overlap.

.second {
  margin-left: 50px;
  margin-top: -50px;
}

The problem with this approach is that the opacity will not be consistent on the overlapped region and it gets a darker shade.

enter image description here

Note that this is not a bug with the browsers and the reason for this behaviour is explained here.


The right approach

The better way to handle this situation is to avoid making the children elements transparent and instead set 'opacity' in the parent level. And while hover, toggle these opacity levels between the parent and children with JS.

$(".first, .second").hover(function() {
  $(".wrapper, .first, .second").not(this).toggleClass("add-opacity");
});

Also, the hover on overlapped region would cause flickering due to change in stack order, which can be handled by setting z-index to the hovered element.

.first:hover,
.second:hover {
  z-index: 1;
}

CODEPEN

Hope this helps.

Gibin Ealias
  • 2,751
  • 5
  • 21
  • 39
-1

Here's another approach using Pointer_events

whenever you hover on an element you disable the pointer-events on the other :

$('.first').hover(
  () => { $('.second').css({'pointer-events': 'none'})},
  () => { $('.second').css({'pointer-events': 'auto'})
})

$('.second').hover(
  () => {$('.first').css({'pointer-events': 'none'})},
  () => {$('.first').css({'pointer-events': 'auto'})
})
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  border: 0;
}

body {
  background-image: linear-gradient(45deg, transparent 50%, #aaa 75%);
  background-size: 20px 20px;
}

.wrapper {
  margin-top: 10px;
  margin-left: 10px;
}

.first {
  width: 100px;
  height: 100px;
  background: rgba(255, 0, 0, 0.6);
}

.second {
  width: 99px;
  height: 98px;
  margin-top: -49px;
  margin-left: 50px;
  background: -webkit-linear-gradient(transparent 50%, rgb(255, 0, 0) 50%), -webkit-linear-gradient(0deg, transparent 50%, rgb(255, 0, 0) 50%);
  opacity: 0.6;
}

.first:hover,
.second:hover {
  background: rgba(255, 0, 0, 1);
  opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
  <div class="first" id="first"></div>
  <div class="second" id="second"></div>
</div>

check the compatibility for this as it will work in most browsers except Safari ( as of now ).

Taki
  • 17,320
  • 4
  • 26
  • 47
  • This still don't solve the inconsistent opacity on the overlapped region. Please check my answer on how it is handled. – Gibin Ealias Apr 24 '18 at 06:57