Centering collapsible but have content go down - css

I have a menu with a dropdown that I've centered in the page. As I've positioned it absoutely, when the dropdown is opened, the menu moves upwards (to account for the increased height due to the menu being open).
I cannot quite work out what a better way around this is? The ideal behaviour is that before the dropdown is open, the menu is perfectly centered, and then when a dropdown is opened, the top of the menu stays in place.
I'm looking to see if there is a CSS only method of maintaining the positioning. Otherwise, I'll implement some JS to position the menu on load.
var dropdown = document.getElementById("dropdown");
var show = false;
function showDropdown() {
var dropdownList = document.getElementById("dropdownList");
if (show) {
dropdownList.classList.remove("show");
show = false;
} else {
dropdownList.classList.add("show");
show = true;
}
}
dropdown.addEventListener("click", showDropdown);
.parent {
height: 100vh;
width: 100vw;
background-color: #aaa;
}
.list {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.dropdown > ul {
display: none;
}
.dropdown > ul.show {
display: block;
}
<div class="parent">
<ul class="list">
<li class="dropdown">Item One
<ul id="dropdownList">
<li>Dropdown 1</li>
<li>Dropdown 2</li>
</ul>
</li>
<li>Item Two</li>
<li>Item Three</li>
</ul>
</div>

Well the problem is you have your content aligned vertically using translate and top. I've changed this to be margin-top, but its not going to be perfect.
The automatic vertical centering of your previous method will sadly result in your content being pushed upward when it gets talked; as that is how vertical alignment works.
You basically have to manually determine where on the page it should be aligned with.
var dropdown = document.getElementById("dropdown");
var show = false;
function showDropdown() {
var dropdownList = document.getElementById("dropdownList");
if (show) {
dropdownList.classList.remove("show");
show = false;
} else {
dropdownList.classList.add("show");
show = true;
}
}
dropdown.addEventListener("click", showDropdown);
.parent {
height: 100vh;
width: 100vw;
background-color: #aaa;
}
.list {
position: absolute;
margin-top: 40vh;
}
.dropdown > ul {
display: none;
}
.dropdown > ul.show {
display: block;
}
<div class="parent">
<ul class="list">
<li class="dropdown">Item One
<ul id="dropdownList">
<li>Dropdown 1</li>
<li>Dropdown 2</li>
</ul>
</li>
<li>Item Two</li>
<li>Item Three</li>
</ul>
</div>https://stackoverflow.com/posts/66536030/edit#

Related

Select target="_blank" when img is not a child

I have the following styling setup to apply an icon to external links, but I'm having an issue where it's applying to images that are a child of the anchor tag, which I do not want.
a:not(.btn) {
&[target="_blank"] {
&:after {
// The styles I want to apply
}
}
}
I've attempted to use the following img:not(:first-child) selector to wrap the block, but it does not seem to do what I want it to.
a:not(.btn) {
&[target="_blank"] + img:not(:first-child) {
&:after {
// The styles I want to apply
}
}
}
What is the correct way to adjust this code where it will correctly apply to any anchor tags with target="_blank" but not to those when an img tag is a child.
If you are happy to add a dash of javascript, you can achieve the effect you're after relatively straightforwardly by:
adding a class to all anchor elements with target="_blank"
removing the same class (the one you just added) from all parent nodes of image elements
Working Example:
// GET ALL THE ANCHORS WITH target="_blank"
const anchorTargetBlanks = [...document.querySelectorAll('[target="_blank"]')];
// ADD A CLASS TO ALL SUCH ANCHORS
anchorTargetBlanks.forEach(anchor => anchor.classList.add('link-contains-no-image'));
// REMOVE THAT CLASS FROM THE PARENT NODE OF EACH IMAGE
[...document.images].forEach((image) => {
if (image.parentNode.getAttribute('target') === '_blank') {
image.parentNode.classList.remove('link-contains-no-image');
}
});
ul {
padding-left: 0;
}
li {
margin: 1px;
}
img {
display: inline-block;
width: 30px;
height: 30px;
margin-right: 6px;
background-color: rgb(0, 0, 191);
vertical-align: middle;
}
.link-contains-no-image {
display: block;
padding: 6px;
color: rgb(255, 255, 255);
background-color: rgb(255, 0, 0);
}
<ul>
<li>Link 1</li>
<li><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'/%3E" alt="Image for Link 2" />Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
<li><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'/%3E" alt="Image for Link 5" />Link 5</li>
<li>Link 6</li>
</ul>
What you are trying to do is target a parent based on its child, and that is not possible with CSS. You need to use JS or JQuery. Check these answers for more detailed suggestions.
Set parent style via child or
Is there a CSS parent selector?

Dynamic Navigation with responsive design and pure css

I built a few websites with responsive navigation already. There are many solutions for responsive designs of website navigation if you have a static navigation.
However, for my current project I am building an user-individual dynamic navigation, so the number of navigation element as well as their contents are subject to change and are user individual. Therefore I tried different things with flexbox and floating layouts already but did not find a solution meeting my needs.
Do you have an idea how to fix the problem showed in the picture attached?
I also attached some sample code.
<!DOCTYPE html>
<html>
<head>
<style>
* {
font-family: "Avenir";
}
body {
margin: 0;
}
header {
background: #303ca2;
color: white;
margin-bottom: 20px;
display: flex;
}
h1 {
font-size: 24px;
margin: 0;
text-transform: uppercase;
font-weight: 400;
padding: 10px;
line-height: 40px;
flex-shrink: 0;
}
nav {
}
nav ul {
margin: 0;
padding: 0;
text-align: right;
}
nav ul li {
list-style: none;
display: inline-block;
font-size: 16px;
padding: 10px 10px;
line-height: 40px;
}
</style>
</head>
<body>
<header>
<h1>Title Zone</h1>
<nav>
<ul>
<li>Navigation Elem 1</li>
<li>Navigation Element 2</li>
<li>Navigation 3</li>
<li>Element 4</li>
<li>Element 5</li>
<li>Navigation 6</li>
<li>Element n</li>
</ul>
</nav>
</header>
</body>
</html>
you need use media query to set what will appear in each screen size.
In the HTML file you will define all cases including a div tag "more" with yheir li tag
but when the screen size if less than a certain number this div will recive display: none
for example:
<ul>
<li>element 1</li>
<li>element 2</li>
<li class="disabledOnMobile">element 3</li>
<li class="disabledOnMobile">element 4</li>
<div class="activeOnMobile">more...
<ul>
<li>element 3</li>
<li>element 4</li>
</ul>
</div>
</ul>
and with media query you can hide or show something based in the width screen like this:
#media only screen and (max-width: 400px) {
.disabledOnMobile {
display: none;
}
}
hiding all tag with disabledOnMobile class (element 3 and 4 showing their only with "more")
#media only screen and (min-width: 400px) {
.activeOnMobile {
display: none;
}
}
Here we hide the activeOnMobile when the screen is bigger than 400px showing only element 3 and 4 that is outside activeOnMobile class
in your case you need add the hover on "more" and set anothers settings, it's only a example, you can also add a breakpoin in any screen size. To becobe it's easier you can use some library like bootstrap.
You can lear more about media query here

