CSS only menu, click to disappear - css

I have a menu structured like this:
<div class="nav">
<div class="drnav">
<ul class="ulMenu">
<li>
<div class="menuHeader">My Home</div>
<div class="menu-content">
<ul>
<li>item1</li>
<li>item3</li>
</ul>
</div>
</li>
<li>
<div class="menuHeader">My Stuff</div>
<div class="menu-content">
<ul>
<li>item4</li>
<li>item6</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
My css is setup so that when you hover over a menuHeader element the menu-content element is displayed (i.e. display: inline). This all works fine but what I want is that when you click one of the links in the list item elements within the menu-content that the menu (i.e. the parent menu-content element) disappears. Of course I want to do this without any JavaScript. I saw one example that used pointer-events but that restricts use to IE 11 and I'd like to support at least IE 10 if not 9 as well. Any suggestions on how to get this to work?

Technically it's possible, but it's much ado about nothing (hard to use it in practice):
.ulMenu .menu-content {
display: none;
}
.ulMenu > li:hover .menu-content {
display: inline-block;
}
.ulMenu > li .menu-content:target {
display: none;
}
<div class="nav">
<div class="drnav">
<ul class="ulMenu">
<li>
<div class="menuHeader">My Home</div>
<div class="menu-content" id="menuContent_1">
<ul>
<li>item1</li>
<li>item3</li>
</ul>
</div>
</li>
<li>
<div class="menuHeader">My Stuff</div>
<div class="menu-content" id="menuContent_2">
<ul>
<li>item4</li>
<li>item5</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
Besides, once you close a menu the only way to reopen it is by opening another and hovering the initial one.
Important note:
I would like to point out having a :hover based menu is a huge disadvantage compared to having a JavaScript based menu. Because more than half of today's traffic is coming from touch devices (and you don't hover much on a touch-device, do you?) while only less than 1% of traffic has JavaScript disabled.
So, could you perhaps explain why you ask for a pure CSS solution? The only practical use for pure CSS I had in past 8 years was for a payment gateway page, where JavaScript was strictly off. But, other than that?
I happen to know my way around CSS, but I was never keen on trying to transfer DOM manipulations to CSS, instead of leaving them for JavaScript. After all, that's what JavaScript is for. Use the right tool for the job. The job here is DOM manipulation. So use JavaScript.
Here's is the input/label solution I described in the comments. I realized they don't have to be checkboxes, I can use the :focus state to hide the menu contents. It's still buggy, in the sense that a click anywhere in the page is needed to make the :hover work again for the recently closed menu. But it's the closest you can get with CSS only or, at least, that's what I think.
.menuHeader input:focus + label,
.menuHeader label {
display: none;
}
.menuHeader:hover label
{
display: inline-block;
}
input.hidden {
position: absolute;
opacity: 0;
pointer-events: none;
}
<ul class="ulMenu">
<li>
<div class="menuHeader">
<div>My Home</div>
<input id="menuContent_1" class="hidden" type="text" />
<label class="menu-content" for="menuContent_1">
<ul>
<li>item1</li>
<li>item2</li>
</ul>
</label>
</div>
</li>
<li>
<div class="menuHeader">
<div>My Stuff</div>
<input id="menuContent_2" class="hidden" type="text" />
<label class="menu-content" for="menuContent_2">
<ul>
<li>item4</li>
<li>item5</li>
</ul>
</label>
</div>
</li>
</ul>

Changed the HTML tags not for semantics but for clarity. I find Nested lists easier to visualize if every other level is represented by <dl>, <dt>, <dd> list elements.
Used hidden radio inputs since it's the easiest way to maintain a 'state' (ex. 'on' and 'off') indefinitely using only CSS.
Targeting elements is done by pairing off groups of <label>s and <input type="radio">s.
Used the visibility property because it's ability to keep children elements of an element with visibility: hidden visible if said child had visibility: visible explicitly set.
I'm not sure what use it really is to remove the parent of a menu and wasn't sure if OP wanted the parent back or not. So the headings aren't really gone when the menu items are clicked, they are just invisible. If you want them back, just click the space above the lists.
SNIPPET
body {
background: #222;
}
li {
text-decoration: none;
display: inline-block;
cursor: pointer;
padding: 3px;
margin-bottom: 12px;
}
.rad {
display: none;
}
.rad + label,
dd {
visibility: hidden;
}
dl:hover dd,
.rad:checked + label {
cursor: pointer;
visibility: visible;
color: #fc2;
}
dd label:hover {
background: #930;
border: .5px solid cyan;
}
<nav class="mainNav">
<div class="drNav">
<ul class="mainMenu">
<li>
<dl class="menuContent">
<input id='rad0' class='rad' name='radA' type='radio' checked>
<label for='rad0'>
<dt class="menuHeader">HOME___</dt>
</label>
<dd>
<label for='rad1'>
<input id='rad1' class='rad' name='radA' type='radio'>Item1
</label>
</dd>
<dd>
<label for='rad2'>
<input id='rad2' class='rad' name='radA' type='radio'>Item2
</label>
</dd>
</dl>
</li>
<li>
<dl class="menuContent">
<input id='rad3' class='rad' name='radB' type='radio' checked>
<label for='rad3'>
<dt class="menuHeader">CONTENT</dt>
</label>
<dd>
<label for='rad4'>
<input id='rad4' class='rad' name='radB' type='radio'>Item3
</label>
</dd>
<dd>
<label for='rad5'>
<input id='rad5' class='rad' name='radB' type='radio'>Item4
</label>
</dd>
</dl>
</li>
</ul>
</div>
</nav>

