Why do parent elements sometimes not cover the children's dimensions? - html

Codepen link: https://codepen.io/lolcatBH/pen/OJbgLyd
I have the following html:
<div id="page">
<div class="overlay"></div>
<div class="click">SHOW POPUP</div>
<div class="popup">
<h1>Korean language</h1>
<button class ="close">X</button>
<div class="desc">Korean (North Korean: 조선말/朝鮮말, chosŏnmal; South Korean: 한국어/韓國語, hangugeo) is an East Asian language spoken by about 77 million people.</div>
</div>
</div>
And CSS:
#page {
position: absolute;
top: 50%;
left: 25%;
}
.popup {
width: 300px;
height: 300px;
border: 1px solid black;
z-index: 1;
position: absolute;
top: -10%;
transform: scale(0);
padding: 20px;
}
#page .overlay {
position: fixed;
top: 0px;
left: 0px;
width: 100vw;
height: 100vh;
background: rgba(0,0,0,0.7);
z-index: 1;
display: none;
}
#page.active .overlay {
display: block;
}
.popup.active {
transform: scale(1);
background-color: white;
}
.close {
position: absolute;
right: 0%;
top: 0%;
color: white;
background: black;
border-radius: 50%;
}
.click {
border: 1px solid black;
padding: 20px;
font-size: 20px;
text-align: center;
z-index: 0;
cursor: pointer;
}
h1, desc {
text-align: center;
}
JS:
const click = document.querySelector('.click');
const x = document.querySelector('.close');
const page = document.querySelector('#page');
const popup = document.querySelector('.popup');
const showPopup = () => {
page.classList.toggle('active');
popup.classList.add('active');
}
const hidePopup = () => {
page.classList.toggle('active');
popup.classList.remove('active');
}
click.addEventListener('click', showPopup);
x.addEventListener('click', hidePopup);
At first, what I tried to do is instead of creating a separate overlay div, I was going to put a background color on the #page element. However, in effect, the background only applies to the button element (.click). I don't actually understand why in this case, since the background-color doesn't seem to affect the .popup element.
Imgur: https://imgur.com/a/UEgXSlY
So my question is, why does the #page element not cover all of its children's width and height? In this case, I thought it would have cover the whole page. I've also tried putting width: 100vw and height:100vh but it in turn only applied the dimensions to the button.
Imgur: https://imgur.com/a/QOMlnTs

The reason is that most of your elements have position set to absolute or fixed, which makes them completely (fixed) or partly (absolute) independent from their parent, i.e. there is no space of its own reserved for them, therefore they typically overlap other elements.
So the parent doesn't span or cover them, but only those elements which don't have a set position or which have position: relative or static.

Related

Align DIV container to bottom of page with variable height element in it

