I have 3 DIVS. A Parent, a Child and a Layer (sibling of Parent). The Layer should appear between parent and child.
<div class="parent">
<div class="child"></div>
</div>
<div class="layer"></div>
CSS
div {
position: absolute;
width: 100px;
height: 100px;
}
.parent {
z-index: 1;
top: 0;
left: 0;
background-color: red;
}
.child {
z-index: 3;
top: 60px;
left: 60px;
background-color: blue;
}
.layer {
z-index: 2;
top: 30px;
left: 30px;
background-color: green;
}
Here's a JS Fiddle:
http://jsfiddle.net/PHwua/
strangely, i can't get the layer to appear between parent and child. On a live site, this works for some reason in all browsers (IE8-11, FF, Chrome) except Safari.
Now i can't even get the JSFiddle to work.
Your issue probably has to do with the stack(ing) order of HTML elements.
Basically, z-index affects elements inside the same stacking context. The parent and layer boxes are in the same context; so their z-indexes are evaluated first. Then the child box's z-index is evaluated against its stacking context (which nothing else exists in it since its a sub-context of parent).
If you take parent out of the stacking order (by making its position static, for example, or getting rid of its z-index), then child and layer will be in the same stacking context.
Forked JSFiddle here.
Related
In the context of the development of a menu, I have a fixed position div (sort of popin that contains level 3 and more menu items) which is contained by an absolute positioned div (that contains level 2 items).
Sometimes the absolute div has a scrollbar and in this case this scrollbar appears above the fixed div on Google Chrome (this doesn't happen on FF and IE).
Simplified jsfiddle example
.level-1 {
background: red;
height: 150px;
width: 200px;
position: absolute;
z-index: 1;
overflow-y: auto;
overflow-x: auto;
}
.level-1-content {
height: 200px;
}
.level-2 {
position: fixed;
left: 50px;
top: 50px;
width: 400px;
height: 200px;
z-index: 2;
background: blue;
}
<div class="level-1">
<div class="level-1-content"></div>
<div class="level-2"></div>
</div>
This issue happens only when the fixed and/or the absolute div have a z-index.
In the jsfiddle simple example, the z-index are not required, but in the context of my menu, I need them.
Does anybody know a CSS solution in order to Chrome not to display this scrollbar above the child div in this context (I mean, with my constraints, i.e. the parent div is absolute and has a z-index and the child div is fixed) ?
Thanks in advance.
Good afternoon,
I've made a very simple page demonstrating the different position types we can use in css for a new starter at our company, and quite embarrassingly this has exposed a gap in my own knowledge.
I have positioned all elements on the page but I've noticed that my relative positioned element will sit on top of my sticky element when the page is scrolled. It is almost like it has a z-index which is higher than my sticky element - I haven't set any z-index values though.
Is this the correct behavior given code I have provided? Apologies if this is really simple stuff, It has me sitting here scratching my head.
CodePen
body {
font-size: 20px;
font-family: Arial, Helvetica, sans-serif;
height: 3000px;
}
.relative {
width: 20%;
min-height: 200px;
background-color: dodgerblue;
color: white;
padding: 10px;
position: relative;
}
.sticky {
position: sticky;
background-color: green;
color: white;
padding: 10px;
width: 20%;
height: 200px;
top:0;
}
.fixed {
position: fixed;
padding: 10px;
background-color: aqua;
color: black;
height: 200px;
width: 20%;
right: 200px;
top: 300px;
}
.absolute {
position: absolute;
padding: 10px;
right: 0;
top: 0;
width: 20%;
height: 200px;
background-color: red;
color: white;
}
.static {
position: static;
width: 20%;
background-color: blueviolet;
padding: 10px;
color: white;
height: 200px;
}
<body>
<div class="sticky">
This is a sticky div
</div>
<div class="relative">
This is a relative div
</div>
<div class="absolute">
This is an absolute div
</div>
<div class="static">
This is a normal div
</div>
<div class="fixed">
This is a fixed div
</div>
</body>
Yes, this is normal behavior. Your relatively positioned element appears after your stickily positioned element in the source, so its natural stack level is higher and therefore it appears above the stickily positioned element. See section 9.9 of CSS2, or section 11 of css-position.
Stickily positioned elements obey the same stacking rules as relatively and absolutely positioned elements.
If two elements are in the same stacking context (have the same z-index value), then the browser will just look at the order that they are inserted in the dom: the last one would appear on top of the previous.
Here's more info on this topic: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_without_z-index
When the z-index and position properties aren’t involved, the rules
are pretty simple: basically, the stacking order is the same as the
order of appearance in the HTML. (OK, it’s actually a little more
complicated than that, but as long as you’re not using negative
margins to overlap inline elements, you probably won’t encounter the
edge cases.)
When you introduce the position property into the mix, any positioned
elements (and their children) are displayed in front of any
non-positioned elements. (To say an element is “positioned” means that
it has a position value other than static, e.g., relative, absolute,
etc.)
https://philipwalton.com/articles/what-no-one-told-you-about-z-index/
Z-index only works on elements that have been position with absolute, relative, fixed. As your sticky element appears before your static element in the HTML, it will take precedence in the stacking order.
How can I set mix-blend-mode on an element, but not it's children? Setting the children to the default value of normal does not seem to work:
http://jsfiddle.net/uoq916Ln/1/
The solution on how to avoid mix-blend-mode affects children:
Make child element position relative, give it a width and height;
Create some real or pseudo element inside the child with absolute position, and apply mix-blend-mode to it;
Create inner element inside the child for your content. Make it's position absolute, and put it on top of other elements;
Live example
html
<div class="bkdg">
<div class="blend">
<div class="inner">
<h1>Header</h1>
</div>
</div>
</div>
css
.blend {
position: relative; /* Make position relative */
width: 100%;
height: 100%;
}
.blend::before { /* Apply blend mode to this pseudo element */
content: '';
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 1;
background-color: green;
mix-blend-mode: multiply;
}
.inner { /* This is our content, must have absolute position */
position: absolute;
z-index: 2;
}
h1 {
color: white;
}
I know this was asked over two years ago, but it could be useful in the future as it could be a better solution than creating pseudo-elements.
There is the CSS isolation property that allows to choose wether the child element should be rendered in its parent's context (auto) or as part of a new context, thus without any blend mode applied to it (isolate).
Check out this page for examples
someone commented that the the whole block is rendered with the effect and that is why you're having the issue. I am able to accomplish what you're are trying to do by removing the h1 from the block, position absolute, and a z-index of 1. here is a jsfiddle to show the effect.
html
<div class="bkdg">
<h1>Header</h1>
<div class="blend">
</div>
</div>
css
.blend {
background-color: green;
mix-blend-mode: multiply;
width: 700px;
height: 35px;
}
h1 {
color: white;
position: absolute;
top: -15px; left: 10px;
z-index: 1;
}
https://jsfiddle.net/jckot1pu/
It’s impossible to remove an element’s mix-blend-mode from its children.
MDN says that mix-blend-mode:
sets how an element's content should blend with the content of the element's parent and the element's background
To achieve the desired effect, place the child in a separate stacking context and make sure it renders on top of the element with mix-blend-mode set.
You need two things to make this work:
Make sure that your opaque content (your text) is not a child of the element that sets the background and the blend mode. For example, with CSS Grid Layout.
Make sure the text is rendered over, and thus not affected by, the element that sets the background and the blend mode. Setting mix-blend-mode on your background will create a stacking context for it, and you may need to give your content its own stacking context to ensure it gets rendered above it.
Position your elements with CSS Grid:
define a grid container with one auto-sized grid area
place both the background element and the text element into that one grid area (so that they overlap)
let the text element dictate the size of the grid area
have the background element stretch to the size of the grid area, which is dictated by the size of the text element
Then, set isolation: isolate on the text element to ensure it gets rendered above, and not under the background element.
A working example
.container {
display: grid;
grid-template-areas: 'item';
place-content: end stretch;
height: 200px;
width: 400px;
background-image: url(https://picsum.photos/id/237/400/200);
background-size: cover;
background-repeat: no-repeat;
}
.container::before {
content: '';
grid-area: item;
background-color: seagreen;
mix-blend-mode: multiply;
}
.item {
grid-area: item;
isolation: isolate;
color: white;
}
h1,
p {
margin: 0;
padding: 10px;
}
<div class="container">
<div class="item">
<h1>HEADLINE</h1>
<p>Subhead</p>
</div>
</div>
An important note if you're using the excellent pseudoelement ::before/::after solution posted by Rashad Ibrahimov.
I found that I had to remove z-index from the parent element and apply it only to the pseudoelements and child elements before mix-blend-mode: multiply would work.
For example
#wrapper {
position: relative;
}
#wrapper .hoverlabel {
position: absolute;
bottom: 0;
left: 0;
right: 0;
/* z-index: 90; Uncomment this to break mix-blend-mode. Tested in Firefox 75 and Chrome 81. */
}
#wrapper .hoverlabel::before {
position: absolute;
content: "";
top: 0;
bottom: 0;
left: 0;
right: 0;
mix-blend-mode: multiply;
z-index: 90;
background-color: rgba(147, 213, 0, 0.95);
}
Explain this behavior:
<div style="z-index: 1"></div>
<div></div>
<div></div>
<div></div>
div {
position: relative;
background: red;
width: 100px;
height: 100px;
}
div:before {
position: absolute;
background: blue;
width: 100px;
height: 100px;
z-index: -1;
content: "";
left: -5px;
top: -5px;
}
http://jsfiddle.net/2VexH/2/
Only difference is the first div has z-index: 1 set.
Setting a positioned element's z-index to anything other than auto (the initial value) causes the element to generate a new stacking context for its descendant boxes.
This prevents any of its descendants from appearing below it, including the div:before pseudo-element, even if their z-index is negative. Of course, any descendant with a negative z-index will continue to appear below a descendant with a zero or positive z-index within the containing element, but that containing element will always be at the very back.1
The rest of your div elements that don't have a z-index set will use the initial value instead, and therefore not generate stacking contexts for their pseudo-elements, allowing the pseudo-elements to appear below the real elements. The stacking context in which they are drawn instead is that of body.
1 Note that the content of a stacking context root will still appear above the background of a descendant with a negative z-index. This is intentional, and is covered in greater detail in this answer, with relevant links to the spec.
So I have three div's
One parent and two child.
The parent is as follows:
#parent {
overflow:auto;
margin: 0 auto;
margin-top:37px;
min-height: 100%;
width:875px;
}
the two child divs are as follows
#child1 {
overflow:auto;
min-height:150px;
border-bottom:1px solid #bbb;
background-color:#eee;
opacity:0.4;
}
#child2 {
height:100%;
background-color:white;
}
The parent div extends 100% as I can see the borders of it till the end of the page but the child2 is not extending down to the end of the page like the parent div.
height doesn't behave the way you seem to be anticipating. When you specify height: 100% that percentage is calculated by looking up the DOM for the first parent of said element with a height specified that has absolute or relative positioning.
You can cheat when it comes to the body tag, so if you had something like this:
<body>
<div style="height: 100%">
</div>
</body>
Some browsers/versions will behave the way you expect by taking up the total height of the page. But it won't work when you go any deeper than that.
Here is the approach I use to strech a div to the bottom of the page, it involves absolute positioning (nice thing about this one is that it is pretty cross-browser compliant and doesn't require javascript to pull it off):
<div id="parent">
<div id="childNorm"></div>
<div id="childStrech"></div>
</div>
#parent
{
position: absolute;
width: 1000px;
bottom: 0;
top: 0;
margin: auto;
background-color: black;
}
#childNorm
{
position: absolute;
width: 1000px;
top: 0;
height: 50px;
background-color: blue;
color: white;
}
#childStrech
{
position: absolute;
width: 1000px;
top: 50px;
bottom: 0;
background-color: red;
color: white;
}
Here is a Jsfiddle for demo: http://jsfiddle.net/t7ZpX/
The trick:
When you specify absolute positioning and then put in bottom: 0; that causes the element to stretch to the bottom of the page; You just have to worry about positioning the elements as a trade off.
Yes, this is one of the annoying things in css. min-height is not considered a "height" for purposes of calculating height. See http://jsfiddle.net/3raLu/3/. You need to have height: 100% on the parent div to make the child full height. Or, if you can have it be absolutely positioned, then this works: http://jsfiddle.net/3raLu/6/.