Related

How to change the style of unrelated element in CSS

I am creating a side navigation bar using CSS only. I was able to trigger dropdown menus using checkbox triggers:
[type="checkbox"] {
display: none;
&:checked ~ div
{
display: block;
}
}
HTML Structure:
<ul>
<li>
<input type="checkbox" id="chk1">
<label for="chk1">Dropdown 1</label>
<div class="dropnav">
.. list of sub menu
</div>
</li>
<li>
<input type="checkbox" id="chk2">
<label for="chk2">Dropdown 1</label>
<div class="dropnav">
.. list of sub menu
</div>
</li>
<li>
<input type="checkbox" id="chk3">
<label for="chk3">Dropdown 1</label>
<div class="dropnav">
.. list of sub menu
</div>
</li>
</ul>
So as you can see when the checkbox is 'checked' the next sibling div element will be display: block to show the dropdown menus, and it's working. However I have multiple dropdown menus and I would like close other dropdown menus when one of them was open. For example if I trigger the 'chk1' it will open its sibling 'dropnav' menu then the other 'dropnav' will be closed (display: none). Is this possible for a CSS only solution?
Use type radio with the same names instead of checkboxes:
[type="radio"] {
display: none;
}
.dropnav {
display: none;
}
[type="radio"]:checked~div {
display: block;
}
<ul>
<li>
<input type="radio" name="c" id="chk1">
<label for="chk1">Dropdown 1</label>
<div class="dropnav">
.. list of sub menu
</div>
</li>
<li>
<input type="radio" name="c" id="chk2">
<label for="chk2">Dropdown 1</label>
<div class="dropnav">
.. list of sub menu
</div>
</li>
<li>
<input type="radio" name="c" id="chk3">
<label for="chk3">Dropdown 1</label>
<div class="dropnav">
.. list of sub menu
</div>
</li>
</ul>

using + with > selector

I am learning responsive menus and by googling, i got the hamburger checkbox hack.
What i am trying to do is show only direct descendants by clicking the hamburger and hide the sub menus.
#toggle-menu {
cursor: pointer;
}
#primary-nav,
#menu-toggle,
#primary-nav>ul {
display: none;
}
#menu-toggle:checked+#primary-nav {
display: block;
}
<link href="https://use.fontawesome.com/releases/v5.7.1/css/all.css" rel="stylesheet" />
<div class="menu">
<a href="#">
<h1>Company</h1>
</a>
<label for="menu-toggle" id="toggle-menu"><i class="far fa-bars"></i></label>
<input type="checkbox" id="menu-toggle">
<ul id="primary-nav">
<li>home</li>
<li>dropdown
<ul>
<li>sub1</li>
<li>sub2</li>
</ul>
</li>
</ul>
</div>
Any help will be appreciated
To only show the ul after the input, you need to hide all uls, then only show the ul directly after a checked input
You can then add a class on all the inputs and do the same for the submenu.
#toggle-menu {
cursor: pointer;
}
.toggler, /*checkboxes a class of toggler and hide */
ul { /* hide all menus (you may want to give them all a class) */
display: none;
}
.toggler:checked+ul { /* only show a ul if it is directly after a checked toggler input */
display: block;
}
<link href="https://use.fontawesome.com/releases/v5.7.1/css/all.css" rel="stylesheet" />
<div class="menu">
<a href="#">
<h1>Company</h1>
</a>
<label for="menu-toggle" id="toggle-menu"><i class="far fa-bars"></i></label>
<input type="checkbox" id="menu-toggle" class="toggler">
<ul id="primary-nav">
<li>home</li>
<li>
<label for="sub-menu1">dropdown</label> <!-- use a label and target the checkbox below -->
<input type="checkbox" class="toggler" id="sub-menu1"> <!-- add this -->
<ul>
<li>sub1</li>
<li>sub2</li>
</ul>
</li>
</ul>
</div>

Why cant i create hovering background for entire <li>?