I am trying to position an element on a page so that it is always spaced away from other control elements on the page. I want this element to be positioned anywhere on the page, depending on other control elements on the page, what action the user has taken etc. These are the eight valid positions:
TOP_CENTER
TOP_LEFT
TOP_RIGHT
LEFT_CENTER
RIGHT_CENTER
BOTTOM_CENTER
BOTTOM_LEFT
BOTTOM_RIGHT
What I have so far are 3 component elements: 1) a DIV container, 2) an image which should always be at the top of the DIV container, 3) some text which should always be at the bottom of the DIV container. My CSS looks as follows:
#floatingElement {
left: 60px;
top: 11%;
width: 25%;
height: 25%;
position: absolute;
pointer-events: none;
z-index: 1;
}
#floatingElement img {
display: block;
margin: auto;
max-width: 95%;
max-height: 100%;
}
#floatingElement div {
text-align: center;
background: rgba(255,255,255,0.5);
border-radius: 5%;
box-shadow: 3px 3px 2px #888888;
font-size: 2vmax;
}
The above works perfectly whenever the element is positioned "TOP" or "CENTER". However, "BOTTOM" positioning is causing problems, I think because the container DIV is somehow not taking into account its height based on the amount of text in the 3rd (text DIV) element.
How can I make it so that the element can always align to the bottom of the page whenever a "BOTTOM" position is chosen, so that the container DIV includes the height of the text DIV (which can vary depending on the amount of text in that DIV), similar to this image:
The black border is my window, the red box is my image and the green box is the text DIV. Both the red and green boxes are inside my container DIV.
Upon being altered using bottom, the scenario you want is not working as expected because you specified the height of #floatingElement to be fixed (in your case, 25%). Having a fixed value relative to the container and not considering the height of the text div, the floating element's position will definitely seem off when altered using bottom. This is because bottom places the element based on the bottom-most pixel of your element's height, which in your case may be smaller than enough to cover your whole text div's and image's height. To see what I mean, do try removing the comment on height below and inspect the element of #floatingElement.
Here's a working example, simply click the button to adjust the div's position (try using full-page mode when running as the window is a tad small).
const button = document.querySelector('.adjustPosition')
const addText = document.querySelector('.addText')
const elem = document.querySelector('#floatingElement')
const textDiv = elem.querySelector('.textDiv')
button.addEventListener('click', e => {
if (elem.classList.contains('topLeft')) {
elem.classList.add('topCenter')
elem.classList.remove('topLeft')
} else if (elem.classList.contains('topCenter')) {
elem.classList.add('topRight')
elem.classList.remove('topCenter')
} else if (elem.classList.contains('topRight')) {
elem.classList.add('rightCenter')
elem.classList.remove('topRight')
} else if (elem.classList.contains('rightCenter')) {
elem.classList.add('bottomRight')
elem.classList.remove('rightCenter')
} else if (elem.classList.contains('bottomRight')) {
elem.classList.add('bottomCenter')
elem.classList.remove('bottomRight')
} else if (elem.classList.contains('bottomCenter')) {
elem.classList.add('bottomLeft')
elem.classList.remove('bottomCenter')
} else if (elem.classList.contains('bottomLeft')) {
elem.classList.add('leftCenter')
elem.classList.remove('bottomLeft')
} else if (elem.classList.contains('leftCenter')) {
elem.classList.add('topLeft')
elem.classList.remove('leftCenter')
}
})
addText.onclick = () => {
textDiv.innerText += "Added some more text so that the div can be larger"
}
html,
body {
height: 100%;
width: 100%;
margin: 0;
}
#floatingElement {
width: 25%;
/* height: 25%; This causes the height to be fixed */
position: absolute;
pointer-events: none;
z-index: 1;
}
#floatingElement img {
display: block;
width: 100%;
height: 100px;
margin: auto;
object-fit: cover;
}
#floatingElement .textDiv {
text-align: center;
background: rgba(255, 255, 255, 0.5);
border-radius: 5%;
box-shadow: 3px 3px 2px #888888;
}
.adjustPosition {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 25px;
height: 25px;
background: #12121299;
border-radius: 50%;
cursor: pointer;
}
.addText {
position: absolute;
top: 60%;
left: 50%;
transform: translate(-50%, -50%);
cursor: pointer;
}
.topLeft {
top: 10px;
left: 10px;
}
.topCenter {
top: 10px;
left: 50%;
transform: translateX(-50%);
}
.topRight {
top: 10px;
right: 10px;
}
.leftCenter {
top: 50%;
left: 10px;
transform: translateY(-50%);
}
.rightCenter {
top: 50%;
right: 10px;
transform: translateY(-50%);
}
.bottomLeft {
bottom: 10px;
left: 10px;
}
.bottomRight {
bottom: 10px;
right: 10px;
}
.bottomCenter {
bottom: 10px;
left: 50%;
transform: translateX(-50%);
}
<div id="floatingElement" class="topLeft">
<img src="https://homepages.cae.wisc.edu/~ece533/images/airplane.png">
<div class="textDiv">Variable height here because it has a long text</div>
</div>
<div class="adjustPosition"></div>
<div class="addText">Add Text</div>

Maintain alignment with scaled sibling without inverse-scaling

