Why does opacity property carry over to an "irrelevant" element? [duplicate] - html

I'm coding a "popup window" in JavaScript and I've come across an interesting thing:
The navy square under the popup window is visible even though I would expect it to be hidden. The popup was added after the square, so it should be on the top.
CSS opacity property of the navy square is 0.3. From what I've tried, it seems that every number from the interval (0,1) would yield the same result. If I change it to 1, then it behaves as expected (i.e. the part of the square under the popup is hidden).
I've tried to set the z-index property to 10 for the square and 100 for the popup, but it doesn't change anything.
What am I missing? Why is part of square displayed?
Tested browsers:
Firefox 3.6.x
Chrome 4

This is not a bug and is actually how it's supposed to work. It's a bit confusing as the elaborate description of Stacking Contexts doesn't mention anything about it. However, the visual formatting module links to the color module where this particular gotcha can be found (emphasis mine):
Since an element with opacity less than 1 is composited from a single
offscreen image, content outside of it cannot be layered in z-order
between pieces of content inside of it. For the same reason,
implementations must create a new stacking context for any element
with opacity less than 1. If an element with opacity less than 1 is
not positioned, implementations must paint the layer it creates,
within its parent stacking context, at the same stacking order that
would be used if it were a positioned element with ‘z-index: 0’ and
‘opacity: 1’. If an element with opacity less than 1 is positioned,
the ‘z-index’ property applies as described in [CSS21], except that
‘auto’ is treated as ‘0’ since a new stacking context is always
created. See section 9.9 and Appendix E of [CSS21] for more
information on stacking contexts. The rules in this paragraph do not
apply to SVG elements, since SVG has its own rendering model ([SVG11],
Chapter 3).

It's not a problem of opacity being more important than z-index, rather than z-index being relative to their stacking context (see z-index in the CSS2 specification).
In other words, z-index are only significant within the context of a positioned ancestor (whether its relative, absolute or fixed). What you need to do to fix your problem is add a position: relative; to the element that contain both your popup and your navy square, and probably add it a z-index: 1; . Seeing your screenshot it will probably be a top element such as a wrapper div.

Workaround for two elements, like divs: add a 0.99 opacity to your top element, and the order of both is reestablished.
opacity: 0.99;

An alternative to using opacity, is to use a transparent colour (with an alpha value)
So, rather than using
{
background: gray;
opacity: 0.5;
}
You could try
{
background: rgba(128,128,128,0.5);
}
It isn't identical, but I was encountering the same issue you were having, and the above fixed it.

Example code might be needed to debug this problem.
You might put overflow: hidden and possibly position: relative in a DIV which surrounds all the editor objects to try to force the elements to only be drawn within that DIV, e.g:
<div style="overflow: hidden; position: relative">
(Editor object buttons go here)
</div>
As a last resort, you could also try a iframe in between the two elements to try to stop them seeping through.

You might try to set the popup window's DIV like this using !important so the style doesn't change on applying new style or class:
background-color: white !important;
z-index: 100 !important;
opacity: 1.0 !important;
Then, make new CSS class:
.PopupElement
{
z-index: inherited;
opacity: inherited;
}
And add class to all elements in the window, like this for example:
<input value="posx" class="some_class PopupElement"/>
My guess is that this would work, since there is no priority in applying CSS attributes... as far as I know. =)

I had the same issue. Using rgba instead of color/opacity solved my problem. Working with LESS (in the Bootstrap framework), the fade() function did the conversion for me.

Although #Guillaume Esquevin already gave a great answer, I will try to expand on it in case someone ignores what a stacking context is (like I did).
As you can read here, there is something called stacking context, which refers to a group of elements sharing a parent that move together in the stack. An example could be a div and all its children.
There are three ways to create a stacking context: in the root of the document (the html element), by positioning the parent element, and by changing the opacity of the parent to something lower than 1.
Then, if you have a div with opacity lower than 1 and you want some sibling element of this div to appear behind it (and its children), you can create a new stacking context on such sibling by setting its position to relative or by changing its opacity as well.

Related

is it possible to create a new stacking context WITHOUT the element being painted as if it's positioned?