For some weird reason i cant change the background of the entire list item even though i selected the list.
Here is the code:
<div class="project-item" ng-controller="openProjectsCtrl">
<ol>
<li class="list-item" ng-repeat="project in projects | orderBy:'Posted' : true">
<h4>{{project.Title}}</h4>
{{project.Skills}}
<span class="col-md-6">{{project.Budget}}</span>
<span class="col-md-6 timestamp">{{project.Posted|timeago}}</span>
</li>
</ol>
</div>
<style>
.project-item>ol>li:hover {
background-color: #eee;
}
</style>
The code you posted works fine. See this example.
.project-item>ol>li:hover {
background-color: #eee;
}
<div class="project-item" ng-controller="openProjectsCtrl">
<ol>
<li class="list-item" ng-repeat="project in projects | orderBy:'Posted' : true">
<h4>{{project.Title}}</h4>
{{project.Skills}}
<span class="col-md-6">{{project.Budget}}</span>
<span class="col-md-6 timestamp">{{project.Posted|timeago}}</span>
</li>
</ol>
</div>
This code seems to work.
Change the colors its pretty hard to see a gray color on a white background.
.project-item ol li:hover {
background-color: firebrick;
}
<div class="project-item" ng-controller="openProjectsCtrl">
<ol>
<li class="list-item" ng-repeat="project in projects | orderBy:'Posted' : true">
<h4>{{project.Title}}</h4>
{{project.Skills}}
<span class="col-md-6">{{project.Budget}}</span>
<span class="col-md-6 timestamp">{{project.Posted|timeago}}</span>
</li>
</ol>
</div>
I resolved the issue by removing float left on first span and just having a float right. That seemed to work.

Whitespace with adjacent selectors

I have a list of checkboxes and labels. Depending on the line item width, and the container wrap, the checkbox may end up on the previous line, with the label starting the 2nd line. I'd like for this to not happen, instead taking the checkbox with the label to the next line.
I attempted to use the ~ selector and I can reference the label this way (as denoted by the border) however the input is not affected which doesn't stop the line break from wrapping.
<ul>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
...
</ul>
Css
ul { list-style: none; }
ul li { display: inline; }
ul li input ~ label {
white-space: nowrap;
border: 1px solid #555;
}
Fiddle:
http://jsfiddle.net/rd1hqjsq/
You may want to use inline-block instead of inline for the display property on the li items:
The element generates a block element box that will be flowed with surrounding content as if it were a single inline box (behaving much like a replaced element would).
ul { list-style: none; }
ul li { display: inline-block; }
ul li input ~ label {
white-space: nowrap;
border: 1px solid #555;
}
<ul>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
<li>
<input type="checkbox" />
<label>AAAAAA</label>
</li>
</ul>
If you use inline-block take in care that generates a white-space between items that you may need to remove. Read this article for more info:
Fighting Space Between Inline-block elements

CSS Hover over an image to make a sub menu appear

I'm trying to make it so that I can use a menu like this
To be fair I barely understood this and I was informed that you can use a style with the hover function.
Here's what I tried with my code:
<center>
<ul>
<li>
<img src="images/audiorageGIFtype/audiorageBanner.gif" alt="Welcome to AudioRage!"/>
</li>
<li>
<img src="images/audiorageGIFtype/audiorageButtonHome.gif" alt="Home"/>
<img class="showHim" src="images/audiorageGIFtype/audiorageButtonStore.gif" alt="Store" />
<img src="images/audiorageGIFtype/audiorageButtonAbout.gif" alt="About"/>
<img src="images/audiorageGIFtype/audiorageButtonCart.gif" alt="Cart"/>
</li>
</ul>
</center>
<br />
<center>
<div class="showMe">I want this to show!!</div>
</center>
Here's the CSS
.showMe
{
display: none;
}
.showHim:hover .showMe
{
display: block;
}
Here's a pre-made link of the code to JSFIDDLE
This won't work because your display:block rule is looking for .showHim that is being hovers with the child of .showMe. You can do it a couple of ways.
Targeting a hidden child: jsFiddle
CSS
.hoverme:hover .showMe {
display: block;
}
HTML
<span class="hoverme">
<img class="showHim" src="images/audiorageGIFtype/audiorageButtonStore.gif" alt="Store" />
<span class="showMe">I want this to show!!</span>
</span>
Or targeting a sibling jsFiddle
CSS
.showHim:hover + .showMe {
display: block;
}
HTML
<img class="showHim" src="images/audiorageGIFtype/audiorageButtonStore.gif" alt="Store" />
<span class="showMe">I want this to show!!</span>
The typical menu is structured like this which makes it easy to target the hidden item. jsFiddle
HTML
<ul class="menu">
<li>
Some menu item
<ul class="submenu">
<li>Sub menu item</li>
</ul>
</li>
...
</ul>
CSS
.submenu {
display:none;
}
.menu > li:hover .submenu {
display:block;
}
You need to use some jQuery here to make if work:
$(".showHim").hover(
function () {
$('.showMe').css("display","block");
},
function () {
$('.showMe').css("display","none");
}
);
});
Here working example
for this you need to change your html structure
html code
<center>
<ul>
<li><img src="images/audiorageGIFtype/audiorageBanner.gif" alt="Welcome to AudioRage!"/></li>
<li><img src="images/audiorageGIFtype/audiorageButtonHome.gif" alt="Home"/></li>
<li class="showHim"><img src="images/audiorageGIFtype/audiorageButtonStore.gif" alt="Store" />
<div class="showMe">I want this to show!!</div></li>
<li><img src="images/audiorageGIFtype/audiorageButtonAbout.gif" alt="About"/></li>
<li><img src="images/audiorageGIFtype/audiorageButtonCart.gif" alt="Cart"/></li>
</ul>
</center>
CSS Code
.showMe{
display: none;
}
.showHim:hover .showMe{
display: block;
}
JsFiddle file

Resources