I have an element that requires the background to be scaled, without scaling the elements within the parent. I have achieved this by using a pseudo element to define the background, and then on hover I simply scale the pseudo element. So far, so good...
The problem is, I need some of the elements to stay inline with the scaled background, despite not scaling themselves. My original plan was to simply translate them, but I quickly realised that is not possible due to scale being based on multiples, and translate being based on percentage/pixels etc...
The obvious solution is to scrap scale and instead use margin to shrink the absolutely positioned pseudo element. However, my reservation with this is that it is bad practice to transition the margin value.
Can anybody think of a way in which I can use scale, and also maintain the alignment?
Update
I want to avoid inverse/reverse scaling at all costs as it renders badly in the browser in most cases. With that in mind, I don't think this is actually possible but will leave the question open in case anyone is aware of some CSS magic.
See the following snippet as an example:
.tile {
position: relative;
width: 500px;
height: 100px;
padding: 40px;
}
.tile:hover:before {
transform: scale(.9);
}
.tile:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
transition: transform .3s ease-out;
}
h1 {
text-align: center;
color: white;
}
.tile > .button {
position: absolute;
right: 0;
top: 0;
display: inline-block;
background: red;
padding: 10px 15px;
color: white;
}
<div class="tile">
<h1>Hello World</h1>
<div class="button">Align Me</div>
</div>
Try scaling .tile itself and reverse-scaling its children:
.tile {
position: relative;
width: 500px;
padding: 40px;
background: #000;
transition: transform .3s ease-out;
}
h1 {
text-align: center;
}
.tile>* {
color: white;
transition: transform .3s ease-out;
}
.tile>.button {
position: absolute;
right: 0;
top: 0;
background: red;
padding: 10px 15px;
color: white;
transform-origin: 100% 0;
}
.tile:hover {
transform: scale(.9);
}
.tile:hover>* {
transform: scale(1.1);
}
<div class="tile">
<section>
<h1>Hello World</h1>
<p>I have an element that requires the background to be scaled, without scaling the elements within the parent. I have achieved this by using a pseudo element to define the background, and then on hover I simply scale the pseudo element. So far, so good...
The problem is, I need some of the elements to stay inline with the scaled background, despite not scaling themselves. My original plan was to simply translate them, but I quickly realised that is not possible due to scale being based on multiples,
and translate being based on percentage/pixels etc... The obvious solution is to scrap scale and instead use margin to shrink the absolutely positioned pseudo element. However, my reservation with this is that it is bad practice to transition the
margin value. Can anybody think of a way in which I can use scale, and also maintain the alignment?</p>
</section>
<div class="button">Align Me</div>
</div>
Another idea is animating top and right of .button:
html,
body {
width: 75%;
margin: 0;
padding: 0
}
* {
box-sizing: border-box
}
.tile {
position: relative;
width: 100%;
padding: 40px;
color: white;
}
.tile:hover:before {
transform: scale(.9);
}
.tile:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
transition: transform .3s ease-out;
}
h1 {
text-align: center;
}
.tile>.button {
position: absolute;
right: 0;
top: 0;
background: red;
padding: 10px 15px;
color: white;
transition: .3s ease-out;
}
.tile:hover>.button {
top: 5%;
right: 5%
}
<div class="tile">
<h1>Hello World</h1>
<p>I have an element that requires the background to be scaled, without scaling the elements within the parent. I have achieved this by using a pseudo element to define the background, and then on hover I simply scale the pseudo element. So far, so good...
The problem is, I need some of the elements to stay inline with the scaled background, despite not scaling themselves. My original plan was to simply translate them, but I quickly realised that is not possible due to scale being based on multiples,
and translate being based on percentage/pixels etc... The obvious solution is to scrap scale and instead use margin to shrink the absolutely positioned pseudo element. However, my reservation with this is that it is bad practice to transition the
margin value. Can anybody think of a way in which I can use scale, and also maintain the alignment?</p>
<div class="button">Align Me</div>
</div>
The next idea is using a bit more complex code, but doing animation of transform property only:
html,
body {
width: 75%;
}
* {
box-sizing: border-box
}
.tile {
position: relative;
width: 100%;
padding: 40px;
color: white;
}
.tile:hover:before {
transform: scale(.9);
}
.tile:before,
.tile>.button {
content: '';
position: absolute;
top: 0;
left: 0;
z-index: -1;
width:100%; height:100%;
background: #000;
transition: transform .3s ease-out;
}
h1 {
text-align: center;
}
.tile>.button {
z-index: 1;
display: flex;
align-items: flex-start;
margin: 0 -100% -100% 0;
background: transparent;
transition: .3s ease-out;
pointer-events: none;
}
.tile>.button div {
padding: 10px 15px;
background: red;
cursor: pointer;
pointer-events: all;
}
.tile>.button:before {
content: '';
flex: 1 0;
}
.tile:hover>.button {
transform: translate3d(-5%, 5%, 0);
}
<div class="tile">
<h1>Hello World</h1>
<p>I have an element that requires the background to be scaled, without scaling the elements within the parent. I have achieved this by using a pseudo element to define the background, and then on hover I simply scale the pseudo element. So far, so good...
The problem is, I need some of the elements to stay inline with the scaled background, despite not scaling themselves. My original plan was to simply translate them, but I quickly realised that is not possible due to scale being based on multiples,
and translate being based on percentage/pixels etc... The obvious solution is to scrap scale and instead use margin to shrink the absolutely positioned pseudo element. However, my reservation with this is that it is bad practice to transition the
margin value. Can anybody think of a way in which I can use scale, and also maintain the alignment?</p>
<div class="button">
<div>Align Me</div>
</div>
</div>
If you are scaling by p then you are reducing the size and the new width will become width*(1 - p). Same logic for the height. You can consider the use of calc() and easily define the translate using this formula.
We divide by 2 because we reduce from both side and we will translate from 1 side
.tile {
position: relative;
width: 540px;
height: 200px;
display:flex;
align-items:center;
justify-content:center;
}
.tile:hover:before {
transform: scale(0.9);
}
.tile:hover .button{
transform: translate(calc(-540px*0.1/2),calc(200px*0.1/2));
}
.tile:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
transition: transform .3s;
}
h1 {
text-align: center;
color: white;
margin:0;
}
.tile > .button {
position: absolute;
right: 0;
top: 0;
display: inline-block;
background: red;
padding: 10px 15px;
color: white;
transition: transform .3s ;
}
<div class="tile">
<h1>Hello World</h1>
<div class="button">Align Me</div>
</div>
You can consider CSS variables to easily change the scale value:
.tile {
position: relative;
width: 540px;
height: 200px;
display:flex;
align-items:center;
justify-content:center;
--s:0.9;
}
.tile:hover:before {
transform: scale(var(--s));
}
.tile:hover .button{
transform: translate(calc(-540px*(1 - var(--s))/2),calc(200px*(1 - var(--s))/2));
}
.tile:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
transition: transform .3s;
}
h1 {
text-align: center;
color: white;
margin:0;
}
.tile > .button {
position: absolute;
right: 0;
top: 0;
display: inline-block;
background: red;
padding: 10px 15px;
color: white;
transition: transform .3s ;
}
<div class="tile">
<h1>Hello World</h1>
<div class="button">Align Me</div>
</div>
<div class="tile" style="--s:0.5">
<h1>Hello World</h1>
<div class="button">Align Me</div>
</div>

