z-index & width issue with nested absolute, fixed and relative elements - html

I am building a complex webapp using React and I'm trying to figure out the smartest way to handle a complex positioning and layering situation.
I'm wondering if I need to rethink my entire structure or just tweak some css values.
The code below explains my issues:
aside {
position: absolute;
top: 0;
left: 0;
width: 20%;
height: 100vh;
background: blue;
color: white;
}
main {
position: absolute;
overflow: hidden;
background-image: url('https://i.ytimg.com/vi/6sta6Gkpgcw/maxresdefault.jpg');
background-size: cover;
top: 0;
left: 20%;
width: 80%;
height: 100vh
}
.page-wrap {
position: relative;
width: 100%;
height: 100vh;
overflow: auto;
}
.overlay {
position: fixed;
z-index: 1;
padding: 40px;
color: white;
font-size: 24px;
width: calc(80% - 80px);
height: 100vh;
background-color: rgba(0, 0, 0, .5);
}
ul {
position: relative;
z-index: 2;
list-style: none;
color: white;
font-size: 24px;
line-height: 2em;
padding-top: 100vh;
}
ul li {
display: inline-block;
width: 200px;
height: 200px;
text-align: center;
padding: 20px;
background: #ccc;
color: red;
margin: 50px;
}
button {
padding: 10px 30px;
background: green;
color: white;
font-size: 24px;
}
<aside>
SIDEBAR
</aside>
<main>
<section class="page-wrap">
<div class="overlay">
<button>
This needs to be clickable even though the list is on top
</button>
<div>
<h1>My issues here are:</h1>
<ol>
<li>the green button is covered (disabled) by the list</li>
<li>the overlay covers the scrollbar on its parent page-wrap section</li>
</ol>
</div>
</div>
<ul>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>6</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>vew</li>
<li>Item</li>
</ul>
</section>
</main>
FIDDLE

My solution was to restructure the DOM so the body had the overflow. I had been trying to avoid this because it's a large webapp with a lot of components and pages, many of which suffered collateral damage, but ultimately, it solved my problem.

Related

Detect if a list has overflown and give it a class, in css

