HTML pseudo elements placed over actual child nodes - html

For some reason, when an element's pseudo child node is added, its z-index is higher than its child nodes, even though its a ::before element, which appears before the relative child nodes, but doesn't display that way. Why does this happen, and is there a way to fix it?
Here's an example where the child <h1> node should appear as if it's hovering over the black pseudo element, but that isn't the case.
https://jsfiddle.net/9u33vko0/
Is my understanding just fundamentally wrong?
div {
width: 100%;
padding: 40px 0;
border: 1px solid #DDD;
text-align: center;
color: #FFF;
position: relative;
}
div::before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: #000;
width: 100%;
}
<div>
<h1> <!-- should appear above the black ::before element -->
Hello, World!
</h1>
</div>

Even though the :before pseudo element appears before the h1 element in the DOM, it will still overlap the h1 element because it establishes a stacking context due to the fact that it is positioned (with position: absolute).
Here is a relevant quote from the CSS2 specification on stacking contexts (painting order; point 8)
All positioned descendants with z-index: auto or z-index: 0, in tree order. For those with z-index: auto, treat the element as if it created a new stacking context ...
Therefore you could establish a stacking context with the h1 element by positioning it (i.e., by adding position: relative). In doing so, the h1 element will be placed above the :before pseudo element because it appears after the pseudo element in the DOM and both elements are positioned with a z-index of auto.
Updated Example
h1 {
position: relative;
}
div {
width: 100%;
padding: 40px 0;
border: 1px solid #DDD;
text-align: center;
color: #FFF;
position: relative;
}
div::before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: #000;
width: 100%;
}
h1 {
position: relative;
}
<div>
<h1>Hello, World!</h1>
</div>
Of course, you could also just give the pseudo element a negative z-index, but that's besides the point. When it comes down to it, establishing a stacking context is enough since the :before pseudo element appears before the h1 element in the DOM (as you have already pointed out).

Related

Static positioned elements affect Absolute position of subsequent sibling elements

I understand that any element with position: absolute will be positioned relative to the nearest ancestor with a positional attribute such as absolute or relative. This is mentioned in various answers for example here. Also on the w3schools site here...
An element with position: absolute; is positioned relative to the
nearest positioned ancestor (instead of positioned relative to the
viewport, like fixed).
However, inserting a static element appears to disrupt this rule and shifts the absolute element. I'd like to understand why that happens. See code snippet below.
If the static element is chaged to absolute, the subsequent elements are displayed as expected (according to the nearesst positional ancestor rule).
div.relative {
position: relative;
width: 440px;
height: 600px;
border: 3px solid #73AD21;
}
div.static {
position: static;
width: 200px;
height: 100px;
border: 3px solid #73ADff;
}
div.absolute {
position: absolute;
width: 200px;
height: 100px;
border: 3px solid #73AD21;
}
div.absolute2 {
left:210px;
position: absolute;
width: 200px;
height: 100px;
border: 3px solid #ffAD21;
}
<div class="relative">This div element has position: relative;
<div class="static">This div element has position: static</div>
<div class="absolute">This div element has position: absolute, but is positioned relative to the preceding static element, not the first positional ancestor.;</div>
<div class="absolute2">This div element also has position: absolute;</div>
</div>
As this answer explains, if there is no (top, left, right, bottom) attributes the position: absolute element will be positioned by default as if it was part of the normal flow , this is helpful in case you want to maintain a position: absolute next to its sibling like a tool tip would, and manipulate it with margin property, let say:
margin-top:-40px;
margin-left:30px;
but if you set any (top,left,right, bottom), this will reset the default position and will be relative to the parent.
top:0
When W3Schools (and the CSS spec) says that an element is "positioned relative to" something, it is never referring to the element's siblings. It's referring to the element's containing block.
The reason a non-positioned element (position: static) affects the layout of subsequent absolutely positioned elements with auto offsets is because absposed elements with auto offsets will assume their static position (see this answer as well as this one RenzoCC links to), and an element's static position, by nature, is influenced by the layout of surrounding elements, especially preceding siblings.
What absolutely positioning an element without changing any of its offsets does, is cause elements that follow it to be laid out as if that element itself were not there. This is what taking an element out of the flow means.
Static position doesn't affect the Absolute position when it comes to the ancestor position which "position: relative".
But the "position: absolute" has a power to position itself whenever you want inside of the (see the additional code I made) "position: relative;" while the "position: static" don't have the ability to used the Top, Right, Bottom and Left because is it a default position where it is only located at the left side.
div.relative {
position: relative;
width: 440px;
height: 600px;
border: 3px solid #73AD21;
}
div.static {
position: static;
width: 200px;
height: 100px;
border: 3px solid #73ADff;
}
div.absolute {
position: absolute;
width: 200px;
height: 100px;
border: 3px solid #73AD21;
/* Absolute Location inside the Relative Position */
top: 0;
left: 0;
}
div.absolute2 {
left:210px;
position: absolute;
width: 200px;
height: 100px;
border: 3px solid #ffAD21;
/* Absolute Location inside the Relative Position */
top: 0;
}
<div class="relative">This div element has position: relative;
<div class="static">This div element has position: static</div>
<div class="absolute">This div element has position: absolute, but is positioned relative to the preceding static element, not the first positional ancestor.;</div>
<div class="absolute2">This div element also has position: absolute;</div>
</div> <!-- / .relative -->

