Dropdown with click

Asked

Viewed 1,025 times

5

I created an HTML and CSS from a menu but I can’t create Dropdown with click, only when you hover your mouse the menu collects or expands and I would like to do it with one click. Thanks in advance for the help.

*{ margin: 0; paddin: 0;}
body {background: ffffff; margin: 5px; }
#nav {margin: 0; padding: 0; }
#nav li {list-style: none; background: #fff; width: 250 px; border-bottom: 1px solid #666;}
#nav li a {display: block; padding: 8px; border-left: 4px solid #444; text-decoration: none; box-shadow: 2px 2px 5px #ccc; color: #555;}
#nav li a:hover {border-left:4px solid #069; background: #f8f8f8;}
#nav li ul{display: none;}
#nav li:hover ul { display: block; cursor:pointer;}
#nav li:hover ul li{background: #333;}
#nav li:hover ul li a {color:ccc;}
#nav li:hover ul li a:hover{background: #222; border-left:4px solid #900;}
<ul id="nav"> 
  <li><a href="#">Consultores</a>
    <ul>
      <li><a href="#">sub - 1</a></li>
      <li><a href="#">sub - 2</a></li>
      <li><a href="#">sub - 3</a></li>
    </ul>
  </li>	
  <li><a href="#">Colaboradores</a></li>
  <li><a href="#">Comercial</a></li>
  <li><a href="#">RH</a></li>
</ul>

  • It’s really hard to understand. Can you edit the question and code your CSS? Add HTML as well.

  • Have you tried instead of :Hover using :Focus? I think it works ;-)

3 answers

6

Only with CSS you could use one label with checkbox (hidden) only on the button that opens the submenu, instead of using a link <a>. When using a link <a> with href="#" only to open the submenu, will change the URL by adding the #.

By clicking on label will mark/deselect the checkbox, and you can use the pseudo-class :checked in the checkbox to show/hide the submenu. Place the input after the label and add a id at the checkbox and a for on the label pointing to the id:

*{ margin: 0; padding: 0;}
body {background: ffffff; margin: 5px; }
#nav {margin: 0; padding: 0; }
#nav li {list-style: none; background: #fff; width: 250 px; border-bottom: 1px solid #666; outline: none;}
#nav li a, #nav li label {display: block; padding: 8px; border-left: 4px solid #444; text-decoration: none; box-shadow: 2px 2px 5px #ccc; color: #555; cursor: pointer;}
#nav li a:hover, #nav li label:hover {border-left:4px solid #069; background: #f8f8f8;}
#nav li ul, #nav li input{display: none;}
#nav li:hover ul li{background: #333;}
#nav li:hover ul li a {color:ccc;}
#nav li:hover ul li a:hover{background: #222; border-left:4px solid #900;}
#nav li input:checked + ul{
   display: block; cursor:pointer;
}
<ul id="nav"> 
   <li>
      <label for="sub1">Consultores</label>
      <input id="sub1" type="checkbox">
      <ul>
         <li><a href="#">sub - 1</a></li>
         <li><a href="#">sub - 2</a></li>
         <li><a href="#">sub - 3</a></li>
      </ul>
   </li>	
  <li><a href="#">Colaboradores</a></li>
  <li><a href="#">Comercial</a></li>
  <li><a href="#">RH</a></li>

</ul>

There is a typo in the first line of your CSS: paddin would be padding.

  • But this application is semantically incorrect. In addition to going against the Semantic rules of W3C screen readers will read this element as a form option. The solution here should be a JS function.

  • Good observation. But screen readers do not ignore elements with display: None?

  • Ignore it, you’re right. But the label will keep popping up. And it’s still bad practice semantically. There are many people discussing the semantic use of this, but most agree it is 'Gambi'. Take a look in that post and in the comments.

  • Why bad practice semantically? That part I didn’t understand.

  • Imagine that HTML is the Portuguese language, you can say "I don’t want to", you can understand but it’s wrong. Here is the same thing, every HTML element has a purpose and should be used for this. Using a word or an offline HTML tag is semantically wrong for your respective language. The label defines the label of a form item, and should only be used for this.

  • So you should not use href=#, following this same logic. Because a link should be a link, not a button.

  • Exactly! I’ll change the code. I’m glad you noticed.

  • I hadn’t even seen your answer rs... Then I’ll analyze

Show 3 more comments

3

Good afternoon, from what I understand you are not using any framework, in this case a look at this code, it shows how to do, just adapt in your code.

