I want a gray overlay above all children except for the selected one. Given the following structure:
<div class="parent">
<!-- I have this subparent which is absolute. I cannot remove it... -->
<div class="subParent1">
<div class="subParent2">
<!-- This child I want to be above the OVERLAY, aka not greyed out -->
<div class="child selected">child</div>
<div class="child">child</div>
<div class="child">child</div>
</div>
</div>
<!-- This component is underneat subParent in the tree structure -->
<div class="grayOverlay"></div>
</div>
Here's an exact fiddle. Maybe, I could use a pseudo-element instead?
PS: I updated the children to be a bit more nested to align with my actual code.
You can take the reference from below code. I have altered the CSS a bit. I have added z-index wherever required you can optimise that. Also, removed position: absolute; from subParent1 and added top: 0; left: 0; on the grayOverlay. You can optimise it or change it as per you preference.
.parent {
width: 300px;
height: 300px;
position: relative;
background-color: gray;
}
.grayOverlay {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: rgb(107 114 128 / 0.8);
z-index: 11000;
}
.subParent1 {
display: flex;
flex-direction: column;
width: 100%;
z-index: 12000;
}
.child {
color: black;
width: 50px;
height: 20px;
background-color: white;
margin: 10px;
z-index: 10000;
}
.childIWantOverOverlay {
background-color: red;
z-index: 12000;
}
<div class="parent">
<div class="subParent1">
<div class="child childIWantOverOverlay">child</div>
<div class="child">child</div>
<div class="child">child</div>
</div>
<!-- This component is underneat subParent in the tree structure -->
<div class="grayOverlay"></div>
</div>
UPDATE 2
Perhaps also consider use a pseudo-element for this, if it is acceptable in the actual use case.
This approach is more isolated, so it might be less likely to have conflict with other existing elements in the actual project.
Example with pseudo-element:
const btn = document.querySelector("button");
const divs = document.querySelectorAll("div.child");
let i = 0;
btn.addEventListener("click", () => {
divs[i].classList.toggle("selected");
if (i < 2) {
divs[i + 1].classList.toggle("selected")
i++;
return;
};
if (i >= 2) {
i = 0;
divs[i].classList.toggle("selected");
}
});
/* Can Change */
.parent {
width: 300px;
height: 300px;
position: relative;
}
/* 👇 Add this */
.parent::after {
content: "";
position: absolute;
inset: 0;
background-color: rgb(107 114 128 / 0.5);
z-index: 50;
}
/* 👇 Disabled for now
.grayOverlay {
position: absolute;
width: 100%;
height: 100%;
background-color: rgb(107 114 128 / 0.5);
z-index: 50;
}
*/
/* CANNOT CHANGE */
.subParent1 {
position: absolute;
display: flex;
flex-direction: column;
width: 100%;
}
/* Can Change */
.child {
color: black;
width: 50px;
height: 20px;
background-color: pink;
margin: 10px;
z-index: 25;
position: relative;
}
/* Can Change */
.selected {
background-color: red;
/* 👇 Add z-index */
z-index: 100;
}
button {
margin-bottom: 1em;
padding: 6px;
}
<button>Toggle</button>
<div class="parent">
<div class="subParent1">
<div class="subParent2">
<div class="child selected">child</div>
<div class="child">child</div>
<div class="child">child</div>
</div>
</div>
<!-- This component is underneat subParent in the tree structure. I cannot move this into subParent1 -->
<!-- <div class="grayOverlay"></div> -->
</div>
Update: also added position: relative on child.
It seems that this can be achieved by removing the z-index on grayOverlay and subParent1 (the grayOverlay is still stacked on top due to natural placement), and add some z-index on selected.
Example:
const btn = document.querySelector("button");
const divs = document.querySelectorAll("div.child");
let i = 0;
btn.addEventListener("click", () => {
divs[i].classList.toggle("selected");
if (i < 2) {
divs[i + 1].classList.toggle("selected")
i++;
return;
};
if (i >= 2) {
i = 0;
divs[i].classList.toggle("selected");
}
});
/* Can Change */
.parent {
width: 300px;
height: 300px;
position: relative;
}
/* Can Change */
.grayOverlay {
position: absolute;
width: 100%;
height: 100%;
background-color: rgb(107 114 128 / 0.5);
/* Removed z-index */
}
/* CANNOT CHANGE */
.subParent1 {
position: absolute;
display: flex;
flex-direction: column;
width: 100%;
/* Removed z-index */
}
/* Can Change */
.child {
color: black;
width: 50px;
height: 20px;
background-color: pink;
margin: 10px;
/* 👇 Add position */
position: relative;
}
/* Can Change */
.selected {
background-color: red;
/* 👇 Add z-index */
z-index: 100;
}
button {
margin-bottom: 1em;
padding: 6px;
}
<button>Toggle</button>
<div class="parent">
<div class="subParent1">
<div class="subParent2">
<div class="child selected">child</div>
<div class="child">child</div>
<div class="child">child</div>
</div>
</div>
<!-- This component is underneat subParent in the tree structure. I cannot move this into subParent1 -->
<div class="grayOverlay"></div>
</div>
Related
I want the fixed progress bar have same width as the yellow container and always positioned at the bottom of screen which overlapping on top of yellow container with CSS.
I have try to using width: inherit in the fixed child but the yellow parent do not have width so it doesn't work.
.container {
display: flex;
width: 500px;
height: 1200px;
}
.left {
flex: 1;
background: blue;
}
.right {
flex: 1;
background: yellow;
}
.progress {
position: fixed;
background: gray;
bottom: 0;
}
<div class="container">
<div class="left">
</div>
<div class="right">
<div class="progress">I'm progress bar</div>
</div>
</div>
Position fixed will be fixed to the page's scroll position and should not be used as a child of a static element.
Instead, you should set position to absolute on the progress bar, and position relative on the container.
.container {
position: relative;
}
.child {
position: absolute;
width: 100%;
left: 0;
}
.container {
position: relative;
display: flex;
width: 500px;
height: 100px;
}
.left {
flex: 1;
background: blue;
}
.right {
flex: 1;
background: yellow;
}
.progress {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background: gray;
}
<div class="container">
<div class="left">
</div>
<div class="right">
<div class="progress">I'm progress bar</div>
</div>
</div>
Alternatively, you can use JavaScript to match two elements width's while keeping position fixed using offsetWidth.
function resizeProgress() {
const container = document.querySelector('.container');
const progress = document.querySelector('.progress');
progress.style.width = container.offsetWidth + 'px';
progress.style.left = container.offsetLeft + 'px';
}
function resizeProgress() {
const container = document.querySelector('.container');
const progress = document.querySelector('.progress');
progress.style.width = container.offsetWidth + 'px';
progress.style.left = container.offsetLeft + 'px';
}
resizeProgress();
.container {
position: relative;
display: flex;
width: 500px;
height: 1200px;
}
.left {
flex: 1;
background: blue;
}
.right {
flex: 1;
background: yellow;
}
.progress {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: gray;
}
<div class="container">
<div class="left">
</div>
<div class="right">
</div>
</div>
<div class="progress">I'm progress bar</div>
I'm new in css. I confused in position. There is a simple example.
https://jsfiddle.net/4t8zn9yo/1/
$(document).ready(function() {
$('.content').scroll(function() {
if ($(this).scrollTop() > 0) {
if (!$('.topline').hasClass('long'))
$('.topline').addClass('long')
} else {
if ($('.topline').hasClass('long'))
$('.topline').removeClass('long')
}
});
});
.header {
position: relative;
height: 65px;
background: brown;
overflow-y: hidden;
overflow-x: hidden;
}
.topline {
position: absolute;
top: 0px;
width: 100vw;
height: 5px;
background: blue;
transition: 0.2s;
}
.topline.long {
height: 65px;
transition: 0.2s;
}
.topbar {
display: flex;
justify-content: center;
position: relative;
margin-top: 10px;
}
.topbar div {
flex: 1;
color: white;
}
.content {
height: calc(100vh - 65px);
overflow-y: auto;
overflow-x: hidden;
}
.content-parta {
height: 800px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class='header'>
<div class='topline'>
</div>
<div class='topbar'>
<div>test1</div>
<div>test2</div>
<div>test3</div>
<div>test4</div>
</div>
</div>
<div class='content'>
<div class='content-parta'>
</div>
</div>
When scroll event trigger, topline class is higher than topbar class. I have no idea why the son of topbar class is higher than topline class.
When I move the topline class after the topbar class, it is the highest.
I want to know why.
If you refer to the specification and the painting order:
All positioned, opacity or transform descendants, in tree order that fall into the following categories:
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, but any positioned descendants and descendants which actually create a new stacking context should be considered part of the parent stacking context, not this new one.
Both elements are positionned with the same properties (z-index:auto is the important one) so the tree order will decide which one will be on the top. In your actual code topbar is on the top. If you switch the HTML order you will have it on the bottom:
$(document).ready(function() {
$('.content').scroll(function() {
if ($(this).scrollTop() > 0) {
if (!$('.topline').hasClass('long'))
$('.topline').addClass('long')
} else {
if ($('.topline').hasClass('long'))
$('.topline').removeClass('long')
}
});
});
.header {
position: relative;
height: 65px;
background: brown;
overflow-y: hidden;
overflow-x: hidden;
}
.topline {
position: absolute;
top: 0px;
width: 100vw;
height: 5px;
background: blue;
transition: 0.2s;
}
.topline.long {
height: 65px;
transition: 0.2s;
}
.topbar {
display: flex;
justify-content: center;
position: relative;
margin-top: 10px;
background:red;
}
.topbar div {
flex: 1;
color: white;
}
.content {
height: calc(100vh - 65px);
overflow-y: auto;
overflow-x: hidden;
}
.content-parta {
height: 800px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class='header'>
<div class='topline'>
</div>
<div class='topbar'>
<div>test1</div>
<div>test2</div>
<div>test3</div>
<div>test4</div>
</div>
</div>
<div class='content'>
<div class='content-parta'>
</div>
</div>
If you remove the position from one element, whataver the tree order the other positionned element will always be on the top.
$(document).ready(function() {
$('.content').scroll(function() {
if ($(this).scrollTop() > 0) {
if (!$('.topline').hasClass('long'))
$('.topline').addClass('long')
} else {
if ($('.topline').hasClass('long'))
$('.topline').removeClass('long')
}
});
});
.header {
position: relative;
height: 65px;
background: brown;
overflow-y: hidden;
overflow-x: hidden;
}
.topline {
position: absolute;
top: 0px;
width: 100vw;
height: 5px;
background: blue;
transition: 0.2s;
}
.topline.long {
height: 65px;
transition: 0.2s;
}
.topbar {
display: flex;
justify-content: center;
margin-top: 10px;
background:red;
}
.topbar div {
flex: 1;
color: white;
}
.content {
height: calc(100vh - 65px);
overflow-y: auto;
overflow-x: hidden;
}
.content-parta {
height: 800px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class='header'>
<div class='topline'>
</div>
<div class='topbar'>
<div>test1</div>
<div>test2</div>
<div>test3</div>
<div>test4</div>
</div>
</div>
<div class='content'>
<div class='content-parta'>
</div>
</div>
Related:
Why can't an element with a z-index value cover its child?
Why does position:relative; appear to change the z-index?
HTML thinks that it is now behind. Think of it as a 3 dimensional environment. You can use z-index. This way you can control how the appearance works. You can check this out.
I have a parent/wrapper container where the heigth is dependent on the width, so it uses the useful aspect-ratio trick often used for iframes and videos; that is, setting i.e. height: 0, padding-top: 53.3% for 16:9.
My problem is now that I have a child inside that wrapper which needs to have height: 100%. But it doesn't take the padding into account, resulting in a heightless child with overflow.
This code snippet illustrates my problem:
let
box = document.querySelector('.aspect-ratio')
;
document.querySelector('[name="toggle"]').onclick = function(){
let
padding_trick = 'height-0'
;
if (box.classList.contains(padding_trick)) {
box.classList.remove(padding_trick);
}Â else {
box.classList.add(padding_trick);
}
};
* {
box-sizing: border-box;
}
.controller {
margin-bottom: 15px;
}
.wrapper {
position: relative;
width: 400px;
}
.aspect-ratio {
background-color: beige;
}
.aspect-ratio.height-0 {
height: 0;
padding-top: 53.3%;
}
.inner {
background-color: tomato;
height: 100%;
border: 1px solid #222;
}
<div class="root">
<div class="controller">
<button name="toggle">Toggle padding-trick</button>
</div>
<div class="wrapper">
<div class="aspect-ratio height-0">
<div class="inner">
<p>Bordered content height is not 1oo%.</p>
</div>
</div>
</div>
</div>
What I would want is that the text appears inside the box, and that the red background-color fills the entire height of the box.
What I would want is that the text appears inside the box, and that the red background-color fills the entire height of the box.
That sound like you want the inner box absolutely positioned over the padding-trick box. If that's the case, this should be possible by just adding in the position attributes.
let
box = document.querySelector('.aspect-ratio')
;
document.querySelector('[name="toggle"]').onclick = function(){
let
padding_trick = 'height-0'
;
if (box.classList.contains(padding_trick)) {
box.classList.remove(padding_trick);
}Â else {
box.classList.add(padding_trick);
}
};
* {
box-sizing: border-box;
}
.controller {
margin-bottom: 15px;
}
.wrapper {
position: relative;
width: 400px;
}
.aspect-ratio {
background-color: beige;
}
.aspect-ratio.height-0 {
height: 0;
padding-top: 53.3%;
position: relative;
}
.inner {
background-color: tomato;
height: 100%;
border: 1px solid #222;
}
.height-0 .inner {
position: absolute;
top: 0;
}
<div class="root">
<div class="controller">
<button name="toggle">Toggle padding-trick</button>
</div>
<div class="wrapper">
<div class="aspect-ratio height-0">
<div class="inner">
<p>Bordered content height is not 1oo%.</p>
</div>
</div>
</div>
</div>
Is there a cleaner/shorter way to achieve the same result with cross-browser CSS and using javascript only to toggle classes?
I'm trying to create a layout like the above but I've never tried to set everything absolutely as I did here so I don't know what might be the disadvantage.
$(".left-col-content > .content").append(() => {
return "left-col<br>".repeat(350);
});
$(".right-col-content > .content").append(() => {
return "right-col<br>".repeat(350);
});
$(".terminal-row > .content").append(() => {
return "terminal<br>".repeat(350);
});
$(".body-col-content > .content").append(() => {
return "content<br>".repeat(350);
});
$(".body-col-header > .content").append(() => {
left_btn = $("<button>Toggle Left</button>").click(() => {
$(".left-col").toggle();
$(".body-col").toggleClass("left-hidden");
});
right_btn = $("<button>Toggle Right</button>").click(() => {
$(".right-col").toggle();
$(".body-col").toggleClass("right-hidden");
});
terminal_btn = $("<button>Toggle Terminal</button>").click(() => {
$(".terminal-row").toggle();
$(".main-row").toggleClass("terminal-hidden");
});
buttons = $("<div></div>")
.append(left_btn)
.append(right_btn)
.append(terminal_btn);
return buttons;
});
.container {
height: 100%;
}
.container .main-row {
height: inherit;
position: absolute;
top: 0px;
right: 0px;
bottom: 50px;
left: 0px;
}
.container .main-row.terminal-hidden {
bottom: 0px;
}
.container .main-row .left-col {
width: 150px;
left: 0px;
top: 0px;
bottom: inherit;
position: absolute;
background-color: yellow;
}
.container .main-row .left-col .left-col-header {
height: 50px;
width: inherit;
position: inherit;
background-color: orange;
}
.container .main-row .left-col .left-col-content {
width: inherit;
top: 50px;
bottom: 0px;
position: inherit;
overflow-y: scroll;
}
.container .main-row .left-col .left-col-content .content {
padding: 15px;
}
.container .main-row .right-col {
width: 150px;
right: 0px;
top: 0px;
bottom: inherit;
position: absolute;
background-color: yellow;
}
.container .main-row .right-col .right-col-header {
height: 50px;
width: inherit;
position: inherit;
background-color: orange;
}
.container .main-row .right-col .right-col-content {
width: inherit;
top: 50px;
bottom: 0px;
position: inherit;
overflow-y: scroll;
}
.container .main-row .right-col .right-col-content .content {
padding: 15px;
}
.container .main-row .body-col {
right: 150px;
left: 150px;
top: 0px;
bottom: inherit;
position: absolute;
background-color: green;
}
.container .main-row .body-col.right-hidden {
right: 0px;
}
.container .main-row .body-col.left-hidden {
left: 0px;
}
.container .main-row .body-col .body-col-content {
width: 100%;
top: 50px;
bottom: 0px;
position: inherit;
overflow-y: scroll;
}
.container .main-row .body-col .body-col-content .content {
padding: 25px;
}
.container .main-row .body-col .body-col-header {
height: 50px;
width: 100%;
position: inherit;
background-color: red;
}
.container .terminal-row {
height: 50px;
width: 100%;
bottom: 0px;
right: 0px;
position: absolute;
background-color: black;
color: green;
overflow-y: scroll;
}
.container .terminal-row .content {
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="container">
<div class="main-row">
<div class="left-col">
<div class="left-col-header">
<div class="content">
</div>
</div>
<div class="left-col-content">
<div class="content">
</div>
</div>
</div>
<div class="right-col">
<div class="right-col-header">
<div class="content">
</div>
</div>
<div class="right-col-content">
<div class="content">
</div>
</div>
</div>
<div class="body-col">
<div class="body-col-header">
<div class="content">
</div>
</div>
<div class="body-col-content">
<div class="content">
</div>
</div>
</div>
</div>
<div class="terminal-row">
<div class="content">
</div>
</div>
</div>
This is not fully finished yet, but will give you a frame you can use to work with.
As you can see in the snippet, I have used very rarely used properties of the <style> tag namely id and in javascript .disabled.
As style tags use and comply to W3 'Global HTML Attributes' rules w3schools.com: HTML style tag you can simply give them an #id and toggle them en/disabled with a simple line of javascript.
As you can see in the Snippet I have defined all major elements as Flexbox Containers with some very generic top/bottom/center/middle classes to get the structure you want/need, leaving you with all the room you might need to specific stuff like spacing, fontsizing create you own #id's, etc.
Comment back when you need more info. (don't forget to close the question if you did get the answer you needed.)
In the code you can see that I first disabled the columns and drawer with display: none, each in its own style block.
Then I define them visible with display: flex (as they are flexbox div's) and enable/disable that style block on request by toggling it enabled/disabled with simple javascript.
function toggleStyle(id) { var el = document.getElementById(id); el.disabled = !el.disabled }
<style>
body { margin: 0 }
header,footer,main,section,item,
div { display: flex }
header,footer,section,div { flex-direction: row }
main,item { flex-direction: column }
header,footer,item,div { flex-wrap: wrap }
header { min-height: 3.5rem } /* 3.5 * 16px */
footer { min-height: 2.5rem }
main { height: 100vh; justify-content: space-between }
section { height: 100%; justify-content: space-between }
.drawer { align-content: space-between }
.left,.right { width: 12.5% }
.center { flex: 1 }
.middle { flex: 1; width: 100% }
.top,.bottom { height: 2rem; width: 100% }
.center-col { flex: 1 }
* { outline: 1px dashed }
</style>
<style>.drawer.left { display: none }</style>
<style>.drawer.bottom { display: none }</style>
<style>.drawer.right { display: none }</style>
<style id="stl-left-col" >.drawer.left { display: flex }</style>
<style id="stl-terminal" >.drawer.bottom { display: flex }</style>
<style id="stl-right-col">.drawer.right { display: flex }</style>
<body>
<main>
<header>
<input type="button" onclick="toggleStyle('stl-left-col')" value="left column">
<input type="button" onclick="toggleStyle('stl-terminal')" value="terminal row">
<input type="button" onclick="toggleStyle('stl-right-col')" value="right column">
</header>
<section class="content">
<item class="drawer left">
<div class="top" >left top </div>
<div class="middle">left middle</div>
<div class="bottom">left bottom</div>
</item>
<item class="content center">
<div class="top">main top</div>
<div class="middle">main middle
</div>
<div class="bottom">main bottom</div>
</item>
<item class="drawer right">
<div class="top" >right top </div>
<div class="middle">right middle</div>
<div class="bottom">right bottom</div>
</item>
</section>
<footer class="drawer bottom">some footer</footer>
</main>
</body>
When I try to use "perspective" property and then "transform: rotateY(10deg)" the effect applies to the children elements too. I want a rectangle with perspective but don't want the text inside to be in perspective. Any idea?
I've created a JSFIDDLE example
https://jsfiddle.net/j0ofgbLo/1/
.container {
perspective: 500px;
}
.content{
transform: rotateY(-45deg);
background: #ddd;
min-width: 100px;
padding: 0 20px;
}
<div class="container">
<div class="content">
<h3>Text</h3>
</div>
</div>
The CSS perspective-property just applies perspective to the children not itself. To get an "unflattened"/perspective result, just put this rule on your content-element:
transform-style: preserve-3d;
And put this on your H3-element:
transform: rotateY(45deg);
If the content-element is transformed or has perspective it builds a containing-block and a stacking-context, so the chidren are forced to render to this layer anyway. That means you cannot use fixed elements in an transformed context (in IE11+ you can) to skip the ancestors transformations. So you have to reverse the transformations on the target element (H3) in perspective to get an undistorted result.
.container
{
perspective: 500px;
}
.content
{
transform: rotateY(-45deg);
background: #ddd;
min-width: 100px;
padding: 0 20px;
transform-style: preserve-3d;
}
h3
{
transform: rotateY(45deg);
}
<div class="container">
<div class="content">
<h3>Text</h3>
</div>
</div>
Finally, I solved this. Here is the codepen and the code if someone want to make something similar:
http://codepen.io/marinagallardo/pen/jPjBMV
.heading {
background: red;
display: inline-block;
margin: 20px;
border-radius: 3px;
color: white;
position: relative;
height: 40px;
transform: skewX(-1deg);
}
.heading p {
padding: 0 20px;
margin: 0;
position: relative;
z-index: 100;
margin-top: 10px;
transform: skewX(1deg);
}
.heading:before {
content: "";
display: block;
height: 50%;
top: -10px;
width: 100%;
background: red;
transform: skewY(-2deg);
position: absolute;
border-radius: 3px;
}
.heading:after {
content: "";
display: block;
height: 50%;
background: red;
transform: skewY(2deg);
position: absolute;
bottom: -10px;
width: 100%;
border-radius: 3px;
}
<div class="heading">
<p>Texto</p>
</div>
<div class="heading">
<p>Texto a top de largo</p>
</div>
<div class="heading">
<p>Prácticamente escribiendo el quijote</p>
</div>
<div class="heading">
<p>Prácticamente escribiendo el quijote, lo cual es una prueba</p>
</div>
You need to put out the container and wrap container and content with a div. I create a fiddle working, the first block is like you defined (container and content inherit perspective), and the second block is like I tell you with a wrapper (perspective only for container, not for content).
http://jsfiddle.net/c72b5184/1/
The code is like this (note that the correct version is .wrapper *)
.container {
background-color: blue;
width: 200px;
transform: rotateY(50deg);
height: 200px;
}
.content {
background-color: green;
width: 50px;
height: 50px;
}
.wrapper {
position: relative;
margin: 20px 0;
}
.wrapper .container,
.wrapper .content{
position: absolute;
top: 0;
left:0;
}
HTML
<div class="container">
<div class="content"></div>
</div>
<div class="wrapper">
<div class="container"></div>
<div class="content"></div>
</div>