Bottom property on a relative child with a fixed parent doesn't work

I have a child element that have position:relative ; bottom: 0; and a parent div that have position: fixed ; height: 100vh;. The child element doesn't go to the bottom, why? Below you have the fullcode. With the inspector, I see the .secondary shape on the top, instead to be on the bottom.
HTML
<div class="fullpagenav active_nav">
<div id="secondary" class="secondary toggled-on" aria-expanded="true">
...
</div>
</div>
CSS
.fullpagenav {
position: fixed;
height: 100vh;
width: 100%;
z-index: 2;
}
.secondary.toggled-on {
width: 100%;
display: block;
bottom: 0;
position: relative;
}
position: relative positions an element relative to where it would normally be placed.
That is bottom: 0 says don't offset the element from where it would normally be placed.
What you're looking for is position: absolute
See the description of different position values here: https://developer.mozilla.org/en/docs/Web/CSS/position
In particular, it says this about relative (emphasis mine):
This keyword lays out all elements as though the element were not positioned, and then adjusts the element's position, without changing layout (and thus leaving a gap for the element where it would have been had it not been positioned).
There can be two solutions by using CSS properties
(i) vertical-align: bottom;
(ii) add position : relative to parent and position : absolute to child
Bottom on relatively positioned elements means how far you want to move its bottom from the automatic position. In your case, 0, which makes it stay where it wants.
You need either position: absolute or a flexbox:
.fullpagenav {
display: flex;
flex-direction: colum;
}
.secondary.toggled-on{
margin-top: auto;
}
That is not how position: relative works. It doesn't position elements relative to parent which you are trying to do here when you set bottom: 0, and you want that element to be at bottom of parent. That is what position: absolute is for but it will take that element out of normal elements flow. So other option is to use display: flex on parent element and align-self: flex-end on child element.
body, html {
margin: 0;
}
.fullpagenav {
position: fixed;
height: 100vh;
width: 100%;
z-index: 2;
background: gray;
display: flex;
}
.secondary.toggled-on {
width: 100%;
background: black;
color: white;
align-self: flex-end;
}
<div class="fullpagenav active_nav">
<div id="secondary" class="secondary toggled-on" aria-expanded="true">
Div
</div>
</div>

stacking pseudo-elements with z-index

