This sidebar is special. I wanted to build archive form page like the mdn web docs when it's impossible to control the page's structure. And the result is this page.
Index
Summary
As I mentioned earlier this sidebar menu is completed in a post. If we could control page structure, this problem simply solves with sub pages. The sidebar function will be done with only position: sticky
.
But if you are going to complete all this functions in a post, it goes complicated. If you write indivisual posts for each menu, post managing will be hard, post list will be complicated, URL structure will be not uniformed, you have to syncronize sidebar menu by hand. I will be like sidebar because you can't apply height: 100%
if you put sidebar directly in specific HTML.
So I wanted a page that can indivisually works. So I wanted something that doesn't be effected by parents structure, don't need to write other posts to use, works well by own. Of course I didn't check other environment because I only do Blogger. (But I know a condition that unable to use at. You can't have overflow: hidden
in parent nodes.)
Frame
HTML
<div class='sidebar-container'> <aside class='sidebar'> [details] </aside> <main> [details] </main> </div>
It's simple. Actually deviding with aside tag and main tag is not necessary either. I just used sementic tags. The point is covering with a div. That's all.
CSS
.sidebar-container { display: flex } .sidebar { position: sticky; height: max-content; max-height: 100vh; display: block; overflow: auto; top: 0; } .sidebar-container > main { width: 100% }
I omitted additional design like margin.
The most important thing is display: flex
. Pages like this doesn't have fixed height. And I couldn't use height: 100%
. I had considered calculating height through javascript but I found a easier way. Flex worked. While writing this post I found out by web search that not using sticky in flexbox is general. Because of syncronized height. But sidebar was possible because this need to have same height with other at first.
position: sticky
is the core. This makes sidebar follows users scroll. Surprisingly max-height: 100vh
is the core too. Except this, if sidebar is longer than the main part space added under the main part. overflow: auto
needs too to scroll. top: 0
for sticky. But if you got bar on the top like me you have to add distance on top.
Width 100% on main is not necessary but required when you got width-based design. Even if you don't, I suggest you to add the code.
Funtion Implementation
This part is not about sidebar. About showing proper contents in the main. As I mentioned before this is for completing in one post or page, so it's wild goose chase for who don't.
Mechanism
First I divided <main>
with multiple <section>
s. This can be alternated with other tags like <div>
. It's on your taste!
And I put hidden
in every sections except a default view.
<section id='...'> [main] </section> <section id='...' hidden> [details] </section> <section id='...' hidden> [details] </section>
Like this.
hidden
is just for not showing.
Javascript
First let's make a function.
function sidebar(id) { if(id) { let m = document.getElementsByTagName("main")[0]; document.getElementById(m.dataset.display).setAttribute("hidden",""); document.getElementById(id).removeAttribute("hidden"); m.dataset.display = id; } }
This is for hide displaying part, show tag have same id with input, save that id in the main tag. For this you need to have data-display
in the document, and you need to have no hidden
attribute in that tag. You might say "Just find a tag that not containing hidden
attribute.". You can do that. But I prefer performance than my convenience.
Applying URL
We have a problem. If someone just copy url and share, reciever will always start at main part wheather where viewer was at. And if you refresh the page, you will return to the main view. You got #id behind url if we links id with the <a>
tag, and we'll use this.
<script> let id = window.location.href.replace(/^.+\#/, ""); if(id != window.location.href) sidebar(id); </script>
I input this. Erases first to # from URL and inputs this into the function. If you got parameters in URL you might change the regex.
But position is important. That tag must be behind the </main>. If it runs without rendering error occurs.
<a> Form
Now you have to run the sidebar("[id of wanted page]")
function when you view other pages. Plus you have to contain href='#[id]'
attribute.
<a href='#[id]' onclick='sidebar("[id]")'></a>
So when you anchor inner links, you have to do like this.
Final Form
<script> function sidebar(id) { if(id) { let m = document.getElementsByTagName("main")[0]; document.getElementById(m.dataset.display).setAttribute("hidden",""); document.getElementById(id).removeAttribute("hidden"); m.dataset.display = id; } } </script> <div class='sidebar-container'> <aside class='sidebar'> <a href='#[id1]' onclick='sidebar("[id1]")'>[main]</a> <a href='#[id2]' onclick='sidebar("[id2]")'>[1]</a> <a href='#[id3]' onclick='sidebar("[id3]")'>[2]</a> </aside> <main data-display='[id1]'> <section id='[id1]'> [details] </section> <section id='[id2]' hidden> [details] </section> <section id='[id3]' hidden> [details] </section> </main> <script> let id = window.location.href.replace(/^.+\#/, ""); if(id != window.location.href) sidebar(id); </script> </div>
Like this. Of course just with that code sidebar will be odd with no line breaks. I put list tag but I omitted because I didn't want to detemine so far.
Done
It taked too long though there's nothing... I slept once while writing this...