Squarespace is a tricky site-builder, because even though there are a ton of facilities for a non-techie client, those pre-made components are not always as the client desires.
Let’s be clear, I won’t use the abbreviation SS for Squarespace, because it resembles with something from the 2nd world war(Protective Echelon for the guy who’s name starts with H), so I won’t do it. Secondly, yes Squarespace does not support only 2 level navigation. The first level are the folders in the editor and the second are the pages.
But what if we would want another level, the 3rd level because the 2nd level got really crowdy? Well then you could try a new prayer or a new site-builder. Or actually you could try my trick with Javascript and CSS. This is what you will get:


Are you with me? Then let me explain:
The basic idea is to create an HTML in the Injections panel in Sitecore, more exactly a <div> tag with links inside and then my javascript snippet will do the rest. Below you can see in the Injections Footer panel on top the 3rd level menu construction, that is HTML.

Each <div> tag represents a 3rd level submenu and each link, the submenu items. The data-connection attribute will have to match with the 2nd level menu’s link url, where we actually want to add the submenu. In this case the first submenu will be connected to the team link and the second to the financials link.
Now the “humongous” Javascript code follows in the Footer panel in script tags, I am just showing a part of it:

Basically what we will do is iterate through each 2nd level navigation item check if the item’s link url matches one of the submenus data-connection value. After we have a match we will move that div with the links near the top navigation link and we will do this also for the separate mobile menu. But let’s just demystify this code in segments.
First I will explain it in little parts and in the end I will post the entire code for you.

As I mentioned before the data-connection is crucial here to be able to identify where does this submenu belongs to. The class “header-nav-folder-subitem” will be important for the CSS.

level2Items will store an array of the navigation items from desktop main navigation and mobile main navigation. Now level3Items is more trickier, because we need the submenus duplicated since we are using them in desktop navigation and mobile navigation so we will need to do some serious cloning to do in the near future.
After we mapped each item in level2Items to be cloned or deep-cloned(true is for cloning with child content), then we will create a new array and spread the cloned and the original submenu items inside of it.
We create the leve2ItemNr to have the number of the items instead of adding it in a loop, where on each iteration it will count the length of the total items.

So we go through the 2nd level navigation items. We used for instead of forEach because we need to break the loop a couple of times and forEach is actually unstoppable, you can not break out of it, but you can with for.
I like to store selectors in variables because it is best practice if you use it more then 1 time. Now level3ItemNr is the tricky variable. First we take the length of the level3Items array, then we divide it with 2 because … well because it has the cloned items and those should not be counted and the Math.round is for the case where we divide 1 with 2 and we don’t want decimals.
The condition which translated is level3Items.length === 0, breaks the loop, stops the iteration because there is no more match checking remaining.

In this step the matching process is in scope.
mainItemLink.pathname – this is the 2nd level navigation item’s url
item.dataset.connection – this is how we get the data-connection attribute from the submenu item
After we made the matching we remove that object from the level3Items, so that array is getting smaller and smaller.
The next 4 rows actually are related to the small icons added on mobile version, a right arrow next to the 2nd level navigation that suddenly became a “parent”, meaning it got a child sub-navigation item(or at least in the next step it will get).
The 4th row is for adding a close button for the 3rd level navigation popup, because this is how the 3rd level will look like, this is how we will close it, because we rock, we do things right and visually striking. That’s why. But you will see later, what I am talking about.

We append the item(3rd level menu) into the mainItem(2nd level menu). The condition checks if we are on the mobile navigation(Squarespace put’s different class on menu items for mobile and desktop);
Now we need to handle the click on the 2nd level navigation item, to not go to another page as it was used to (ev.preventDefault()) but instead do the following:
Add the class ‘opened-nav’ to the menu item, that will open in a spectacular fashion the 3rd level menu from CSS. Then we add to the popup the height of the document minus the sticky header height(if you have one). Finally we use that forEach to check if we are on the page of one of the 3rd level submenu links, than that link should have the active class on it(to style it with an underline or bold or as you wish).

Eventually we add some event listeners to the close button of the popup, when clicking on it to close the popup and one for the burger menu to clear the opened 3rd level navigation popup if you click on burger menu and revisit that submenu.
The only thing remaining is the CSS:

This is for mobile version, but of course you can style it as you wish. Also when you open the navigation by adding the opened-nav class, you should add also this style in the custom CSS panel:

The pointer events actually initially was set to none, because the popup was there but it was invisible, fully transparent. When opening we restore the pointer-events to default value.
Conclusion: This trick involves a lengthy code and some CSS and also some HTML but if you think the juice worth the squeeze, then use it, implement it and style it in your own way.
The entire code is down below:
<div class="header-nav-folder-subitem" data-connection="whatweoffer">
<a href="/vision"><span>Our vision</span></a>
<a href="/legacy"><span>Our legacy</span></a>
</div>
<div class="header-nav-folder-subitem" data-connection="financials">
<a href="/money"><span>Money</span></a>
</div>
<script>
const level2Items = [...document.querySelectorAll('.header-display-desktop .header-nav-folder-item, .header-menu--folder-list .header-menu-nav-item')];
let level3Items = [...document.querySelectorAll('.header-nav-folder-subitem')];
const level3ClonedItems = level3Items.map(item => {
return item.cloneNode(true);
});
level3Items = [...level3ClonedItems, ...level3Items];
const level2ItemsNr = level2Items.length;
for(let n = 0; n < level2ItemsNr; n++){
const mainItem = level2Items[n];
const mainItemLink = mainItem.querySelector('a');
const level3ItemsNr = Math.round(level3Items.length / 2) ;
if(!level3ItemsNr) {
break;
}
for(let i = 0; i < level3ItemsNr; i++){
const item = level3Items[i];
if(mainItemLink.pathname === '/' + item.dataset.connection) {
level3Items.splice(i,1);
if(mainItem.classList.contains('header-menu-nav-item')) {
mainItemLink.insertAdjacentHTML('beforeEnd', '<span class="chevron chevron--right"></span>')
}
const closeButton = item.insertAdjacentHTML('beforeEnd','<span class="close"></span>');
mainItem.append(item);
if(mainItem.classList.contains('header-menu-nav-item')) {
mainItemLink.addEventListener("click", ev => {
ev.preventDefault();
const docH = document.documentElement.offsetHeight;
const headerH = document.getElementById('header').offsetHeight;
item.classList.add('opened-nav');
item.style.height = docH - headerH;
item.querySelectorAll('a').forEach(submenuLink => {
if(window.location.pathname === submenuLink.pathname) {
submenuLink.classList.add('active');
}
});
});
item.querySelector('.close').addEventListener('click', ev => {
item.classList.remove('opened-nav');
});
document.querySelectorAll('.header-burger')[1].addEventListener('click', burger => {
item.classList.remove('opened-nav');
});
}
break;
}
}
};
</script>
.header-nav {
.header-nav-folder-item {
position: relative;
&:hover {
.header-nav-folder-subitem {
display: block;
}
}
}
.header-nav-folder-subitem {
position: absolute;
top: -1px;
left: calc(100% + 15px);
background: #000;
color: #fff;
}
}
.header-nav-folder-subitem {
display: none;
min-width: 200px;
a {
padding: 10px 0 8px!important;
border-top: 2px solid black;
line-height: 1.5rem;
&:first-child {
border: none;
}
}
}
body:not(.header--menu-open) .header-nav-folder-item--active .header-nav-folder-item-content {
background: none;
}
@media (max-width: 1024px) {
.header-nav-folder-subitem {
background: @gray!important;
display: flex!important;
justify-content: center;
flex-direction: column;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: 0 auto;
pointer-events: none;
transform: scale(2);
opacity: 0;
transition: transform .6s, opacity .3s;
z-index: 10000;
a {
border: 0;
&.active {
text-decoration: underline;
}
}
.close {
font-size: 30px;
position: absolute;
right: 6vw;
top: 10px;
cursor: pointer;
min-width: 32px;
min-height: 32px;
&:before, &:after {
position: absolute;
left: 15px;
content: ' ';
height: 33px;
width: 2px;
background-color: #333;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
}
}
.opened-nav {
opacity: 1;
transform: scale(1) translateX(0);
pointer-events: auto;
}
}
In case you’re having trouble with keeping the 3rd level navigation opened then use this:
.header-nav {
.header-nav-folder-item {
position: relative;
&:hover {
.header-nav-folder-subitem {
display: block;
}
}
}
.header-nav-folder-subitem {
position: absolute;
top: -1px;
left: 100%;
background: #000;
color: #fff;
}
}
.header-nav-folder-subitem {
display: none;
min-width: 200px;
a {
padding: 10px 0 8px!important;
border-top: 2px solid black;
line-height: 1.5rem;
&:first-child {
border: none;
}
}
}
body:not(.header--menu-open) .header-nav-folder-item--active .header-nav-folder-item-content {
background: none;
}
@media (max-width: 1024px) {
.header-nav-folder-subitem {
background: @gray!important;
display: flex!important;
justify-content: center;
flex-direction: column;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: 0 auto;
pointer-events: none;
transform: scale(2);
opacity: 0;
transition: transform .6s, opacity .3s;
z-index: 10000;
a {
border: 0;
&.active {
text-decoration: underline;
}
}
.close {
font-size: 30px;
position: absolute;
right: 6vw;
top: 10px;
cursor: pointer;
min-width: 32px;
min-height: 32px;
&:before, &:after {
position: absolute;
left: 15px;
content: ' ';
height: 33px;
width: 2px;
background-color: #333;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
}
}
.opened-nav {
opacity: 1;
transform: scale(1) translateX(0);
pointer-events: auto;
}
}
For further explanations watch the video below:
Do you have video for the installation?
I don’t, but I will attach one to this article, soon!
Hi. I attached the video with the instructions on the top of the article. I hope it helps.
Hi Jozsef,
We’re using your code to create the navigation for a website we’re currently designing. The code has worked perfectly on desktop, allowing us to create a third level of navigation pages. However, when we try the website on mobile, it isn’t allowing us to click on the 2nd level pages that we can click on desktop. We also tried to add in more pages as we needed them, these are showing on desktop but the click through to the third level isn’t showing on mobile.
Is it also possible to delete the second ‘X’ or cross that appears on the 3rd level on the mobile version and instead replace it with the ‘Back <' that is currently appearing on the 2nd level?
Thanks so much for your help,
Rebecca
Could you provide your website url, to take a look at the elements in the 3 level navigation ?