$(document).ready(function(){

  $('div.dropdown').each(function() {
    var $dropdown = $(this);

    $("a.dropdown-link", $dropdown).click(function(e) {
      e.preventDefault();
      $div = $("div.dropdown-container", $dropdown);
      $div.toggle();
      $("div.dropdown-container").not($div).hide();
      return false;
    });

});
    
  $('html').click(function(){
    $("div.dropdown-container").hide();
  });
     
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<div id="dropdown-1" class="dropdown dropdown-processed">
  <a class="dropdown-link" href="#">Options</a>
  <div class="dropdown-container" style="display: none;">
    <ul>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </ul>
  </div>
</div>

<div id="dropdown-2" class="dropdown dropdown-processed">
  <a class="dropdown-link" href="#">Options</a>
  <div class="dropdown-container" style="display: none;">
    <ul>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </ul>
  </div>
</div>

<div id="dropdown-3" class="dropdown dropdown-processed">
  <a class="dropdown-link" href="#">Options</a>
  <div class="dropdown-container" style="display: none;">
    <ul>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </ul>
  </div>
</div>

To work you will need to import jquery into your application.

I just don’t edit your entire code because I’m running out of time, but it solves.

  • Hello Renan, could you post a part of the code here or explain better the answer? It is possible to put an executable code in the reply on the HTML/CSS/Javascript Snippet button or with the keyboard shortcut Ctrl + M.

  • 1

    I edited it, see if it makes me better.

  • The ideal would be to maintain the hierarchy of lists (ul li) as was in the question, as it is the most 'correct' way in terms of semantics, good practices and accessibility. Moreover it is not advisable to insert style in the code and adding a Listener throughout HTML to always be active in the dropdown state independent click, is unnecessary processing. Maybe you needed to have a function checking if the dropdown is open but even this would run all click on the screen.

0

The simplest and most correct way to get the expected result would be to add a javascript (or jQuery) that turns on the li when your link is clicked with a CSS class, and then you control the visual effect via CSS (in some cases animation via JS is also done, but animations via CSS are hardware-accelerated and have much better visual and processing performance). This is how most frameworks work.

More or less follows the code (with very weak JS).

$(".nav li > span").click(function() {
	// confesso que esse código parece pouco otimizado, aceito sugestões
	$parent_list_item = $(this).parent("li");
	// Remove todas as classes ativas de todos os itens dessa lista, para não ter 2 ativos ao mesmo tempo no mesmo dropdown. Mas ignora o item atual para checarmos depois de precisamos minimizar-lo
$parent_list_item.parent("ul").find(".active").not($parent_list_item).removeClass("active");
	// adiciona classe ativa para o <li> (pai) do <a> clicado, se ele não for o item que já está ativo
	if (!$parent_list_item.hasClass("active")) {
		$parent_list_item.addClass("active");
	}else{
		// Se for o que já está ativo remove a classe
		$parent_list_item.removeClass("active");
	}
});
body{
	padding:20px;
}
/* apenas para visualização */
.nav{
	width:400px;
}
/* reseta as listas em .nav */
.nav, .nav ul{
	list-style:none;
	margin:0;
	padding:0;
}
/* estiliza todos os botões dentro de nav */
.nav a,
.nav span{
	display:block;
	text-decoration:none;
	transition:all .3s ease; /* essa animação é entre a cor de fundo e cor da fonte, sempre muito útil */
}
/* posiciona o item relativo para os subitens (flutuantes) respeitarem sua posição */
.nav li{
	position:relative;
}
/* estiliza o primeiro nível */
.nav > li > a,
.nav > li > span{
	padding:10px;
	background:#eee;
	margin-bottom:1px;
	color:#333;
}
.nav li > span{
  cursor:pointer;
}
.nav li > span:after{
  display:inline-block;
  content:"+";
  font-weight:bold;
  font-size:16px;
  float:right;
  line-height:1em;
}
.nav > li > a:hover,
.nav > li > span:hover{
	background:green;
	color:white;
}
/* estiliza todos os subitems, ou seja, a lista pode ter vários subdropdowns. Está flutuante pois inseri position:absolute, se você remover o item mostra abaixo;*/
.nav li > ul{
	position:absolute; /* comente esta linha para mostrar abaixo*/
	top:100%;
	z-index:2;
	background:#aaa;
	max-height:0; /* limita a altura para animarmos (obs este elemento não pode ter padding) */
	overflow:hidden;
	transition:all 0.5s ease; /* anima os estados do elemento */
	/*left: 100%; /* descomente esta linha para mostrar ao lado invés de abaixo */
	width:100%; /* deixa o submenu com a largura do pai, depende do caso, ele sobrepõe todos os filhos nesse caso, que não é bom */
	margin-left:5px; /* um espaçamento pra ficar visualmente compreensível os níveis */
}
/* 2º nível */
.nav li  ul > li > a,
.nav li  ul > li > span{
	padding:9px;
	font-size:0.9em;
	border-bottom:1px solid white;
	color:#666;
}
.nav li > ul > li > a:hover,
.nav li > ul > li > span:hover{
	background:tomato;
	color:white;
}
/* 3º nível */
.nav li > ul > li > ul > li > a,
.nav li > ul > li > ul > li > span{
	padding:7px;
	font-size:0.8em;
	border-bottom:1px solid white;
	color:#666;
}
.nav li > ul > li > ul > li > a:hover,
.nav li > ul > li > ul > li > span:hover{
	background:limegreen;
	color:white;
}
/* estiliza o link dentro do item quando está ativo */
.nav li.active > a,
.nav li.active > span{
	background:#333;
	color:#fff;
}
.nav li.active > span:after{
	content:"-";
}

.nav li.active > ul{
	max-height:100vh; /* remove a limitação de altura para animar para height:auto.*/
    animation:0s 1s delay-overflow forwards; /* pequeno hack para o overflow mudar apenas para o item aberto depois da animação terminada */
}

@keyframes delay-overflow {
  to { overflow:visible; }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="nav"> 
  <li><span>Consultores</span>
    <ul>
      <li><a href="#0">sub - 1</a></li>
      <li><a href="#0">sub - 2</a></li>
      <li><span>sub - 3 (com subs)</span> <!-- como aqui é um botão simples de ordenação, ele não deve ser um link -->
				<ul>
					<li><a href="#0">sub sub - 1</a></li>
					<li><a href="#0">sub sub - 2</a></li>
					<li><a href="#0">sub sub - 3</a></li>
				</ul>
			</li>
    </ul>
  </li>	
  <li><a href="#0">Colaboradores</a></li>
  <li><a href="#0">Comercial</a></li>
  <li><a href="#0">RH</a></li>
</ul>

I find it strange how stackoverflow inserts code right into the page, so I always use alternatives like Codepen, click here to see this solution.

Other remarks about your code:

  • Try not to use IDs on elements that may be generic / modularized / componentized or repeatable on the page, try treating the ID only for specific JS selections. Ex: the page can have multiple dropdowns, so the component is generic.

I hope I helped (although this JS still bother me rs)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.