what is the question?
Under the current spec of CSS, for any element, creating a new stacking context is equivalent to being painted as if it's positioned.
Various properties creating a new context like opacity, isolation and so on inevitably lead the browser to handle the element with the property as positioned.
For example, if you just add to an element only opacity less than 1 and no position, the browser will paint the element as positioned. This means an element with opacity less than 1 and no position is always over any non-positioned elements.
Any property creating a new stacking context like isolation are the same.
You can check this info from this discussion.
https://github.com/w3c/csswg-drafts/issues/2717
So does anyone know any "hack" for creating a new stacking context WITHOUT the element being painted as if it's positioned?
why am i asking it?
background
I'm now developing a personal browser extension which adds popups, rect and so on to the web page the user is opening, through DOM scripting.
I don't wanna change anything about the original DOM including style, because I want the basic view of the web page to be kept as the original.
All I can do is just add/insert some new DOMs without destroying the web page.
the main problem
In the development mentioned above, i wanna insert some elements with position: relative between two div elements: one is without any setting of position and the other is without any setting of position but with float setting.
Please run the below snippet. I wanna insert some positioned elements between them.
<!DOCTYPE html>
<html lang="en">
<body>
<div class="cover">float: left and no-position</div>
<div class="target">no-float and no-position</div>
</body>
</html>
<style>
.target {
color: white;
background-color: red;
height: 200px;
width: 200px;
}
.cover {
color: white;
background-color: blue;
height: 100px;
width: 180px;
float: left;
}
</style>
Of course, just inserting some elements with position: relative fails, because positioned elements are painted always over non-positioned elements.
Here, if i can create a new stacking context between them, the goal will be achieved.
However, again, under the current spec of CSS, for any element, creating a new stacking context is equivalent to being painted as if it's positioned. That means newly created stacking contexts are always over the floated elements...
So anyone has a solution?? Thank you for taking your precious time!

Bringing a HTML element to front with z-index on overflow in CSS