I am having trouble properly stacking my divs using CSS z-index. In my code, if I set .nose::before and .nose::after to z-index: -1, it puts the two divs at the very back of the stack. However, I just these divs to sit behind the .nose div. Here's my code:
*, *::after, *::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body { height: 100%; }
body {
background: #44BBA4;
}
.head {
position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: 375px;
width: 400px;
background: #df9e27;
border-radius: 50%;
border: 10px solid #000;
}
.head::before, .head::after {
content: "";
position: absolute;
height: 90px;
width: 90px;
background: #df9e27;
border-radius: 50%;
border: 10px solid #000;
z-index: -1;
}
.head::before {
top: -30px;
left: 40px;
}
.head::after {
top: -30px;
right: 40px;
}
.eye {
position: absolute;
top: 150px;
height: 25px;
width: 25px;
background: #000;
border-radius: 50%;
}
.eye.left {
left: 90px;
}
.eye.right {
right: 90px;
}
.eye::before {
content: "";
position: absolute;
top: -50px;
left: -37px;
height: 100px;
width: 100px;
border-radius: 50%;
border: 12px solid transparent;
border-top: 12px solid #000;
}
.nose {
position: absolute;
margin: auto;
right: 0;
left: 0;
bottom: 130px;
height: 30px;
width: 30px;
background: #000;
border-radius: 50%;
}
.nose::before, .nose::after {
content: "";
position: absolute;
height: 68px;
width: 73px;
background: #fff;
border-radius: 50%;
border: 10px solid #000;
z-index: -1;
}
<div class="head">
<div class="eye left"></div>
<div class="eye right"></div>
<div class="nose"></div>
</div>
In short: Set z-index on your head element. Move ears out of the head element.
Here is why.
z-index has stacking contexts. Each of those contexts has a root element (just any html element). Now, to become a root element it must comply with any of the following rules:
be <html> element
position other than static and z-index other than auto
opacity less then 1
So the default stacking context is with the <html> element as a root.
Once the element is inside a scope (in other words, child of a root element), it can only be positioned relative to the elements inside the scope.
Think about it as a nested list.
Wrap here is a root element, as it has position set to relative and z-index to 1. And all of its children are now inside a stacking scope with the Wrap as a root.
So, like in a nested list, children of a particular element cannot appear before its root. For example, Child2 cannot appear before the Wrap, since it is scoped inside of it. But it can appear before the Child1.
Now, in your case the structure is as follows:
Notice that the head is not a root, because it doesn't comply with the rules for becoming one (positioned elements must also have z-index other than auto). Therefore when you assign z-index of -1 to the Nose::before and ::after you get this:
The elements have been positioned all the way behind the Head, because they are in the same stacking scope. But they appear on top of Head::before, since when elements have the same z-index, they are stacked according to the order of appearance in html.
Now, to prevent head children from appearing behind it, you must add z-index to it. This will make it a root element of new stacking scope.
But this creates another problem. Now ears are positioned on top of the head. This is not possible to solve with css alone, since they are inside a stacking scope of the head. And root always lies behind every of its children.
To solve it, you must move the ears out of the head. So, it means, you won't be able to use pseudoelements (before & after) anymore. I suggest creating ear elements outside of the head and wrapping everything in some other element (named bear?) with position relative. Wrapper is needed if you still want to position ears relative to the head.
The answer is mostly inspired by this article.

Why is an absolutely positioned element placed by its sibling instead of at the top corner of the page?

I don't understand why my absolutely positioned element appears after my child_static div. I always thought that absolutely positioned elements are taken out of the flow. So why doesn't child_absolute cover the child_static div?
.parent {
position: relative;
}
.child_static {
height: 20px;
background: blue;
}
.child_absolute {
position: absolute;
height: 20px;
width: 100%;
background: green;
}
<div class='parent'>
<div class='child_static'></div>
<div class='child_absolute'></div>
</div>
http://jsfiddle.net/4v4eLtp1/
I always thought that absolute positioned elements are out of the flow.
Yes, they are removed from normal flow.
I don't understand why absolute positioned element appeared after child_static div.
Just because absolute positioning removes elements from normal flow, it doesn't mean it does alter the position of the elements as well.
In other words, absolutely positioned elements would be at the same place as they are not positioned absolutely unless their top, left, ... offsets are set.
So what happens is that they would overlap next sibling elements, because they are not part of document flow anymore.
For instance have a look at the following example where the gold <div> element is covered by absolutely positioned element.
.parent {
position: relative;
}
.child_static {
height: 20px;
background: blue;
}
.child_absolute {
position: absolute;
height: 20px;
width: 100%;
background: green;
}
.child_static ~ .child_static {
background: gold;
}
<div class='parent'>
<div class='child_static'>Green</div>
<div class='child_absolute'>Blue</div>
<div class='child_static'>Gold</div>
</div>
You forgot to set from which sides your DIV is positioned.
Something like:
top: 0;
left: 0;
http://jsfiddle.net/Paf_Sebastien/uqprmkwo/
(I changed the 2nd DIV dimensions so you can see both.)

::before pseudo-element with negative z-index not displaying behind parent

I swear I've done this a hundred times, but for the life of me, I can't figure out why my :before pseudo-element's background is displaying behind the text but not behind the anchor's background. Thoughts?
.button {
background: tomato;
padding: 10px 30px;
margin: 20px 0;
display: inline-block;
}
.button:hover {
margin: 18px 0 22px 2px;
position: relative;
z-index: 2;
}
.button:hover:after {
position: absolute;
background: red;
top:-2px;
left: -2px;
content: "";
z-index: -10;
width: 100%;
height: 100%;
}
<a class="button" href="#">Meow</a>
Sample here: http://jsfiddle.net/cfree/hnKLr/
When you add a z-index value to an element, you create a new stacking context. Every child of that element will stack on top of it.
So when you want to place an element behind its parent, simply don't create a stacking context on the parent. You still need to use a negative z-index though, because the default stack level of the parent will be 0 (in whatever context the element is)
The ::before and ::after pseudo-elements are named a bit confusingly – they behave as if they were children of the matched element, not its siblings.
In this particular case, it seems like a box-shadow would be most appropriate:
.button:hover {
box-shadow: -2px -2px 0 red;
}
Is this the effect you were looking for?