Reputation: 25
I am trying to achieve a basic css drop down menu with NO nested lists within lists. In other words the normal way is:
<ul>
<li><a>Blah</a></li>
<li><a>Blah Parent</a>
<ul>
<li><a>Blah Child</a></li>
</ul>
</li>
</ul>
The way that I need to do it is:
<ul>
<li><a>Blah</a></li>
<li><a>Blah Parent</a></li>
<li class="childitem"><a>Blah Parent</a><li>
<li class="childitem"><a>Blah Parent</a><li>
<li><a>Blah</a></li>
<li><a>Blah</a></li>
<li class="childitem"><a>Blah Parent</a><li>
<li class="childitem"><a>Blah Parent</a><li>
</ul>
Is there any way to do this with purely css? Or with some basic js help?
Upvotes: 1
Views: 1434
Reputation: 13
Here is an example which uses CSS and a javascript.
<div class="topnav" id="myTopnav">
<a href="home.html" class="active">Home</a>
<div class="dropdown">
<button class="dropbtn">Products
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<a href="plinkone.html">Link 1</a>
<a href="#">Link 2</a>
<a href="#">Link 3</a>
</div>
</div>
<div class="dropdown">
<button class="dropbtn">Services
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<a href="slinkone.html">Link 1</a>
<a href="#">Link 2</a>
<a href="#">Link 3</a>
</div>
</div>
<a href="a.html">About</a>
<a href="news.html">News</a>
<a href="c.html">Contact</a>
<a href="javascript:void(0);" style="font-size:15px;" class="icon" onclick="myFunction()">☰</a>
</div>
Here is the javascript:
<script>
function myFunction() {
var x = document.getElementById("myTopnav");
if (!x.classList.contains("responsive")) {
x.classList.add("responsive");
} else {
x.classList.remove("responsive");
}
}
</script>
and the CSS
.topnav {
background-color: #333;
overflow: ;
}
.topnav::after {
clear: both;
content: "";
display: block;
}
.topnav a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
}
.active {
background-color: ;
color: white;
}
.topnav .icon {
display: none;
}
.dropdown {
float: left;
overflow: ;
}
.dropdown .dropbtn {
font-size: 17px;
border: none;
outline: none;
color: white;
padding: 14px 16px;
background-color: #333;
font-family: inherit;
margin: 0;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.dropdown-content a {
float: none;
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
}
.topnav a:hover, .dropdown:hover .dropbtn {
background-color: #555;
color: white;
}
.dropdown-content a:hover {
background-color: #ddd;
color: black;
}
.dropdown:hover .dropdown-content {
display: block;
}
@media screen and (max-width: 600px) {
.topnav a:not(:first-child), .dropdown .dropbtn {
display: none;
}
.topnav a.icon {
float: right;
display: block;
}
}
@media screen and (max-width: 600px) {
.topnav.responsive {position: relative;}
.topnav.responsive .icon {
position: absolute;
right: 0;
top: 0;
}
.topnav.responsive a {
float: none;
display: block;
text-align: left;
}
.topnav.responsive .dropdown {float: none;}
.topnav.responsive .dropdown-content {position: relative;}
.topnav.responsive .dropdown .dropbtn {
display: block;
width: 100%;
text-align: left;
}
}
Upvotes: 0
Reputation: 14645
Indeed, "with some basic js help", you'd need just a simple 'one-liner' (in your function) to fix up the HTML, here it is formatted for better readability (and explanation in comments):
function nestSubULbyClass(par, cls){
for( var elms=par.getElementsByTagName('li'), L=elms.length, t
; L-- //loop through elements in reverse to avoid nested look-ahead loop(s)
; ~(' '+elms[L].className+' ').indexOf(cls) //if current elm contains specified class
? (t || (t=document.createElement('ul')) //then take temp UL or create a new one
).appendChild(par.removeChild(elms[L])) // and move current elm to temp UL
: t && (elms[L].appendChild(t), t=0) //else append temp UL to current elm
); // and clear temp with falsy value
}
//"Make it so, number one!!" Note this is just an example, hook and pass UL any way you like
window.onload=function(){
nestSubULbyClass(document.getElementsByTagName('ul')[0], 'childitem');
};
<ul>
<li><a>Blah</a></li>
<li><a>Blah Parent</a></li>
<li class="childitem"><a>Blah Parent</a></li>
<li class="childitem"><a>Blah Parent</a></li>
<li><a>Blah</a></li>
<li><a>Blah</a></li>
<li class="childitem"><a>Blah Parent</a></li>
<li class="childitem"><a>Blah Parent</a></li>
</ul>
EDIT:
From here on, one can now simply use any pure css based drop-down menu (and styling) without requiring any other javascript-handlers (nice)!
For the following example I used the css from this answer. Note: I removed comments and formatting from the one-liner (humoring anyone arguing that non-context-aware standard formatting is better to read ☺)
function nestSubULbyClass(par, cls) {
for(var elms=par.getElementsByTagName('li'), L=elms.length, t; L--; ~(' '+elms[L].className+' ').indexOf(cls) ? (t || (t=document.createElement('ul'))).appendChild(par.removeChild(elms[L])) : t && (elms[L].appendChild(t), t=0));
}
window.onload=function(){
nestSubULbyClass(document.getElementsByTagName('ul')[0], 'childitem');
};
ul {
font-family: Arial, Verdana;
font-size: 14px;
margin: 0;
padding: 0;
list-style: none;
}
ul li {
display: block;
position: relative;
float: left;
}
li ul {
display: none;
}
ul li a {
display: block;
text-decoration: none;
color: #ffffff;
border-top: 1px solid #ffffff;
padding: 5px 15px 5px 15px;
background: #2C5463;
margin-left: 1px;
white-space: nowrap;
}
ul li a:hover {
background: #617F8A;
}
li:hover ul {
display: block;
position: absolute;
}
li:hover li {
float: none;
font-size: 11px;
}
li:hover a {
background: #617F8A;
}
li:hover li a:hover {
background: #95A9B1;
}
<ul>
<li><a>Blah</a></li>
<li><a>Blah Parent</a></li>
<li class="childitem"><a>Blah Parent</a></li>
<li class="childitem"><a>Blah Parent</a></li>
<li><a>Blah</a></li>
<li><a>Blah</a></li>
<li class="childitem"><a>Blah Parent</a></li>
<li class="childitem"><a>Blah Parent</a></li>
</ul>
Upvotes: 1
Reputation: 93571
The result you want is actually a bit ambiguous, but there are several approaches to this.
If you want to change the DOM dynamically you can make use of jQuery's nextUntil()
to find the sibling elements to move:
JSFiddle: http://jsfiddle.net/TrueBlueAussie/9afavum5/
// Solution 1 - change the DOM
$("ul li:not(.childitem)").each(function () {
var children = $(this).nextUntil(":not(.childitem)");
if (children.length) {
$(this).append($("<ul>").append(children));
}
});
This works by finding the intended parents (that do :not have .childitem), then finding the siblings up until the next parent element using nextUntil.
If you simply want to toggle to child items, and not change the DOM, you can use similar matching techniques to find the sibling items:
JSFiddle: http://jsfiddle.net/TrueBlueAussie/9afavum5/1/
// Solution 2 - toggle the siblings
$("ul li.childitem").hide();
$("ul li:not(.childitem)").click(function(){
$(this).nextUntil(":not(.childitem)").toggle();
});
Upvotes: 1
Reputation: 1458
You can As Long as The Got Id's And Clases FIDDLE HERE
HERE
<ul>
<li><a>Blah</a></li>
<li class="drp"><a>Blah Parent</a></li>
<li class="childitem"><a>Blah Parent</a><li>
<li class="childitem"><a>Blah Parent</a><li>
</ul>
JS
$(document).ready(function(){
$(".drp").hover(function(){
$(".childitem").slideToggle("fast");
},function(){
$(".childitem").hide();
});
$(".childitem").hover(function(){
$(".childitem").show();
},function(){
$(".childitem").hide();
});
});
CSS
.drp:hover
{
cursor: pointer;
}
ul li
{
list-style-type: none;
}
.childitem
{
display: none;
}
.childitem:hover
{
background: green;
cursor: pointer;
}
Upvotes: 0