Centering content vertically in list which is displayed as table rows

I am creating vertical page navigation. I know exact height (300px) and that there will be 5 items (all of them may be of different sizes, but none of them will exceed 60px of height).
What I want to achieve is to display all the items centered vertically in their table row, here is the fiddle: https://jsfiddle.net/6sp5n7xg/.
HTML
<div class="wrapper">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5<br />Item 6</li>
</ul>
</div>
CSS
.wrapper {
height: 300px;
background-color: #EEE;
}
ul {
display: table;
}
ul li {
display: table-row;
height: 60px;
}
Horizontally it works fine: https://jsfiddle.net/vq9mt02h/1/, but I want it also to be vertical.
Here a working example that starts from your code, only slightly modified. You must wrap each cell content in a span and set it display:table-cell;, moving to this selector also properties about height and vertical-align (I added also a red border to highlight middle alignement). So, here the modified code:
HTML
<div class="wrapper">
<ul>
<li><span>Item 1</span></li>
<li><span>Item 2</span></li>
<li><span>Item 3</span></li>
<li><span>Item 4</span></li>
<li><span>Item 5<br />Item 6</span></li>
</ul>
</div>
CSS
ul li {
display: table-row;
}
ul li span {
display: table-cell;
height: 60px;
border:solid 1px red;
vertical-align:middle;
}
For the love of everything sacred don't abuse tables for this. Flexbox is your saviour, both for the menu layout itself, and for the perfect bidirectional centering. Consider the following sample:
.wrapper {
background-color: #EEE;
}
ul, li {
margin:0;
padding:0;
}
ul {
display: flex;
flex-direction: column;
}
li {
display: flex;
align-items:center;
justify-content:center;
height: 60px;
width:120px;
border:1px dotted black;
}
<div class="wrapper">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5<br />Item 6</li>
</ul>
</div>
Change flex-direction on the ul to row to have the same layout horizontally.
Only need to forget support for IE9, but since that's 3 major versions gone by now that's generally considered fine for most sites.