Issue with z-index and transformations

This has been asked before but none of the answers seem to be working for me.
My issue is related to a lost z-index when a transformation is applied.
I have an overlay div with a defined z-index, it has a sibling with no z-index and this div contains a child with a z-index greater than the overlay. This child can be dragged around.
At some point I rotate this sibling and it's child loses the z-index.
How can I prevent this from happening?
I tried several solutions attemps like transform-style: flat; or transform-style: preserve-3d; but with no luck
This is the code
HTML
<div class="main">
<div class="some_container">
<div class="drag"></div>
</div>
</div>
<div class="overlay"></div>
<br><br><br>
<button>rotate!</button>
CSS
body {
padding: 20px
}
div {
border: 1px solid black;
width: 50px;
height: 50px;
}
.main {
border: 1px dashed blue;
padding: 15px;
}
.some_container {
position: relative;
background-color: green;
}
.overlay {
background-color: red;
position: absolute;
left: 35px;
top: 35px;
z-index: 5
}
.drag {
position: relative;
width: 30px;
height: 30px;
background-color: lime;
z-index: 10;
cursor: move;
}
.rotated {
transform: rotateZ(15deg);
}
.rotated .drag {
background-color: yellow;
transform: rotateZ(-15deg);
position: relative;
z-index: 100;
transform-style: flat;
}
JS
$(".drag").draggable();
$("button").click(function()
{
$(".some_container").addClass("rotated");
});
fiddle: https://jsfiddle.net/2zkn9dap/
The transform that you have in your .rotated class creates a new stacking context that is changing the order that the elements are layered. A great explanation with more detail can be found here: z-index is canceled by setting transform(rotate)
The best approach to solving this is to move the .drag div to be a sibling of the .overlay and .some_container div. Then update your JS to add the rotated class to the green and yellow squares so they are both rotated. Otherwise, you'll never be able to get the yellow square on top of the red one consistently, because the z-index of the parent, in this case the .some_container div takes precedence.
$("button").click(function(){
$(".green").addClass("rotated")
$(".lime").addClass("rotated").css({backgroundColor: 'yellow'});
});
body {
padding: 20px
}
div {
border: 1px solid black;
width: 50px;
height: 50px;
}
.container {
border: 1px dashed blue;
padding: 15px;
}
.green {
position: absolute;
background-color: green;
z-index: 2;
}
.red {
background-color: red;
position: absolute;
left: 35px;
top: 35px;
z-index: 3;
}
.lime {
position: absolute;
width: 30px;
height: 30px;
background-color: lime;
z-index: 4;
cursor: move;
}
.rotated {
transform: rotateZ(15deg);
}
<div class="container">
<div class="green">
</div>
<div class="lime"></div>
</div>
<div class="red"></div>
<br><br><br>
<button>rotate!</button>
<script src="http://code.jquery.com/jquery-3.3.1.js"></script>
Change the position: relative to absolute of .lime.
If you don't want to rotate the '.lime' div, then remove `.addClass("rotated") on the 4th line of the script.

