2

I'm currently setting up a Structure nav and am trying to set it up so that child pages only display if;

1) The child page itself (or it's sibling) is currently active 2) The parent page of that child is currently active

At the moment all I can get working is having the child pages display regardless of what page is active. Even if a page has no children, the child pages are still being shown beneath another parent page, whereas ideally those child pages would not be seen until those pages are become active.

{% set pages = craft.entries.section('about').level('1') %}

<ul class="side-nav p-0">
{% for page in pages %}
{% set activePage = (entry.slug == craft.request.segment(2)) %}
<li class="{% if activePage %} active{% endif %}">
  <a href="{{page.url}}">{{ page.title }}&nbsp;</a>
  {% if activePage %}
    {% set subPage = craft.entries.descendantOf(page) %}
    <ul class="p-0 pl-5">
      {% for child in subPage %}
        <li><a href="{{child.url}}">{{child.title}}</a></li>
      {% endfor %}
    </ul>
   {% endif %}
 </li>
{% endfor %}
</ul>

Is there any solution out there for this as I feel like I maybe close to getting it working. Can anybody shed any light on this?

1 Answer 1

3

I hope I have understand your problem..

Let's look at your code:

One sidenote: It might be good to limit your pages-selection only to active pages:

{% set pages = craft.entries.section('about').level(1).status('live') %}

First of all, store the elements of the slug in an array:

{% set slugs = craft.request.segments %}

Here is the first for-loop for the first-level elements:

<ul class="side-nav p-0">
{% for page in pages %}
    {% set activePage = (page.slug in slugs) %}
    <li class="{% if activePage %} active{% endif %}">
        <a href="{{page.url}}">{{ page.title }}&nbsp;</a>
 </li>
{% endfor %}
</ul>

The important part here is to check whether the slug of the page in the for-loop is active. In your code, you checked the slug of the entry (of the currently active page, not the page in the loop) with the slug of the request. This will always yield a true result, which is what you experienced.

Using the code above you will by now receive a list of all first-level pages, the active one with labelled with class="active".

Now let's look into the second loop for the subpages.

{% set activeSubPage = (entry.level == 2 and entry.getParent().slug == page.slug) %}
{% if activePage or activeSubPage %}
    {% set subPage = craft.entries.descendantOf(page) %}
    <ul class="p-0 pl-5">
        {% for child in subPage %}
            <li><a href="{{child.url}}">{{child.title}}</a></li>
        {% endfor %}
    </ul>
{% endif %}

This time it is not enough to check whether the slug of the active page in the loop matches the slug of the actual page (this will show children only if the parent page is selected), we also need to check whether the parent's slug matches the active page (to show children even if the active page is one of the children itself and not the parent).

With activeSubPage we check at first, whether the page is level 2 (therefore it has a parent and we won't run into an error for an undefined attribute on pages that are level 1 and do not have a parent) and if so, whether the parent's slug is equal to the slug of the current page in the loop.

We enclose the second for-loop with an if-clause that checks both of the results, whether the current item in the loop is either activePage or activeSubPage. The remaining code is identical to your code.

You could now check again, whether the sub-item is active, by using the same principle: {% if (child.slug in slugs) %}active{% endif %}

Here is the complete updated code:

{% set pages = craft.entries.section('about').level(1).status('live') %}
{% set slugs = craft.request.segments %}

<ul class="side-nav p-0">
{% for page in pages %}
    {% set activePage = (page.slug in slugs) %}
    <li class="{% if activePage %} active{% endif %}">
        <a href="{{page.url}}">{{ page.title }}&nbsp;</a>
        {% set activeSubPage = (entry.level == 2 and entry.getParent().slug == page.slug) %}
        {% if activePage or activeSubPage %}
            {% set subPage = craft.entries.descendantOf(page) %}
            <ul class="p-0 pl-5">
                {% for child in subPage %}
                    <li><a href="{{child.url}}">{{child.title}}</a></li>
                {% endfor %}
            </ul>
        {% endif %}
 </li>
{% endfor %}
</ul>

I hope this helps and solves your problem. If you have any questions or if you do not understand my explanation (sorry for my bad English, I'm not a native speaker): Don't hesitate to ask!

Further improvements:

With some small improvements you could put this code into a file and use it recursively for navigations with more than two levels. In your template, you would then be able to include it like this

{% include "_includes/nav_links_withchildren" with { 'slug' : 'about' } %}

and would then include itself again to list sub-pages.

4
  • Thats exactly what I was looking for. Thank you for taking the time to form a well structured and understandable answer. I've learned something there and it works great! With regards to the improvements. Are you saying that I could drop an include containing this code and craft would pull the slug and then display the correct structure? I say this because I do have other Structures like this, but with different slugs obviously
    – Adam
    Commented Dec 18, 2018 at 15:17
  • I am glad I could help. With regard to your question yes: You can put this code as it is into a file and include it. By the use of the with-part, you can also define a variable, let's call it "slug". You would now have to replace the about with slug and it should work. However, to use it recursively for more than the two layers, you would have to modify the code a little bit. I hope this helps.
    – Luke
    Commented Dec 18, 2018 at 17:58
  • That's great, thank you. I will give that a try. Another thing I wanted to ask is if a level 2 child page is active, is there a way to add a class of active to the parent <li> of that child page too?
    – Adam
    Commented Dec 18, 2018 at 18:39
  • It is already in there.. you can use the same if-clause that checks for subpages to do that.
    – Luke
    Commented Dec 18, 2018 at 22:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.