Pseudo class :focus doesn't propagate, now what?

Question: How do I get this to work for tabbing, using CSS only? (Tabbing already works).
#menu:before {
content:"Menu \25bc";
font-weight:bold;
width:100%;
}
#menu:hover:before {
content:"Menu \25b2";
}
#menu li {
position:absolute;
left:-9999px;
}
#menu:hover li {
position:relative;
left:0;
}
<html>
<title>Test</title>
<body>
<header>
Link to homepage
</header>
<nav>
<ul id="menu">
<li>Menu item 1</li>
<li>Menu item 2</li>
</ul>
</nav>
<main>
<p>Other text with maybe a link here.</p>
</main>
</body>
</html>
EDIT: Original question follows.
I have a menu:
<ul id="menu">
<li>Menu item 1</li>
<li>Menu item 2</li>
</ul>
However, I want to hide it at a narrow page width, so I apply the following CSS:
#media (max-width: 768px) {
#menu:before {
content:"Menu \25bc";
}
#menu:hover:before {
content:"Menu \25b2";
}
#menu a {
position:absolute;
left:-9999px;
}
#menu:hover a {
position:relative;
left:0px;
}
}
This hides the menu, adds the word "Menu" in it's place, with a down or up arrow, depending on the hover state, which also shows the menu when you hover over it.
The problem is that, while :hover works just fine, I cannot get both to show by tabbing to one of the tags, using the :focus pseudo class. (Alas, :root will not work like other pseudo classes, so something like #menu a:focus:root #menu a { position:relative; left:0; } won't work, as far as I can see).
Does anyone have any ideas as to how I could approach this, using only CSS? Or have I dug myself into a hole?
Based on OP comment below:
I'm happy to change the HTML, but how would :target work here?
here is a snippet with :target
nav {
height: 0;
overflow: hidden;
position: relative;
}
nav:target {
height: auto;
}
nav + div a:before {
content: "Menu \25bc";
font-weight: bold;
width: 100%;
}
nav:target + div a:before {
content: "Menu \25b2";
}
nav:target + div .open,
nav + div .close {
display: none;
}
nav:target + div .close,
nav + div .open {
display: block;
position: absolute;
top: 0
}
<nav id="menu">
<ul>
<li>Menu item 1
</li>
<li>Menu item 2
</li>
</ul>
</nav>
<div>
<a class="open" href="#menu"></a>
<a class="close" href="#"></a>
</div>

Show / Hide toggle text based on :target (No JS)

What I'm trying to create is a toggle that's workable without using JavaScript or jQuery. The main issue that I'm having is with getting the toggle text to switch from Open to Close when :target is applied on the #menu selector. The navigation is collapsed by default and is opened by #menu toggle.
HTML
<nav class="site-nav" id="menu">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</nav>
<section class="site-nav__toggle">
<a class="site-nav__toggle--open" href="#menu">Open</a>
<a class="site-nav__toggle--close" href="">Close</a>
</section>
CSS via SCSS
.site-nav {
border-bottom: 4px solid black;
float: left;
height: 0;
overflow: hidden;
width: 100%;
&:target {
height: 100%;
min-height: 170px;
transition: height .25s ease, min-height .25s ease;
}
}
.site-nav__toggle--open > .site-nav:target,
.site-nav__toggle--close > .site-nav {
display: none;
}
.site-nav__toggle--close > .site-nav:target,
.site-nav__toggle--open > .site-nav {
display: inline;
}
The main problem I have is that the text is not switching on the target states. Maybe a 2nd pair of eyes would help and spot the obvious if I've missed anything in this particular problem.
From W3C:
div > p
Selects all P elements where the parent is a DIV element
div + p
Selects all P elements that are placed immediately after DIV elements
I think your selectors should look like
.site-nav:target + .site-nav__toggle .site-nav__toggle--open,
.site-nav + .site-nav__toggle .site-nav__toggle--close {
display: none;
}
.site-nav:target + .site-nav__toggle .site-nav__toggle--close,
.site-nav + .site-nav__toggle .site-nav__toggle--open {
display: inline;
}
Check this codepen:
http://codepen.io/anon/pen/goAIu
Best,
Marek

Resources