I would like to do this in CSS if this is possible with no javascript.
I have a list with x amount of items, it could anything. Here is a picture
The container is a % of the screen, so as i resize, the ul gets smaller, therefore the list items go on a new line.
What I would like to do is give the ul an text alignment of center if there is 1 line and text align of left if there is 2.
Is this possible with css only?
I cant do media queries as the number of items could be anything.
Are you looking for something like this?
body {
background: #11b5c3;
font-family: sans-serif;
padding: 50px;
}
h1 {
color: #fff;
text-align: center;
font-weight: 100;
}
/*
.container {
border-top: 1px solid #fff;
padding: 10px 0;
margin: 0 auto;
list-style: none;
display: flex;
justify-content: center;
flex-wrap: wrap;
width:75%;
}
.container li {
background: #fefefe;
margin: 3px;
padding: 5px 10px;
border-radius: 3px;
font-size: 14px;
color: #999;
}*/
.container {
border-top: 1px solid #fff;
padding: 10px 0;
margin: 0 auto;
list-style: none;
/* display:flex; */
/*justify-content:center;*/
flex-wrap: wrap;
max-width: 500px;
text-align: justify;
}
.container li {
background: #fefefe;
;
margin: 3px 0;
padding: 5px 10px;
border-radius: 3px;
font-size: 14px;
color: #999;
display: inline-block;
font-size: 16px;
}
<h1>Tools</h1>
<ul class="container">
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
I am piggy-backing off #Mobarak Ali but I think in this case you want to use grid and not flexbox. If you use flexbox with flexwrap: wrap; and justify-content: center; the items will always be centered. But the OP wants the items to be centered if they don't wrap but then justify on the left side on each subsequent row. Because grid is used for 2d layouts it can achieve this pretty easily.
Add or delete list items to see the effect. Here is a codepen if you prefer that.
https://codepen.io/zenRyoku/pen/rgWBEp
body {
background: teal;
font-family: sans-serif;
padding: 50px;
}
h1 {
color: #fff;
text-align: center;
font-weight: 100;
}
.container {
border-top: 1px solid #fff;
padding: 10px 0;
margin: 0 auto;
list-style: none;
display: grid;
justify-items: center;
grid-template-columns: repeat( auto-fit, minmax(50px, 1fr) );
grid-gap: 1rem;
max-width: 500px;
}
.container li {
width: 50px;
background: #fefefe;
padding: 5px 10px;
border-radius: 3px;
font-size: 14px;
color: #999;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>title</title>
</head>
<body>
<h1>Tools</h1>
<ul class="container">
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
</body>
</html>

Style the Child and not Grandchild?

I am trying to make a 'fancy' navigation, with multiple ul/li. I think my problem is in the CSS with the child selector (>). If you run the code snippet provide you'll notice when you hover over li.first (item one) you can visibly see the div.hover-container although li:first>ul>li has the css with overflow: hidden; along with this div.hover-container is within li.one (example one) so why does it display when I hover over li.>one (other examples).
I am just confused and would love some input on this after staring and testing possible solutions for an hour.
EDIT: I apologize for how messy it looks in the snippet.
/* ================== Example ================ */
.example>ul {
display: flex;
position: relative;
height: 50px;
width: 800px;
margin: 50px auto;
list-style-type: none;
background-color: #5A827E;
}
.example>ul>li {
display: block;
height: 100%;
width: 33.3%;
border-right: 2px solid rgba(0, 0, 0, 0.5);
position: relative;
overflow: hidden;
color: white;
font-weight: bold;
text-align: center;
line-height: 50px;
font-style: italic;
}
.example>ul>li:hover {
overflow: visible;
background-color: #415E5B;
}
.first>ul {
display: block;
position: absolute;
top: 50px;
height: 400px;
width: 100.5%;
list-style-type: none;
background-color: #415E5B;
}
.first>ul>li {
display: block;
color: white;
line-height: 40px;
text-align: center;
height: 10%;
width: 100%;
border-top: 1px dotted rgba(255, 255, 255, 0.5);
font-weight: 400;
font-style: normal;
overflow: hidden;
}
.first>ul>li:hover {
background-color: white;
color: blue;
}
.hover-container {
display: flex;
justify-content: flex-start;
flex-flow: column wrap;
position: absolute;
left: 265px;
top: 0;
height: 400px;
width: 535px;
border: 3px solid gold;
}
.list-container {
display: block;
height: auto;
width: 30%;
border: 3px solid #F2D7E2;
}
.list-container h2 {
text-align: left;
color: darkgray;
font-weight: bold;
}
.list-container ul {
list-style-type: none;
text-align: left;
}
/* ------------------ Example END ---------------- */
<div class="example">
<ul>
<!-- first navigation container -->
<li class="first"> Item One
<ul>
<!-- dropdown -->
<li class="one"> Example 1
<!-- dropdown item -->
<div class="hover-container">
<!-- sidebar -->
<div class="list-container">
<!-- sidebar content box-->
<h2>Content Header One</h2>
<ul>
<li>Content One</li>
<li>Content Two</li>
<li>Content Three</li>
</ul>
</div>
<!-- sidebar content box END -->
<div class="list-container">
<!-- sidebar content box-->
<h2>Content Header One</h2>
<ul>
<li>Content One</li>
<li>Content Two</li>
<li>Content Three</li>
</ul>
</div>
<!-- sidebar content box END -->
</div>
<!-- sidebar END-->
</li>
<!-- dropdown item END -->
<li>Example 2</li>
<li>Example 3</li>
<li>Example 4</li>
<li>Example 5</li>
<li>Example 6</li>
<li>Example 7</li>
<li>Example 8</li>
<li>Example 9</li>
<li>Example 10</li>
</ul>
<!-- END of dropdown -->
</li>
<!-- main list item END -->
<li class="second">Item Two</li>
<li class="third">Item Three</li>
</ul>
</div>
If you want the hover-container div to be shown only when the first li(Example 1) is hovered, you need to replace the .hover-container you have added with the below CSS:
.first>ul>li:first-child:hover .hover-container{
display: flex;
justify-content: flex-start;
flex-flow: column wrap;
position: absolute;
left: 265px;
top: 0;
height: 400px;
width: 535px;
border: 3px solid gold;
}
Your child selectors seem okay. I think the problem is with your positioning. The .hover-container element has "position: absolute", meaning it's placed relative to its first positioned ancestor (see https://www.w3schools.com/css/css_positioning.asp). You want to determine if it's overflowing the .first>ul>li element above it, so that element needs a "position: relative."
Adding these rules should give the behavior you're looking for:
.first>ul>li {
position: relative;
...
}
.first>ul>li:hover {
overflow: visible;
...
}
The following CSS property rule defined on .hover-container set its position to absolute with respect to .example>ul>li which is the nearest ancestor that is a containing block (read about how the browser determines a containing block.)
.hover-container {
/*...*/
position: absolute;
/*...*/
}
The fix for this is to form a containing block with the li that is closest ancestor to .hover-container by setting its position to relative.
.first>ul>li {
/*...*/
position: relative;
/*...*/
}
Another fix for this is to set the overflow property to hidden for the closest containing block that is an ancestor to .hover-container.
.first>ul {
/*...*/
overflow: hidden;
/*...*/
}

First `li` element in second `ul` column has no margin?

I'm trying to add a top margin to every li element in a ul element that has the css property column-count: 2 set. For some reason, the margin works for every li element except the first element in the second column:
ul {
column-count: 2;
padding: 0;
border: 1px solid black;
}
li {
margin-top: 10px;
}
li:nth-child(1) {
background-color: red;
}
li:nth-child(5) {
background-color: blue;
}
<ul>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
What about just using margin-bottom instead? Also apply 10px padding to your ul in order to achieve the same.
ul {
column-count: 2;
padding: 10px 0 0 0;
border: 1px solid black;
}
li {
margin-bottom: 10px;
}
li:nth-child(1) {
background-color: red;
}
li:nth-child(5) {
background-color: blue;
}
<ul>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
You could set the display property of the list items to inline-block and the width to 100% to remedy this
ul {
column-count: 2;
padding: 0;
border: 1px solid black;
}
li {
margin-top: 10px;
display: inline-block;
width: 100%;
}
li:nth-child(1) {
background-color: red;
}
li:nth-child(5) {
background-color: blue;
}
<ul>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
The suggestion #Troels gave is a good option. You can look into padding as an option too:
li {
padding-bottom: 10px;
}

Clicking on buttons through a container mask

I have a container with some clickable items, and at the bottom of the container, there is a gradient that I'm adding with .container:after to create a fade effect. I would still like the items at the bottom to be clickable, but also to be affected by the fade, so putting the fade behind the clickable items won't work.
http://jsfiddle.net/mwjj7hff/1/
HTML:
<div class="container">
<ul>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
</div>
CSS:
.container {
border: 1px solid #ccc;
font-family: sans-serif;
height: 400px;
overflow: hidden;
position: relative;
width: 200px;
}
.container:after {
background: linear-gradient(to bottom, rgba(255, 255, 255, .3) 0%, white 100%);
bottom: 0;
content: " ";
height: 150px;
display: block;
position: absolute;
width: 100%;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
li {
cursor: pointer;
margin: 3px;
padding: 3px;
text-align: center;
}
li:hover {
background: #99d;
}
A quick, easy solution would be to add pointer-events: none to the pseudo-element.
In doing so, you can essentially click through it:
MDN - pointer-events: none
The element is never the target of mouse events; however, mouse events may target its descendant elements if those descendants have pointer-events set to some other value. In these circumstances, mouse events will trigger event listeners on this parent element as appropriate on their way to/from the descendant during the event capture/bubble phases.
Updated Example
.container:after {
background: linear-gradient(to bottom, rgba(255, 255, 255, .3) 0%, white 100%);
bottom: 0;
content: " ";
height: 150px;
display: block;
position: absolute;
width: 100%;
pointer-events: none;
}
Browser support for pointer-events can be found here - currently 87.29%.

Overflow issue in a fixed position sidebar

I have a fixed sidebar, with overflow-x:hidden so I get a scrollbar to scroll. But now I want to add a submenu, that when shown will overflow into the main window.
This works fine if I set overflow:visible but then I lose the scrolling ability.
Can I get them both together?
http://codepen.io/anon/pen/OPzvdP
#sidebar-wrapper {
width: 200px;
background-color: #396DA5;
position: fixed;
height: 100%;
overflow-x: hidden;
}
#menu ul ul {
display: none;
list-style: none;
}
#menu ul ul {
position: relative;
}
#menu ul li:hover > ul {
display: block;
}
#menu ul ul {
padding: 50px;
position: absolute;
left: 80%;
top: 0;
background: #f00;
}
<div id="sidebar-wrapper">
<div id=menu>
<ul>
<li>Item Hover
<ul>
<li>subitem<li>
</ul>
</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</div>
</div>
The top li item has a hover, that can be fully see when the overflow-x:hidden is removed from the top css line, but then the side bar cant be scrolled!
EDIT - Answers have said use position:fixed and this works. But can this be applied to any of the list items so the submenu opens beside the parent?
No need to add overflow-y.
Your class should be
#menu ul li:hover > ul {
display: block;
position: fixed;
top: 8px;
left: 150px;
}
DEMO
Try
overflow-y: scroll
and set your hover box to position fixed:
#menu ul ul { padding:50px; position: fixed; left: 200px; top:0; background:#f00;}
You will need to adjust the position.
Not a very pretty solution though.