I'm building a web application that will show a hierarchy of things. The hierarchy will closely resemble that of a file system with folders and files, i.e. I'll have folders that contains files and subfolders in any depth level (though it will probably never go deeper than three levels).
The whole hierarchy will be shown in one view. It will be shown in a tree style and the folders can be expanded/closed by the user at will. The diffrent levels are indented, just as a standard file system browser.
What is a good way of representing this with HTML and CSS? Note that it is not the design/look itself I need help with but rather how to structure this in a good way using HTML. Should I use lists?
An approach framed using div and span, explained below with a working example at last.
Every folder contains 2 types of contents. Files and inner Folders. So a basic structure is designed with a overall container.
Overall Container:
<div id="hierarchy">
<!--folder structure goes here-->
</div>
Folder Structure:
<div class="foldercontainer">
<span class="folder fa-folder-o" data-isexpanded="true">Folder 1</span>
<span class="file fa-file-excel-o">File 11</span>
<span class="file fa-file-code-o">File 12</span>
<span class="file fa-file-pdf-o">File 13</span>
</div>
Folder Structure (with no files):
<div class="foldercontainer">
<span class="folder fa-folder">Folder 1</span>
<span class='noitems'>No Items</span>
</div>
The foldercontainer contains span elements that specifies the file names. First span element contains the folder title, while the remaining contains the filenames. The data- attribute isexpanded specifies whether the specified folder is expanded or collapsed.
If a folder contains no files, then add the noitems span element to the structure.
While a folder contains another folder, just add the same html as a child to it. i.e.,
<div class="foldercontainer">
<span class="folder fa-folder-o" data-isexpanded="true">Folder 1</span>
<span class="file fa-file-excel-o">File 11</span>
<span class="file fa-file-code-o">File 12</span>
<span class="file fa-file-pdf-o">File 13</span>
<div class="foldercontainer">
<span class="folder fa-folder-o" data-isexpanded="true">Folder 1-1</span>
<span class="file fa-file-excel-o">File 1-11</span>
<span class="file fa-file-code-o">File 1-12</span>
<span class="file fa-file-pdf-o">File 1-13</span>
</div>
</div>
Javascript:
The click event is delegated from the parent #hierarchy element to the folders and files, to handle click in a single event listener. On clicking a folder, it is expanded and the icon changes. On clicking again it collapses and resets the icon.
While the inner folder is expanded and outer folder is collapsed, when expanding the outer folder, the inner folder contents states are preserved, by design.
Refer this nice article to learn how to handle events on multiple elements in a single listener.
Note: The code can be altered to make use of CSS custom variables and Data attributes access to CSS to be more efficient, while the former is not supported in IE and latter has its own disadvantages in cross-browser support. So use at your own risk.
Working example with various hierarchies:
var hierarchy = document.getElementById("hierarchy");
hierarchy.addEventListener("click", function(event){
var elem = event.target;
if(elem.tagName.toLowerCase() == "span" && elem !== event.currentTarget)
{
var type = elem.classList.contains("folder") ? "folder" : "file";
if(type=="file")
{
alert("File accessed");
}
if(type=="folder")
{
var isexpanded = elem.dataset.isexpanded=="true";
if(isexpanded)
{
elem.classList.remove("fa-folder-o");
elem.classList.add("fa-folder");
}
else
{
elem.classList.remove("fa-folder");
elem.classList.add("fa-folder-o");
}
elem.dataset.isexpanded = !isexpanded;
var toggleelems = [].slice.call(elem.parentElement.children);
var classnames = "file,foldercontainer,noitems".split(",");
toggleelems.forEach(function(element){
if(classnames.some(function(val){return element.classList.contains(val);}))
element.style.display = isexpanded ? "none":"block";
});
}
}
});
#hierarchy
{
font-family: FontAwesome;
width: 300px;
}
.foldercontainer, .file, .noitems
{
display: block;
padding: 5px 5px 5px 50px;
}
.folder
{
color: red;
}
.file
{
color: green;
}
.folder, .file
{
cursor: pointer;
}
.noitems
{
display: none;
pointer-events: none;
}
.folder:hover,.file:hover
{
background: yellow;
}
.folder:before, .file:before
{
padding-right: 10px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<div id="hierarchy">
<div class="foldercontainer">
<span class="folder fa-folder-o" data-isexpanded="true">Folder 1</span>
<span class="file fa-file-excel-o">File 11</span>
<span class="file fa-file-code-o">File 12</span>
<span class="file fa-file-pdf-o">File 13</span>
<div class="foldercontainer">
<span class="folder fa-folder-o" data-isexpanded="true">Folder 1-1</span>
<span class="file fa-file-excel-o">File 1-11</span>
<span class="file fa-file-code-o">File 1-12</span>
<span class="file fa-file-pdf-o">File 1-13</span>
</div>
<div class="foldercontainer">
<span class="folder fa-folder">Folder 1-2</span>
<span class='noitems'>No Items</span>
</div>
<div class="foldercontainer">
<span class="folder fa-folder">Folder 1-3</span>
<span class='noitems'>No Items</span>
</div>
<div class="foldercontainer">
<span class="folder fa-folder">Folder 1-4</span>
<span class='noitems'>No Items</span>
</div>
</div>
<div class="foldercontainer">
<span class="folder fa-folder-o" data-isexpanded="true">Folder 2</span>
<span class="file fa-file-excel-o">File 21</span>
<span class="file fa-file-code-o">File 22</span>
<span class="file fa-file-pdf-o">File 23</span>
<div class="foldercontainer">
<span class="folder fa-folder-o" data-isexpanded="true">Folder 2-1</span>
<span class="file fa-file-excel-o">File 2-11</span>
<span class="file fa-file-code-o">File 2-12</span>
<span class="file fa-file-pdf-o">File 2-13</span>
<div class="foldercontainer">
<span class="folder fa-folder">Folder 2-1-1</span>
<span class='noitems'>No Items</span>
</div>
</div>
</div>
<div class="foldercontainer">
<span class="folder fa-folder-o" data-isexpanded="true">Folder 3</span>
<span class="file fa-file-excel-o">File 31</span>
<span class="file fa-file-code-o">File 32</span>
<span class="file fa-file-pdf-o">File 33</span>
<div class="foldercontainer">
<span class="folder fa-folder">Folder 3-1</span>
<span class='noitems'>No Items</span>
</div>
</div>
</div>
The good way to represent it in HTML is to organize your file list as a... HTML list :) For example you might get this:
<ul>
<li>Folder 1
<ul>
<li>SubFile 1</li>
<li>SubFile 2</li>
<li>SubFile 3</li>
</ul></li>
<li>Folder 2
<ul>
<li>SubFile 4</li>
<li>SubFile 5</li>
<li>SubFile 6</li>
</ul></li>
<li>Main File 1</li>
<li>Main File 2</li>
</ul>
Then the CSS can be very soft, because the list already represents a hierarchy.
I would avoid <ul> since that's for an "un-ordered list" whereas your folder structure will likely be in some sort of order.
I'd use the Definition List, or Ordered List;
<dl>
<dt>Folder 1
<dl>
<dt>Child 1</dt>
<dt>Child 2</dt>
<dt>Child 3</dt>
</dl>
</dt>
<dt>Folder 2
<dl>
<dt>Child 1</dt>
<dt>Child 2</dt>
<dt>Child 3</dt>
</dl>
</dt>
</dl>
<ol>
<li>Folder 1
<ol>
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ol>
</li>
<li>Folder 2
<ol>
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ol>
</li>
</ol>
I have created folder hierarchy using list items. This will help for sure.
$(function() {
setFolderHeirarchy();
});
function setFolderHeirarchy() {
var labelWrapper= $('.labelWrapper');
$(labelWrapper).each(function () {
if (!$(this).next('.subFolderHeirarchy')||$(this).next('.subFolderHeirarchy').length==0) {
$(this).find('.arrow').remove();
}
else{
console.log($(this).next('.subFolderHeirarchy'));
}
});
$('.labelWrapper').click(function() {
if ($(this).next('.subFolderHeirarchy').length > 0) {
$(this).parent('.folderHeirarchyList').toggleClass('active');
if ($(this).parent('.folderHeirarchyList').hasClass('active')) {
$(this).find('.arrow').removeClass('glyphicon-triangle-right').addClass('glyphicon-triangle-bottom');
} else {
$(this).find('.arrow').removeClass('glyphicon-triangle-bottom').addClass('glyphicon-triangle-right');
}
}else{
// $(this).css('color','red');
}
});
}
.mainFolderHeirarchy li {
border: none;
background: 0 0!important
}
.mainFolderHeirarchy li a {
color: #000;
text-decoration: none!important
}
.mainFolderHeirarchy li a:hover {
text-decoration: none!important
}
.mainFolderHeirarchy .folderHeirarchyList {
padding-top: 3px;
padding-bottom: 3px
}
.mainFolderHeirarchy .folderHeirarchyList.active>.subFolderHeirarchy {
display: block
}
.mainFolderHeirarchy .folderHeirarchyList .subFolderHeirarchy {
display: none;
padding-left: 5px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<h1> Folder Heirarchy/ multi level list item</h1>
<hr />
<ul class="list-group mainFolderHeirarchy">
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
<ul class="subFolderHeirarchy">
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
<ul class="subFolderHeirarchy">
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
</li>
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
<ul class="subFolderHeirarchy">
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
</li>
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
<ul class="subFolderHeirarchy">
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
</li>
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
<ul class="subFolderHeirarchy">
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
<ul class="subFolderHeirarchy">
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
</li>
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
</li>
</ul>
</li>
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li class="list-group-item folderHeirarchyList">
<span class="arrow glyphicon glyphicon-triangle-right"></span><span class="listlabel">listitem</span>
</li>
</ul>
</li>
</ul>
You can create a file tree with this html format:
<div class="cont">
<ul class="tree">
<li>
<div class="div"><i class="folder"></i> Folder</div>
<ul class="branch">
<li><i class="file"></i> File 1</li>
<li><i class="file"></i> File 2</li>
</ul>
</li>
</ul>
</div>
And with css and JavaScript:
var toggler = document.getElementsByClassName("div");
var i;
var slct = "None"
for (i = 0; i < toggler.length; i++) {
toggler[i].addEventListener("click", function() {
this.parentElement.querySelector(".branch").classList.toggle("active");
});
toggler[i].addEventListener("click", function() {
slct = this.textContent
document.getElementById("slct").textContent = slct
})
}
var files = document.getElementsByClassName("file")
for (i = 0; i < files.length; i++) {
files[i].parentNode.addEventListener("click", function() {
slct = this.textContent
document.getElementById("slct").textContent = slct
})
}
.tree {
list-style-type: none;
text-align: left;
padding: 0;
padding: 4px;
}
.active {
display: block !important;
}
.branch {
list-style-type: none;
display: none;
}
.folder {
height: 0px;
width: 0px;
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48IS0tISBGb250IEF3ZXNvbWUgUHJvIDYuMS4xIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlIChDb21tZXJjaWFsIExpY2Vuc2UpIENvcHlyaWdodCAyMDIyIEZvbnRpY29ucywgSW5jLiAtLT48cGF0aCBkPSJNNTEyIDE0NHYyODhjMCAyNi41LTIxLjUgNDgtNDggNDhoLTQxNkMyMS41IDQ4MCAwIDQ1OC41IDAgNDMydi0zNTJDMCA1My41IDIxLjUgMzIgNDggMzJoMTYwbDY0IDY0aDE5MkM0OTAuNSA5NiA1MTIgMTE3LjUgNTEyIDE0NHoiLz48L3N2Zz4=);
background-repeat: no-repeat;
overflow: clip;
padding: 8px;
text-align: center;
background-position: center;
position: relative;
filter: invert(74%) sepia(99%) saturate(730%) hue-rotate(357deg) brightness(100%) contrast(104%);
}
.file {
height: 0px;
width: 0px;
background-repeat: no-repeat;
overflow: clip;
padding: 6px;
text-align: center;
background-position: center;
position: relative;
filter: invert(74%) sepia(99%) saturate(730%) hue-rotate(357deg) brightness(100%) contrast(104%);
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj48IS0tISBGb250IEF3ZXNvbWUgUHJvIDYuMS4xIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlIChDb21tZXJjaWFsIExpY2Vuc2UpIENvcHlyaWdodCAyMDIyIEZvbnRpY29ucywgSW5jLiAtLT48cGF0aCBkPSJNMCA2NEMwIDI4LjY1IDI4LjY1IDAgNjQgMEgyMjRWMTI4QzIyNCAxNDUuNyAyMzguMyAxNjAgMjU2IDE2MEgzODRWNDQ4QzM4NCA0ODMuMyAzNTUuMyA1MTIgMzIwIDUxMkg2NEMyOC42NSA1MTIgMCA0ODMuMyAwIDQ0OFY2NHpNMjU2IDEyOFYwTDM4NCAxMjhIMjU2eiIvPjwvc3ZnPg==);
}
.cont {
width: 100%;
border-style: solid;
background-color: #dea002;
}
<div class="cont">
<ul class="tree">
<li>
<div class="div"><i class="folder"></i> Hello</div>
<ul class="branch">
<li class="item"><i class="file"></i> Hi</li>
<li>
<div class="div"><i class="folder"></i> Folder</div>
<ul class="branch">
<li><i class="file"></i> File 1</li>
<li><i class="file"></i> File 2</li>
</ul>
</li>
</ul>
</li>
<li><i class="file"></i> World</li>
</ul>
</div>
<b>Selceted File: <code id="slct">None</code></b>
This format is comprehensive and is not glitchy and unusable.
Note: The html above is a more extensive version of the first example.
I would use a table. The different columns represent deeper levels of indentation.
Related
What should I change in the following code to get my output right?
I want the skills and the bullets side-by-side but it's coming on the top of the skills.
I tried looking at different references but it spoiled my layout even more.
Below are my HTML and CSS codes:
.skills__content,
.languages__content {
grid-template-columns: repeat(2, 1fr);
}
.skills__content,
.experience__content {
gap: 1;
}
.languages__content {
gap: 0;
}
.skills__name,
.languages__name {
display: flex;
align-items: center;
margin-bottom: var(--mb-3);
}
.skills__circle,
.languages__circle {
display: inline-block;
width: 5px;
height: 5px;
background-color: var(--text-color);
border-radius: 50%;
margin-right: 0.75rem;
}
<!-- Skills -->
<section class="skills section" id="skills">
<h2 class="section-title">Skills</h2>
<div class="skills__content bd-grid">
<ul class="skills__data">
<li class="skills__name">
<span class="skills__circle">C </span>
</li>
<li class="skills__name">
<span class="skills__circle">C++</span>
</li>
<li class="skills__name">
<span class="skills__circle">Python</span>
</li>
<li class="skills__name">
<span class="skills__circle">Java</span>
</li>
<li class="skills__name">
<span class="skills__circle">Linux</span>
</li>
</ul>
<ul class="skills__data">
<li class="skills__name">
<span class="skills__circle">Html</span>
</li>
<li class="skills__name">
<span class="skills__circle">Css</span>
</li>
<li class="skills__name">
<span class="skills__circle">Javascript</span>
</li>
<li class="skills__name">
<span class="skills__circle">React.js</span>
</li>
<li class="skills__name">
<span class="skills__circle">Firebase</span>
</li>
</ul>
<ul class="skills__data">
<li class="skills__name">
<span class="skills__circle">MYSQL</span>
</li>
<li class="skills__name">
<span class="skills__circle">Excel</span>
</li>
</ul>
</div>
</section>
Attaching pics for references:
This is how it is right now
and This is how I want it to look,
You can do like this by adding some properties to <li> tag also . Below is the correct implementation.
Removed some unnecessary styling also
ul {
display: grid;
grid-template-columns: repeat(2,1fr);
list-style: none;
grid-row-gap: 20px;
}
li {
display: flex;
flex-direction: row;
}
.skills__name {
align-items: center;
}
.skills__circle,
.languages__circle {
display: flex;
align-items: center;
justify-content: left;
width: 5px;
height: 5px;
background-color: green;
border-radius: 50%;
margin-right: 5px;
}
<section class="skills section" id="skills">
<h2 class="section-title">Skills</h2>
<div class="skills__content">
<ul class="skills__data">
<li class="skills__name">
<span class="skills__circle"> </span>C
</li>
<li class="skills__name">
<span class="skills__circle"></span>C++
</li>
<li class="skills__name">
<span class="skills__circle"></span>Python
</li>
<li class="skills__name">
<span class="skills__circle"></span>Java
</li>
<li class="skills__name">
<span class="skills__circle"></span>Linux
</li>
</ul>
<ul class="skills__data">
<li class="skills__name">
<span class="skills__circle"></span>Html
</li>
<li class="skills__name">
<span class="skills__circle"></span>Css
</li>
<li class="skills__name">
<span class="skills__circle"></span>Javascript
</li>
<li class="skills__name">
<span class="skills__circle"></span>React.js
</li>
<li class="skills__name">
<span class="skills__circle"></span>Firebase
</li>
</ul>
<ul class="skills__data">
<li class="skills__name">
<span class="skills__circle"></span>MYSQL
</li>
<li class="skills__name">
<span class="skills__circle"></span>Excel
</li>
</ul>
</div>
</section>
I have a 2 level ul. I am using counter-reset, counter-increment, and content to insert a running counter. It works in that the content in the lis is numbering correctly.
I have and a link in the first level li, after its nested ul that I want to say "Add To Rule [number of parent li]".
Below is a minimum working example of what I am doing. In the a link, it is using the counter for the 2nd level ul/li.
What it outputs:
Rule 1
Rule 1.1...
Rule 1.2...
Rule 1.3...
Add To Rule 1.3
Rule 2
Rule 2.1...
Rule 2.2...
Rule 2.3...
Add To Rule 2.3
Rule 3
Rule 3.1...
Rule 3.2...
Rule 3.3...
Add To Rule 3.3
What I am trying achieve:
Rule 1
Rule 1.1...
Rule 1.2...
Rule 1.3...
Add To Rule 1
Rule 2
Rule 2.1...
Rule 2.2...
Rule 2.3...
Add To Rule 2
Rule 3
Rule 3.1...
Rule 3.2...
Rule 3.3...
Add To Rule 3
I can't figure out what I need to do to get my expected/wanted output.
ul
{
counter-reset: section;
}
li
{
counter-increment: section;
}
.ruleNumber::after
{
content: counters(section, ".");
}
a::after
{
content: counters(section, ".");
}
<ul>
<li>
<span class="ruleNumber">Rule </span>
<ul>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
</ul>
Add To Rule
</li>
<li>
<span class="ruleNumber">Rule </span>
<ul>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
</ul>
Add To Rule
</li>
<li>
<span class="ruleNumber">Rule </span>
<ul>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
</ul>
Add To Rule
</li>
</ul>
You might use two separate counters -- one for sections and one for items:
ul {
counter-reset: section;
}
ul ul {
counter-reset: item;
counter-increment: section;
}
li {
counter-increment: item;
}
.ruleNumber::after {
content: counters(item, ".");
}
a::after {
content: counters(section, ".");
}
<ul>
<li>
<span class="ruleNumber">Rule </span>
<ul>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
</ul>
Add To Rule
</li>
<li>
<span class="ruleNumber">Rule </span>
<ul>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
</ul>
Add To Rule
</li>
<li>
<span class="ruleNumber">Rule </span>
<ul>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
<li>
<span class="ruleNumber">Rule </span>
<div class="rule">...</div>
</li>
</ul>
Add To Rule
</li>
</ul>
I have this (simplified) navigation:
.ctmenu {
display: none;
float: right;
}
li:hover > .ctmenu {
display: inline-block;
}
<ul class="menu">
<li>
<span class="ctmenu">CT Menu</span>
Main page
<ul class="menu sub">
<li>
<span class="ctmenu">CT Menu</span>
Sub page
</li>
</ul>
</li>
</ul>
When I hover an li, the direct child .ctmenu should be visible. That works, but I only wan't the element that the mouse is currently on to show .ctmenu. When I hover "Sub menu", .ctmenu for "Main page" is also visible, because I'm hovering that too.
I'd prefer a CSS-fix only, but javascript/jQuery can be okay.
EDIT:
I made a little mistake in my first example that I can see from the answers is important:
The .ctmenu is before the a in the structure and with a float:right, so I can't use the a + .ctmenu selector. Is there an equivalent the other way around?
You need to add one extra div after LI tag. Please check below example
.ctmenu {
display: none;
float: right;
}
.ct-div:hover > .ctmenu {
display: inline-block;
}
<ul class="menu">
<li>
<div class="ct-div">
<span class="ctmenu">CT Menu</span>
Main page
</div>
<ul class="menu sub">
<li>
<div class="ct-div">
<span class="ctmenu">CT Menu</span>
Sub page
</div>
</li>
</ul>
</li>
</ul>
Try This:
.ctmenu { display: none; }
li a:hover + .ctmenu, .ctmenu:hover { display: inline-block; }
<ul class="menu">
<li>
Main page<span class="ctmenu">C Menu 1</span>
<ul class="menu sub">
<li>
Sub page<span class="ctmenu">C Menu 2</span>
</li>
</ul>
</li>
</ul>
Try This:
.ctmenu { display:none }
li a:hover+.ctmenu,
.ctmenu:hover {
display: inline-block;
}
<ul class="menu">
<li>
Main page
<span class="ctmenu">Demo 1</span>
<span class="ctmenu">Demo 2</span>
<span class="ctmenu">Demo 3</span>
<span class="ctmenu">Demo 4</span>
<ul class="menu sub">
<li>
Sub page
<span class="ctmenu">Demo 1</span>
<span class="ctmenu">Demo 2</span>
<span class="ctmenu">Demo 3</span>
<span class="ctmenu">Demo 4</span>
</li>
</ul>
</li>
</ul>
If the HTML structure doesn't change, you can do it with JavaScript, because there is no parent selector in CSS.
With the usage of mouseover, mouseout and classList, you can do something like:
var menu = document.querySelector(".menu");
var items = menu.querySelectorAll("li");
[].forEach.call(items, function(item) {
item.addEventListener("mouseover", function(e) {
e.stopPropagation();
this.children[0].classList.add("active");
});
item.addEventListener("mouseout", function(e) {
e.stopPropagation();
this.children[0].classList.remove("active");
});
})
.ctmenu {
display: none;
float: right;
}
.active {
display: inline-block;
}
<ul class="menu">
<li>
<span class="ctmenu a">CT Menu</span>
Main page
<ul class="menu sub">
<li>
<span class="ctmenu b">CT Menu</span>
Sub page
</li>
</ul>
</li>
</ul>
I have the following html code:
<ul class="hover amazing-menu">
<li>
item1
</li>
<li class="parent-item">
item2 <span class="fa fa-caret-down"></span>
<ul class="hover sub-menu">
<li class="parent">
sub-item1 <span class="fa fa-caret-right"></span>
<ul class="sub-menu2">
<li>sub-item11</li>
<li>sub-item11</li>
<li>sub-item11</li>
<li>sub-item11</li>
<li>sub-item11</li>
</ul>
</li>
</ul>
</li>
</ul>
And the following style:
.hover > li.parent-item:hover > ul.sub-menu, .hover > li.parent:hover > ul.sub-menu2{
opacity: 1 !important;}
I want to show sub menu when I hover to element with .parent-item class. It works properly, But when I hover to its children, Sub menu is shown.
Try to use display: none; and trigger the shown on the shown element :hover.
Look at this example:
ul.sub-menu,
ul.sub-menu2 {
display: none;
}
a:hover + ul.sub-menu,
a:hover + ul.sub-menu2,
ul.sub-menu:hover,
ul.sub-menu2:hover {
display: block;
}
<ul class="hover amazing-menu">
<li>
item1
</li>
<li class="parent-item">
item2 <span class="fa fa-caret-down"></span>
<ul class="hover sub-menu">
<li class="parent">
sub-item1 <span class="fa fa-caret-right"></span>
<ul class="sub-menu2">
<li>sub-item11</li>
<li>sub-item11</li>
<li>sub-item11</li>
<li>sub-item11</li>
<li>sub-item11</li>
</ul>
</li>
</ul>
</li>
</ul>
Fiddle example
I have a dropdown menu from the navbar and it has right-aligned (css class pull-right) glyphicons. One of the menu items is a bit longer than the others and it runs into the glyphicon, causing it to be moved down a half-line:
I can obviously set the width of this dropdown manually with a CSS class and a hardcoded width, but I'd rather get Bootstrap to incorporate the glyphicon width appropriately. How do I need to structure my HTML or what changes to CSS do I need to make to make this work?
Here's the HTML for the entire navbar:
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<span>
<img id="XXX-brand-logo" alt="Brand" src="/Content/Images/XXX.png" width="20" height="20"></span>
Home
</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-left">
<li>
XXX
</li>
<li class="dropdown">
XXX<span class="caret"></span>
<ul class="dropdown-menu">
<li>XXX</li>
<li>XXX</li>
</ul>
</li>
<li class="dropdown">
XXX<span class="caret"></span>
<ul class="dropdown-menu">
<li>XXX</li>
<li>XXX</li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
Admin<span class="caret"></span>
<ul class="dropdown-menu">
<li>Manage Roles</li>
<li>Manage Groups</li>
<li>Manage Users</li>
</ul>
</li>
<li class="dropdown">
<span class="glyphicon glyphicon-user"></span> Administrator<span class="caret"></span>
<ul class="dropdown-menu">
<li>Settings <span class="glyphicon glyphicon-wrench pull-right"></span></li>
<li>Change Password <span class="glyphicon glyphicon-lock pull-right"></span></li>
<li role="separator" class="divider"></li>
<li>Logout <span class="glyphicon glyphicon-log-out pull-right"></span></li>
</ul>
</li>
</ul>
</div>
</div>
And here's any CSS that could possible modify this page:
body {
padding-top: 50px;
padding-bottom: 20px;
}
/* Set padding to keep content from hitting the edges */
.body-content {
padding-left: 15px;
padding-right: 15px;
}
.glyphicon-hover {
border-radius: 25px;
border: 1px solid transparent;
padding: 2px;
}
.glyphicon-hover:hover, .glyphicon:focus {
border-radius: 5px;
border: 1px solid black;
color: red;
}
I'm using jquery-ui.css (version 1.11.4 via jQuery.UI.Combined in Nuget) and bootstrap.css (version 3.3.6.1 via Nuget).
Rearranging your markup to place the icon before the anchor text (this will circumvent the "step-down" effect), then wrapping the anchor text in another nested tag that you can target to apply further styling will help in resolving the issue at hand.
HTML
<ul class="dropdown-menu ">
<li><span class="glyphicon glyphicon-wrench pull-right "></span><span class="dropdown-link-text ">Settings</span>
</li>
<li><span class="glyphicon glyphicon-lock pull-right "></span><span class="dropdown-link-text ">Change Password</span>
</li>
<li role="separator " class="divider "></li>
<li><span class="glyphicon glyphicon-log-out pull-right "></span><span class="dropdown-link-text ">Logout</span>
</li>
</ul>
CSS
.dropdown-link-text {
display: inline-block;
margin-right: 20px;
}
Working example: http://www.bootply.com/wMD9IaHs4R
Alternatively, you could also try adding the icons in as pseudo elements of the link tag itself, then position them absolute and adjust the top, right values accordingly.
I've used this CSS instead of the .pull-right class:
#media (min-width: 768px) {
.navbar-right .dropdown-menu > li > a {
padding-right: 46px;
position: relative;
}
.navbar-right .dropdown-menu > li > a > .glyphicon {
display: block;
position: absolute;
right: 20px;
top: 5px;
}
}
Please check the result. Is it what you want to achieve?
#import url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css');
body {
padding-top: 50px;
padding-bottom: 20px;
}
/* Set padding to keep content from hitting the edges */
.body-content {
padding-left: 15px;
padding-right: 15px;
}
.glyphicon-hover {
border-radius: 25px;
border: 1px solid transparent;
padding: 2px;
}
.glyphicon-hover:hover, .glyphicon:focus {
border-radius: 5px;
border: 1px solid black;
color: red;
}
#media (min-width: 768px) {
.navbar-right .dropdown-menu > li > a {
padding-right: 46px;
position: relative;
}
.navbar-right .dropdown-menu > li > a > .glyphicon {
display: block;
position: absolute;
right: 20px;
top: 5px;
}
}
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<span>
<img id="XXX-brand-logo" alt="Brand" src="/Content/Images/XXX.png" width="20" height="20"></span>
Home
</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-left">
<li>
XXX
</li>
<li class="dropdown">
XXX<span class="caret"></span>
<ul class="dropdown-menu">
<li>XXX</li>
<li>XXX</li>
</ul>
</li>
<li class="dropdown">
XXX<span class="caret"></span>
<ul class="dropdown-menu">
<li>XXX</li>
<li>XXX</li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
Admin<span class="caret"></span>
<ul class="dropdown-menu">
<li>Manage Roles</li>
<li>Manage Groups</li>
<li>Manage Users</li>
</ul>
</li>
<li class="dropdown">
<span class="glyphicon glyphicon-user"></span> Administrator<span class="caret"></span>
<ul class="dropdown-menu">
<li><span class="glyphicon glyphicon-wrench"></span>Settings</li>
<li><span class="glyphicon glyphicon-lock"></span>Change Password</li>
<li role="separator" class="divider"></li>
<li><span class="glyphicon glyphicon-log-out"></span>Logout</li>
</ul>
</li>
</ul>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
The simplest solution is to line break the text
Change<br>Password
This leaves all elements and the menu the same size horizontally. The second simple solution is to change the width
.glyphicon {
padding-left: 10px;
}