2

I'm having a problem with the side nav on my site (desktop view): https://www.teamwinstudios.com

I want the active class on the side nav to inherit the active class as soon as (or closer to) the relevant page comes into view as the user is scrolling down. So for example, if you scroll down you'll notice the side nav changes from 'Home' to 'Featured Work' at around the middle of the image grid of the 'Featured Work' page. What I want is for this change to happen as the 'Featured Works' title comes into view. The active class needs to change earlier.

This is the relevant html for the side nav:

    <nav class="Index-nav">
      <div class="Index-nav-inner">

          <a href="#home2" class="Index-nav-item active">
            <div class="Index-nav-indicator"></div>
            <div class="Index-nav-text"><span>Home</span></div>
          </a>

          <a href="#recent-work" class="Index-nav-item">
            <div class="Index-nav-indicator"></div>
            <div class="Index-nav-text"><span>Featured Work</span></div>
          </a>

          <a href="#team-win" class="Index-nav-item">
            <div class="Index-nav-indicator"></div>
            <div class="Index-nav-text"><span>Who We Are</span></div>
          </a>

          <a href="#services" class="Index-nav-item">
            <div class="Index-nav-indicator"></div>
            <div class="Index-nav-text"><span>What We Do</span></div>
          </a>

          <a href="#contact-1" class="Index-nav-item">
            <div class="Index-nav-indicator"></div>
            <div class="Index-nav-text"><span>Contact</span></div>
          </a>

      </div>
    </nav>

I've used jQuery below to fade out the active class:

<script>
    $(document).ready(function() {
    $(document).scroll(function() {
    var y = $(this).scrollTop();
    if (y > 400) {
        $('.Index-nav-item.active, .Index-nav-text').fadeOut();
    } else {
        $('.Index-nav-item.active, .Index-nav-text').fadeIn();
    }
});
});      
</script>    

I'm stuck now targeting the the next item from index-nav-text to fade in and inherit the class active; w This is what I've tried:

<script>
    $(document).ready(function() {
    $(document).scroll(function() {
    var y = $(this).scrollTop();
    if (y > 400) {
        $('.Index-nav-item.active, .Index-nav-text').fadeOut().removeClass('.active');
    } else if (y > 450) {
        $('.Index-nav-text:nth-of-type(2)').addClass('.active').fadeIn();
    } else {
        $('.Index-nav-item.active, .Index-nav-text').fadeIn();
    }
});
});      
</script>    

I'm pretty new to JS so I'm not sure if I'm going about it the best way, I can't target the next nav item and give it the class necessary at the specified height. If there's a better solution I'm all ears. Thanks for your help!

MjBVala
  • 137
  • 3
  • 9

1 Answers1

1

This is a great question, one which I'm surprised I haven't seen asked before. In order to achieve what you're after, I think it is actually very complex.

Squarespace's own code is handling the index navigation, and its methods aren't accessible to be modified using our own out-of-scope code injection. Therefore one must first short-circuit the default controller for this feature in order to stop it from working, then create a new controller that behaves desirably.

Interestingly, Squarespace's own controller doesn't take into account which direction the user is scrolling. This is important, because if a user is scrolling back up, you want the index navigation to update as sections are coming "back down" the other direction. This is also critical for your desired outcome.

So, using Squarespace's own controller code, modifying it to:

  • include necessary imports from other controllers and such
  • stop the default controller from working
  • take into account scroll direction, detect the 'active' section from the bottom when scrolling down, and the top when scrolling up
  • add a small buffer to the top/bottom intersection points so that sections don't trigger until they are adequately in view
  • include some other misc. changes to avoid having to include imported gallery-related libraries

...and then compiling the code to es2015 and minifying it, the following seems to work for me on your site, included via sitewide footer code injection:

<script>
/**
 * Custom Squarespace index navigation controller. Ref: https://stackoverflow.com/questions/58046642/
 */