I am trying to get the blue rectangle container to have a larger z-index than the other boxes when hovering over elements that overflow the container.
Game 3 here has the larger z-index, but I want to access the Loser select in the blue circle below, however I can't, unless I hover back over the blue rectangle to gain focus.
Is there a way around this where it can be handled with just CSS or do I need JQuery?
I created a Fiddle that can replicate this so ignore any JS errors as the actual page requires quite a bit of includes, however the issue is in tact. Hover over the 4th game which has three dropdowns, Source, Pools, Seeds. You can select Seeds just fine. However, hover over another game at the top then come back down to "Seeds", you can't select it unless you hover over "Pools" again. I need "Seeds" to always be selectable regardless of what the overflow is.
https://jsfiddle.net/cblaze22/qp4L15tj/8/
Current Code For Game Hover (Blue Rectangle area)
The .forward puts a large zindex on the blue rectangle area.
$(element).hover(
function () {
if (!$(this).parent().hasClass('editing')) {
$(this).addClass('forward');
}
},
function() {
$(this).removeClass('forward');
}
);
This was actually a surprisingly easy fix once you understood the structure and the actual problem. Div's covering div's. First you disable all click events on everything within .bracket-part as they aren't needed. Then you add the click events back onto the selects. To make it more generic for easier use again you can simple change select in the CSS selector a class .re-enable-events or something. The JS about z-index's wasnt actually needed.
#bracket-wrapper .bracket-part select {
pointer-events: all !important;
}
#bracket-wrapper .bracket-part {
pointer-events: none;
}
See: https://jsfiddle.net/uws8pf1y/
Pointer events has a very good compatibility rate so this solution should be fine across pretty much all devices.
To get straight to the point: There is no clean CSS-only solution to your problem.
Since all your elements are pretty much identical (and by that I mean the class for example) you will not find a solution that covers all configurations. Since they do not differ from each other they all have the same z-index but not the same stacking context. Unless you give their parents a different z-index or change the stacking context you will not be able to access the blocked element. It also comes down to how limited you are with changing the code. The code looks like it has been build by JS and you just copied it to your fiddle for us to test.
Attempt #1
Attempt #1 is to just add high z-index directly to the according parent.
#mmshr already tried to do this. However, he tried to give the whole class a high z-index which is not gonna work out of course as you've already pointed out.
You could however try to only give this element a high z-index element in its style attribute. This comes down to how limited you are with changing the code. You could theoretically use JQuery for this but the way you would select the element (e.g. by nth-child()) brings me to Attempt #2 which uses the same pseudo-class and is a CSS-only attempt so using JS is nonsense in this case. By the way if you can change your code like this you could remove your little JQuery function that adds the forward class on hover.
Attempt #2
This attempt works fine and you are not limited by the ability to change code since this is pretty much one line of CSS. As already stated in Attempt #1 you could use a pseudo-class to select this element. However, this is not valid for all configurations. If you would add one element (<div data-bind="template: { name: 'bracket-template', data: $data }">...</div>) before your blocked element you would have to change your CSS each time. But if there is no need for changing elements and configurations (or at least not the order) this is a valid solution:
#bracket-wrapper > div > div:nth-child(8) > div > div {
z-index: 2 !important;
}
In this attempt you can (and have to) remove your little JQuery function too:
$('.bracket-part').hover(
function() {
debugger;
$(this).addClass('forward');
},
function() {
$(this).removeClass('forward');
}
);
Remove the entire thing.
Attempt #3
Probably the cleanest and best attempt is to use the stacking context. The stacking context is explained right here. But to give you a simple overview (W3C):
Each box belongs to one stacking context. Each positioned box in a given stacking context has an integer stack level, which is its position on the z-axis relative other stack levels within the same stacking context. Boxes with greater stack levels are always formatted in front of boxes with lower stack levels. Boxes may have negative stack levels. Boxes with the same stack level in a stacking context are stacked back-to-front according to document tree order.
Most important is this part because it applies to your structure:
Boxes with the same stack level in a stacking context are stacked back-to-front according to document tree order.
If you take a look at your HTML tree you will find the following:
According to the stacking-context we should be able to give your element in the background a higher stacking-order then your element in the front by changing the order of those elements in your tree.
It is just a guess but you probably have something like an array where you store the data and some JS-file builds a tournament bracket out of it. If you could somehow change the order of those two elements (for example by changing the order of your array) you would not use CSS and would not use any additional JQuery.
What if none of these work?
Well, then I do not see any solution that requires only CSS.
I also thought about a possible JS solution but this is a tough one and I couldn't figure out a (simple) solution. Let me explain the problem:
Since your select is behind a div element JQuery would not recognize it (e.g.) on hover so you would have to use pseudo-classes again which I already covered with a CSS-only attempt.
I also thought about adding a z-index of -1 to the blocking element, because JQuery could recognize it on hover. But this leads to problems too: the blocking element is now in the background and the blocked element in the front and you can also click it. The problem is that the (former) blocking element is now behind the #bracket-wrapper. This is also not a valid solution because you would have to use a pseudo-class again to target this specific element.
Conclusion
I am gonna be honest with you: This tournament tree is poorly designed and structured. There shouldn't be overlapping elements or elements outside of a container and certainly not both in combination. If none of my attempts are suitable I do not see any CSS or JS solution. At least not a simple one. You have provided little information about how the tournament tree is build but things could change if you do.
At this state I think rebuilding this whole structure is the only really clean solution.
Edit
Solution #1 by #Deckerz
#Deckerz provided a great solution which does not focus on the z-index. Instead, it uses pointer-events. I tried this approach but failed because I forgot an important part. The logic behind it is simple:
First you disable all click events on everything within .bracket-part as they arent needed. Then you add the click events back onto the selects. To make it more generic for easier use again you can simple change select in the CSS selector a class .re-enable-events or something. The JS about z-index's wasnt actually needed.
#bracket-wrapper .bracket-part select {
pointer-events: all !important;
}
#bracket-wrapper .bracket-part {
pointer-events: none;
}
However, this is still a workaround. I still recommend restructuring your code and CSS.
If you want Seeds to always be selectable, why not always give it's bracket-part parent a high z-index?
Right now, the z-index is only high after the bracket-part is hovered. Although Seeds is technically a child of the bracket-part, it is positioned outside of it, so unless the Seeds select is hovered directly after bracket-part is hovered then it won't be selectable.
If you add z-index: 10000; to Seeds' bracket-part parent styles, Seeds will always be selectable:
<div class="part bracket-part ui-draggable ui-resizable ui-draggable-disabled ui-state-disabled" data-bind="bracketPartInit: { left: $data.left, top: $data.top, height: $data.height, width: $data.width, disabled: $root.members.bracket.disableDrag, minHeight: $data.minHeight }, css: { 'dash-top' : $data.dashedBorderTop(), 'dash-right' : $data.dashedBorderRight(), 'dash-bottom' : $data.dashedBorderBottom(), 'dash-left' : $data.dashedBorderLeft(), 'reverse-bracket' : $data.type() == 2, 'box-bracket': $data.type() == 13, 'bye': $data.bye()}" aria-disabled="true" style="top: 459px; left: 0px; height: 80px; width: 150px; z-index: 10000;">

