How do I keep dropdown list open in a sidnav when using a link?

I am trying to do an admin layout in only html, css and js. One problem that remains is that the dropdown list collapses as soon as I add a link. Using # as a link the list stay open (desired).

Here is the jsfiddle that behaves as desired as I cannot add any links: http://jsfiddle.net/q2en6djg/1/
And my attempt in a server: http://94.237.25.207:8080 Some of the list items are with links (Examples/about)
The server is using Golang and templates.

How do I get the sublist to stay open when using links?

TIA!

I’m a little unsure of the question as the sublist is staying open ok. Of course when you select a link to another page then the sublist will not ‘magically’ be open on the new page.

If that’s the issue you are concerned with then you will either need a script to decipher the fragment identifier part of the url and then accordingly open the side menu to the correct position.

Alternatively you could hard code it so that for example on the ‘about’ page you hard code the active class into the html on that page.

<button class="btn active">Examples</button>

I’m not sure if this was the issue were talking about though :slight_smile:

1 Like

The fragment link goes to the already loaded page so the list state is not affected. But a link that loads a different page will have the default list state. Of course. :slight_smile:

To reinstate the list state on e.g. (Examples/about) the loading page needs to open the list by itself.

That could be done in many ways using scripts serverside or clientside or even by html-css only. All methods inserts some classes and css into the page to reinstate the state of the menu as it was when the link was activated.

The html-css method:

  • Each page has its own class name in the body tag. That is what identifies what page is loaded.
  • Each page has its own class name on the list/menu link that opens it. The menu list is the same in all pages, loaded or not.
  • Each page has the css to use the body class to open the list that has the link and then combine the body class with the list item class to highlight the link that loaded the page. The css is the same for all pages, loaded or not.

The methods using scripts are mainly the same with dynamically inserted class names when the page is loaded.

For the moment I couldn’t find any good examples of how to code this so I described the general method instead. Searching for e.g. “highlight current page” should hopefully give some good examples of all methods to achieve this.

1 Like

Thanks, but the button should not be open by default. When clicking the sub menu should open and stay put until clicked on same or other button.

The sub page is already parsed and only displayed in an another grid on the same page.

Thank you! I will try to search.

www.w3schools have a couple of examples of same meny repeated on every page, but in this case there is only ONE sidenav serving several sub pages in a layout grid.

If you will find any good example to solve my problem, please tell me…

Please elaborate your circumstances.

1 Like

That’s contradictory to what you are saying?

You said you wanted to click a link to go to a destination page and on that destination page you want the submenu to be open. You don’t want the submenu closed on the destination page. The code I gave you is for the destination page and will open the menu on that destination page.

That doesn’t seem to be true either? If I view source for the about page:
http://94.237.25.207:8080/about

It is not the same as the view source for the steps page:
http://94.237.25.207:8080/steps

A complete re-load of the page happens when a link is clicked. Whether or not you are doing something at the back end to change the html I can’t tell from here but either way that would still allow you the ability to add the active class where needed.

I can tell you though that he html is badly formed and invalid so please use the w3c
html validator before you get too far along with this.:slight_smile:

1 Like

This is the goal at the end: https://materializecss.com/getting-started.html

I started to play with Angular, but there was no “multi level sidebar” available. I am now trying to achieve the same using Golang with templates. The upside with Golang is that it is lightning fast. The downside is that the main option is to create this from scratch in order to control (html, css and js).

In Golang you can have html templates. And one layout template can have sub templates (contents, headlines etc)

The example on http://94.237.25.207:8080 contains only one single sidenav serving the sub contents. But as soon as I select one item in a sublist, the button closes. Using the “#” does not close the list.

Answer to your question?

I was more wondering what your goal in this particular case is, if you would explain, please. The link doesn’t tell what that is, if I understand correctly it wasn’t just to have a multilevel menu.

Well, It does not “go to a destination page”. It just get a page and fill an other grid. So your code will work if you duplicate the same sidenav on each page.

That is an other good question, not yet asked. The first step is to be able to click on the sidenav and the submenu should stay open.