"use strict";window.Squarespace.onInitialize(Y,function(){!function(){var e=document.body;if(!e.classList.contains("Index--empty")){var t=document.getElementsByClassName("Index-nav-inner")[0];t.innerHTML=t.innerHTML;var n,o=window.pageYOffset||document.body.scrollTop,i={},r=[],a=function(){var e=window.pageYOffset,t=!1,o=!1,i=function(){o=!1,r.forEach(function(t){var n=t.fn;"scroll"===t.type&&n(e)})};return function(){!1===t&&(t=!0,r.forEach(function(e){var t=e.fn;"start"===e.type&&t()})),e=window.pageYOffset,o||window.requestAnimationFrame(i),o=!0,n&&clearTimeout(n),n=setTimeout(function(){t=!1,r.forEach(function(e){var t=e.fn;"end"===e.type&&t()})},100)}},c=function(){clearTimeout(n)};!function(){var e=a();window.addEventListener("scroll",e),window.addEventListener("mercury:unload",c)}();var s,u,f,l,d,w,v,m=window.innerHeight,g=Array.from(e.querySelectorAll(".Index-page, .Index-gallery")),p=g.reduce(function(e,t){return e[t.getAttribute("id")]=t,e},{}),y=e.querySelector(".Index-nav"),h=Array.from(y.querySelectorAll(".Index-nav-item")),b=h.reduce(function(e,t){return e[t.getAttribute("href")]=t,e},{}),L=h.filter(function(e){return e.classList.contains("active")})[0],E=L.getAttribute("href").substring(1),T=null,A=function(){return g.reduce(function(e,t){var n=function(e){var t=e.getAttribute("id");if(i[t])return i[t];var n=document.documentElement.scrollTop||document.body.scrollTop,o=e.getBoundingClientRect();return i[t]={top:Math.floor(o.top+n),right:o.right,bottom:Math.ceil(o.bottom+n),left:o.left,width:e.offsetWidth,height:e.offsetHeight+2},i[t]}(t),o=n.top,r=n.bottom,a=n.left,c=n.right;return e[t.getAttribute("id")]={top:o,bottom:r,left:a,right:c},e},{})},O=function(e){var t,n,i=e+m/2,r=(n=window.pageYOffset||document.documentElement.scrollTop,t=n>o?1:n<o?-1:0,o=n<=0?0:n,t),a=window.pageYOffset+window.innerHeight;r&&Object.keys(s).forEach(function(t){var n,o,c,f=s[t],l=f.top,d=f.bottom;if(E!==t&&(1===r?a>=l+300&&a<d:e<=d-500&&e>l)){var w="#"+t;L.classList.remove("active");var v=b[w];v.classList.add("active"),E=t,L=v}if(u)y.classList.remove("overlay");else if(T!==t&&i>=l&&i<d){var m=p[t];y.classList.toggle("overlay",(o=(n=m).classList.contains("Index-gallery"),c=n.classList.contains("Index-page--has-image"),o||c)),T=t}})};return f=(s=A())[Object.keys(s)[0]].left,l=window.Static.SQUARESPACE_CONTEXT.tweakJSON["tweak-index-nav-position"].toLowerCase(),d=parseFloat(window.getComputedStyle(y)[l]),u=f>d,O(window.pageYOffset),function(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100,o=function(){clearTimeout(t),t=setTimeout(function(){e()},n)};window.addEventListener("resize",o),window.addEventListener("mercury:unload",function(){window.removeEventListener("resize",o)})}(function(){i={},m=window.innerHeight,s=A()}),w="scroll",v=O,r.push({type:w,fn:v}),{destroy:function(){var e,t;e="scroll",t=O,r.some(function(n,o){return!(n.type!==e||n.fn!==t||(r.splice(o,1),0))})}}}}()});
</script>
Brandon
  • 3,572
  • 2
  • 12
  • 27
  • Thank you so much, this is an amazing answer. I didn't realize It would get this complicated; didn't even take into account scrolling back up. I'll be digging through this for a while! – MjBVala Sep 24 '19 at 20:20