Style a banner using transform: rotateZ() and ::after pseudo-elements

I want to add some pizzazz to some banners... my banners are simply an h1 element with a background color property that stretches the legth of the containing element.
Here is my CSS:
.banner {
position: relative;
z-index: 1000;
padding: 20px;
}
.banner-blue {
background-color: #93DEFF;
color: #222222;
}
.banner-yellow {
background-color: #FFF072;
color: #777777;
}
.banner-red {
background-color: #FF356B;
color: white;
}
And I would apply it like this:
<h1 class="banner banner-yellow">I'm a banner!</h1>
My problem:
I want to overlay a copy of the banner background but change the color and rotate it slightly on the z-axis to get an effect like this.
However I can't work out how to do that using the ::before (or is it ::after) psuedo-elements to do that... here is what I have tried:
.banner-red::before {
display: block;
position: absolute;
padding: 20px;
content: "";
background-color: rgba(255,30,60,0.4);
transform: rotateZ(3deg);
width: 100%;
margin-left: -30px;
}
Here is a codepen of it running: not looking too good: https://codepen.io/jethazelhurst/pen/JyKqRB
Just rotate your box in the opposite direction: transform: rotateZ(-3deg);
You can set the top and left value in order to place your rotated box correctly.
.banner-red::before {
display: block;
position: absolute;
content: "";
background-color: rgba(255,30,60,0.4);
transform: rotateZ(-3deg);
width: 102%;
height: 97px;
margin-left: -30px;
top: 2px;
}
Of course you can change the colors: your horizontal box is #91c6ff and the rotated one is #91c6ff. Also, they are transparent.
Here's a fork of your project: https://codepen.io/anon/pen/zdBVGe
And with the colors:
Make a element with another child element for text, span for example. Then you can set z-index on span so that text is above pseudo element.
div {
background: #91C6FF;
padding: 25px;
margin: 40px;
position: relative;
}
div:after {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: rgba(135, 171, 255, 0.7);
transform: rotate(-4deg);
}
span {
position: relative;
z-index: 2;
font-size: 30px;
}
<div><span>Lorem ipsum dolor.</span></div>