I think you are correct.The html is entirely reloaded. That may be the problem. But how do I “reload” the previous status and select the correct line in the sublist?

And they do exactly as I mentioned above in that they add the active class to the relevant elements in the source of the html. If you view source for the colours page you will see the hard coded active classes.


<li class="bold active"><a class="collapsible-header waves-effect waves-teal">CSS</a>
              <div class="collapsible-body">
                <ul>
                  <li class="active"><a href="color.html">Color</a></li>

You need to do the same on your page.

However, it sounds like your sidenav is a re-usable template and if so then you need to use the method mentioned by Erik in that you add a class to the body element (or relative parent) to indicate which item should be open on that page. You can then use CSS to target the correct item by using the new body class that was just added.

Alternatively you will need to pass a reference to a script so that it will open the correct menu level.

I assume that when you click a link your page is going back to the server and requesting the new page and a new page is loaded with the sidebar being a template that is called in each time. When you go back to the server I would assume you would also have the opportunity of adding a class to the body to indicate which page was open. I know nothing about golang so that would be something for you to research.

There are several goals.

  1. 2 level sidebar with only one sub menu open at a time (solved)
  2. Select a link in a sub list and the list should stay open - and be selected. (not solved)
  3. Use a link like http://94.237.25.207:8080/tab and open the correct button and select correct line by default.

My initial question was #2, but I think this will lead to a clue to solve #3 as well.

Again it is unclear what you mean exactly. The sub list has no further nested sublists so there is nothing else to open. Do you mean that when you click a ‘different’ sublist you want both sublists to be open? (i.e. not one opens and the current one closes).

If so then that is because you use different js in your fiddle from your real page. In the fiddle you simply open a clicked item but in the real page you first close all active items and just open the newly clicked item.

This is the code that does it.

for (i = 0; i < l; i++) {
  dropdown[i].addEventListener("click", function() {
    for (var j = 0; j < l; j++) {
      if (this != dropdown[j])
        dropdown[j].classList.remove('active')
    }
    this.classList.toggle('active');

  });
}

If you want menus to remain open until specifically clicked to close then remove the .remove part.

e.g. Do this instead.

for (i = 0; i < l; i++) {
  dropdown[i].addEventListener("click", function() {
    this.classList.toggle('active');
  });
}

Note that the second sequence of js that you have in your page is doing nothing.

This does nothing:


var sub = document.querySelector(".list a");

for (var i = 0; i < sub.length; i++) {
  sub[i].addEventListener("click", function() {
    this.classList.toggle('active');
	console.log('here');
  });
}

You need document.querySelectorAll to find all the items.

var sub = document.querySelectorAll(".list a");

The rest of the routine just adds an ‘active’ class to the anchor that was clicked (maybe you are checking that class serverside)?

var dropdown = document.getElementsByClassName("btn");
var l = dropdown.length;
var i;

for (i = 0; i < l; i++) {
  dropdown[i].addEventListener("click", function() {
    this.classList.toggle('active');
  });
}

 
var sub = document.querySelectorAll(".list a");

for (var i = 0; i < sub.length; i++) {
  sub[i].addEventListener("click", function() {
    this.classList.toggle('active');
	console.log('here');
  });
}
1 Like

Thanks for pointing me in the right direction.

If I interpret this correct: This has to be done dynamically by Javscript? Set the active row?

In this case Golang basically replaces Nginx or Apache. And parses the pages. The Golang code is about 40 rows. Everything else is normal web stuff.

Yes… sort of :slight_smile:

I think the point you are missing is that when you click a real link with a destination your original page is gone. You said goodbye to it and went to the server for a new page. The server then sends you the new page back down for you to view. The new page knows nothing about what was clicked on the old previous page (it may well be constructed with some of the same templates but is still essentially a new page).

At some part in the process you have to inform the new page about which menu item needs to be open. If you were using php or similar then you could send that data along with the url or indeed harvest it from the url. However as I said above I don;t know how golang works so don’t know if you can manage pages in that way.