Delayed transition only in certain situations

I have 4 overlays inside a container (overflow: hidden) translated horizontally 100% on default.
.active on the .overlay animates it into view.
activating another one removes .active from the current one and adds .active to the new one.
Now I want a transition delay on the "new active" element, because animating both the old and the new overlay at once results in inconsistent visuals (overlays overlaying each other etc.). And both animating simultaneously feels too hasty.
My first approach:
sibling selector to delay the transition for all siblings of the .active, didn't work out, since the sibling selector doesn't look "behind" or "around" ...
Second approach:
class on parent atLeastOneIsActive and then apply transition-delay to .active. Didn't work aswell, because both the new and the old overlay then get a transition-delay, making all even worse.
Unfortunately I can't show you the live example.
The question is more in general anyways; but to get a picture of the result here 2 screenshots
hover on either pin or link
overlay displayed
I'm looking for a clean and sweet way to apply delays in certain situations.
jQuery is only used for class management.
activating another one removes .active from the current one and adds .active to the new one.
Is this "activation" made with jQuery .on("mouseover", function(){?
Because if you add and remove classes this way, why not simply use setTimeout on the .addClass()?
-------------------------
EDIT
I worked on it a while.
And I'm pretty sure to have a solution...
Let's say I found the exact nature of you specific problem, to be more exact.
I reproduced your problem and the solution in a fiddle.
But before you have a look to it, please read my explanations:
The image transitions are overlapping.
And that is because of their width versus their animation start position.
Since they are pushed to outer right of the viewport at a specific distance...
This distance is not enought versus the with of the images. It has to be twice (minimum) the larger image.
I found it by setting them all to a same size.
This is not mandatory... But sure is a good thing!
So, the solution is to push them twice this "max-width" away from the right side of the viewport.
I made a Fiddle and made 4 buttons (representing your map pins) to animate the images. I also assigned keyboard numbers to them, so it's easyer to closely watch the images without having to target the buttons with the mouse. ;)
And finally, there is a button "Toggle class equalSize" which forces the images to all the same size.
Have a look now!
:D
.active {
right:0;
}
img{
position:fixed;
right:-1200px;
top:100px;
transition: right 2s;
}
.equalSize{
width:600px;
height:450px;
}

Document stacking context root element: <body> or <html>?

Here's what I know:
In HTML, the document's root element is <html>
A document's root element creates a stacking context, hence <html> is the root element of the first stacking context created in any HTML document
From this, I'd expect any z-index: -1 element to be positioned behind <body> (though in front of <html>) in a document where no other stacking context comes into play.
The following example, however, demonstrates otherwise in all modern browsers: http://jsfiddle.net/39q2u/
I've dug a little deeper in that second example: http://jsfiddle.net/39q2u/1/
Adding a background-color to <html> somehow makes the rendering engine realize that the z-index: -1 element should indeed be displayed behind <body>, effectively making it invisible since <body> also has a background-color set.
Interestingly, I've given a try to several other CSS properties, an none of them seem to have the same effect.
(The element remains visible in IE 9.)
Removing the background-color from <body> makes the z-index: -1 element appear again, proving it was hidden behind <body>.
The issue I'm having with this is how to make sense of that behavior:
Is <body> subject to additional rules when it comes to stacking contexts?
Why is it needed to set a background-color on <html> for the rendering engine to behave correctly?
Or did I just misunderstand something, somewhere?
The default background-color of html is transparent. Thus, elements with a negative z-index are displayed, because you can see them "through" the html element. The link provided by #thirtydot points toward the right direction, although perhaps this link: http://www.w3.org/TR/css3-background/#background-color might be more on point.
I am not 100% sure but I believe the body tag always follows a z-index which is relative to other elements to make it easier when ordering multiple elements on the page.
If you apply an attribute to html, it suddenly becomes, and behaves like any other element, therefore putting it's child element, tag, into use as an element on the page also. Think of it as if you have toys you can play with, then toys you cannot reach. By assigning an attribute to the html, you are bringing the toys you could not reach into reaching distance. You are allowing the webpage to use the and subsequently the
That made sense in my head but I am stood from a bias stance so I hope that helped!
actually your expectation working as described except for the background property
take a look at this snippet it a prove that p element under body after positioned with negative integer :
html{
border: 20px solid green;
width: 250px
}
body {
padding:0;
margin:0;
width: 200px;
border: 20px solid orange;
overflow: hidden;
background:red;
}
p {
position: relative;
z-index: -1;
background: yellow;
}
<p>helllllllllllllllllllllllllllllllllllllllllllo</p>
so we can now exclude z-index from reasons
and It looks like a weird behavior on background-color property
I think it's better to read about it by following this links :
https://css-tricks.com/just-one-of-those-weird-things-about-css-background-on-body/
Giving background-color to body applying whole page. Why?

Why does id not override class in bootstrap?

I am working on front-end web development and keep running into the same issue. I am using bootstrap styling rules (bootstrap.css) with a few modifications.
HTML
<div class="container">
<div class="jumbotron">
<button type="button" id="jnav">Restricted</button>
</div>
</div>
style.css
#jnav{
opacity: 1;
}
From bootstrap.css
.jumbotron {
opacity: 0.75;
}
Basically, I wanted to use ID to override the opacity, so that the button would have an opacity of 1 while the rest of the jumbotron would have an opacity of 0.75. The problem is that the button's opacity is remaining at 0.75 (so it is annoyingly the same as the jumbotron background)! Does anyone know what's up? Is there something basic that I am missing? I thought that id was a more specific attribute and would override class styles.
Opacity doesn't inherit in the same way as things like color or background. Setting the opacity of an element makes that element and everything it contains render at that opacity relative to whatever is behind it. The opacity property of chile element then compunds like #techfoobar said. You can read more here.
Basically, what you need to do is set the opacity for each child of .jumbotron separately while leaving the opacity of .jumbotron at 1.
It's hard to say given the limited information, but it sounds like you're trying to place a button (#jnav) inside the jumbotron which has an opacity. Since the jumbotron has an opacity of 0.75, everything inside of it will follow that same opacity regardless of any other rules. This is not a problem with class/ID specificity or bootstrap, more just a general styling nuance.
Basically what Jcubed just said above.
Typically the selectors used in Bootstrap.css are very specific. The selector might be something like body > div > .jumbotron which is very specific. In this case simply using the element Id won't override the css. You will need to match the specificity or be more specific. For example body > div > #jnav would effectively override the css as they are both equally specific.
This of course assumes that the css you want to use comes after the css you are replacing. Either after it in the same css file or a seperate css file included after the base Bootstrap.css.
If worst comes to absolutely worst, then you can use:
#jnav{
opacity: 1 !important;
}
But this shouldn't need to happen unless you are absolutely desperate.
They do not override each other. They both applied, but #jnav is within .jumbotron. So .jumbotron's opacity will apply on top of #jnav's opacity.
If you just want the effect, you should use rgba
Example:
#jnav{
background: rgba(111, 111, 111, 0.1);
}
The last index is the opacity of the background, and it will not overlap with your font.
#jnav does have an opacity of 1. But that would be, in a sense, relative to its parent .jumbotron with an opacity of 0.75.
As techfoobar mentions, opacity is compounded with the inherited value. And hence, #jnav's opacity will effectively be 1 * 0.75.
Here's what MDN has to say:
The value applies to the element as a whole, including its contents, even though the value is not inherited by child elements. Thus, an element and its contained children all have the same opacity relative to the element's background, even if the element and its children have different opacities relative to one another.
View this question, if you want to achieve a transparent background but not the content effect.