why is `position:relative` functioning like `z-index`?

Hey guys I am relatively very new to HTML and CSS and have the following difficulty I made a small input box and I am trying to add a few CSS transforms and create a small animation on the input box. Code below:
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
}
.input {
position: relative;
display: inline-block;
max-width: 350px;
width: 100%;
}
.akira-input {
position: absolute;
top: 0;
left: 0;
display: block;
height: 100%;
width: 100%;
background: transparent;
z-index: 10;
}
.akira-label {
display: block;
height: 100%;
padding: 0;
width: 100%;
background: #696a6e;
color: #cc6055;
cursor: text;
}
.akira-label:before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: red;
-webkit-transform: scale3d(0.97, 0.50, 1);
transform: scale3d(0.97, 0.50, 1);
-webkit-transition: .3s;
-o-transition: .3s;
transition: .3s;
}
.label-content {
color: #000;
font-size: 1.3em;
text-transform: uppercase;
position: relative;
display: block;
text-align: center;
padding: 1.6em 0;
width: 100%;
-webkit-transition: .3s;
-o-transition: .3s;
transition: .3s;
}
<span class="input">
<input type="text" id="akira" class="akira-input">
<label for="akira" class="akira-label">
<span class="label-content">Akira</span>
</label>
</span>
My difficulty is, if I apply position:relative to <span class="label-content">Akira</span>, it shows, if I remove position:relative , that element disappears from view.
My question is why is position:relative functioning like z-index?
Can somebody elaborate ??
EDIT :: refering to Justinas answer , i have the folloing question ,
Does applying position:relative places an element
higher in the stack , even without applying z-index ??
z-index is only working for non-static elements, so when you remove position: relative than element becomes statically positioned and moves below higher index elements (disappears from view). When you add position: relative to element, than z-index will take effect and so element appears in your view.
Also position and z-index is two different properties
position - how element is positioned according to other elements on page. Default to static
z-index - how high element is in z-axis (z-index: 2 - is behind element with z-index: 10). Default to 5
.wrapper {
position: relative;
}
#static {
position: static;
z-index: 999;
width: 400px;
height: 150px;
background-color: #ddd;
padding: 3px;
}
#top-1 {
position: absolute;
z-index: 10;
left: 8px;
top: 45px;
width: 330px;
height: 80px;
background-color: #888;
padding: 3px;
}
#relative {
position: relative;
z-index: 11;
background-color: #88a;
width: 330px;
height: 80px;
padding: 3px;
top: 30px;
left: 8px;
}
#top-2 {
position: absolute;
z-index: 10;
left: 0;
top: 0;
width: 400px;
height: 150px;
background-color: #dda;
padding: 3px;
}
<div class='wrapper'>
<div id="static">
I'm static, so behind #top-1, but have z-index higher than #top-1... Means z-index has no effect.
<br/>Text that is not visible, because behind #top-1 element
</div>
<div id='top-1'>
I'm above #static, because i have non-static position, so my z-index has effect.
</div>
</div>
<hr/>
<div class='wrapper'>
<div id="relative">
I'm relative and above #top-2, because my z-index higher than #top-2... Means z-index has taken effect.
</div>
<div id='top-2'>
I'm below #relative, because i have lover z-index.
<br/>Text that is not visible, because behind #top-1 element
</div>
</div>
z-index only works on positioned elements so position:absolute, position:relative or position:fixed
It does not behave like a z-index, because z-index specifies an ordering rule, but not the way how the element is displayed.
position: relative; says to go to the relative mode where it can compete the absolutely positioned elements.
Your problem here is that :before pseudo-element is a hierarchical sibling of span, and it takes the whole available parent width. So it fully covers a static span element.
When you make it relative, it becomes shown because when z-index is not specified for both non-static elements they are shown in the same order like they are placed in HTML (so element which is defined in HTML later is always on top).
Your structure is:
label
:before
span
so the span becomes visible.