It would be possible instead to insert a script into each page that looks at the url and then for example it sees that the destination matches a menu item in the side menu it could then go and add the active classes to the menu as required. (I think @rpkamp had an example of doing that in an old thread here.)

Does any of that makes sense :slight_smile:

2 Likes

If its of any use here’s the script from the old thread I mentioned adjusted to fit your page a little better but does use jquery.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content= "width=device-width, initial-scale=1">
<link rel="stylesheet" type= "text/css" href= "css/layout.css">
<title>Test</title>
<style>
.sidenav {
	margin-top: 60px;
	height: auto;
	width: 200px;
	position: fixed;
	z-index: 1;
	top: 0;
	left: 0;
	background-color: #555;
	overflow-x: hidden;
}
.sidenav a, .btn {
	padding: 10px 10px 10px 20px;
	text-decoration: none;
	font-size: 17px;
	color: #fff;
	display: block;
	text-align: left;
	border: none;
	background: none;
	width: 100%;
	cursor: pointer;
	outline: none;
	border-bottom:1px solid #777;
}
.sidenav a:hover, .btn:hover {
	color: #fff;
}
.sidenav a.active{background:red}
.list {
	display: none;
	background-color: #999;
}

@media screen and (max-height: 450px) {
	.sidenav a {
		font-size: 18px;
	}
}
</style>
</head>

<body>
<div class="sidenav"> 
	<a href="/home">Home</a>
  	<button class="btn">Examples</button>
  	<div class="list"> 
    	<a href="/about">About</a> 
        <a href="/table">Table</a> 
        <a href="/form">Register</a> 
    </div>
  	<button class="btn">Navigation</button>
  	<div class="list"> 
    	<a href="/tab">Tabs</a> 
    	<a href="/steps">Steps</a> 
    	<a href="#">Link 3</a> 
    </div>
     <button class="btn">Dropdown </button>
 </div>



<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {

    var currentURI = window.location.href;
    console.log(currentURI);
    var links = $('.sidenav a');
    for (var i = 0; i < links.size(); i++) {
        var elem = links.eq(i);
        var href = elem.attr('href');
        var hrefLength = href.length;
        var compareTo = currentURI.substr(-1 * hrefLength);

        console.log(compareTo);
        console.log(href);
        if (href == compareTo) {
            $(elem).closest('div').show()
			$(elem).addClass('active');
        }
    };
	$( ".sidenav" ).on( "click", "button", function() {
        var submenu = $(this).next('div');
        if (submenu.is(':visible')) {
            submenu.slideUp(500);
        } else {
            $('.sidebav').not(submenu).slideUp(500);
            submenu.slideDown(500);
        }
    });
});			
</script>
</body>
</html>

The above won’t work until its plugged into a page with proper urls to test. I’m not sure if it will work with your url structure but it works for me locally OK.

The script probably needs tidying up form an expert @rpkamp :slight_smile:

It could be made a bit more compact, and the console.log need to be removed before going live, but I’d say it’s fine to keep as is :slight_smile:

2 Likes

Only one sublist open at a time. As it is now.

I finally is beginning to understand what happens. The submenu closes because the page i reloaded. As pointed out of you earlier.

I can (as a newbie) think of two ways to fix this:

  1. Set the button and list row as “active” before reloaded. Something like this:
for (var i = 0; i < btns.length; i++) {
  btns[i].addEventListener("click", function() {
  var current = document.getElementsByClassName("active");
  current[0].className = current[0].className.replace(" active", "");
  this.className += " active";
  });
}
  1. Let the other page “reopen” the submenu and select the correct row. I guess that I have to add an id for every row in order to find the row to highlight?

#2 seem to be the smartest, because it will probably work both ways. I e either click OR open by a link.

What do you think?

No you can’t set anything on the current page that will affect the new page. Whatever class you add will be missing when the page is reloaded.

I gave you a full working example of how to do this in my last post :slight_smile:

2 Likes

Yes, thank you! It is almost perfect! http://94.237.25.207:8080/tab

1 Like