How to render menu with one active item with DRY?

I would like to render a constructions like:

<a href='/home'>Home</a>
<span class='active'>Community</span>
<a href='/about'>About</a>

Where Community is selected menu item. I have menu with same options for several templates but I would not like to create combinations for each template:

<!-- for Home template-->
        <span class='active'>Home</span>
        <a href='/comminuty'>Community</a>
        <a href='/about'>About</a>
    ...
<!-- for Community template-->
        <a href='/home'>Home</a>
        <span class='active'>Community</span>
        <a href='/about'>About</a>
    ...
<!-- for About template-->
        <a href='/home'>Home</a>
        <a href='/community'>Community</a>
        <span class='active'>About</span>

We have permanent list of menu items, so, it can be more effective way - to create only one generalized structure of menu then render menu with required option for template.

For example it could be a tag that allows to do that.


Solution 1:

Figured out another way to do it, elegant enough thanks to this answer : https://stackoverflow.com/a/17614086/34871

Given an url pattern such as:

url(r'^some-url', "myapp.myview", name='my_view_name'),

my_view_name is available to the template through request ( remember you need to use a RequestContext - which is implicit when using render_to_response )

Then menu items may look like :

<li class="{% if request.resolver_match.url_name == "my_view_name" %}active{% endif %}"><a href="{% url "my_view_name" %}">Shortcut1</a></li>
<li class="{% if request.resolver_match.url_name == "my_view_name2" %}active{% endif %}"><a href="{% url "my_view_name2" %}">Shortcut2</a></li>

etc.

This way, the url can change and it still works if url parameters vary, and you don't need to keep a list of menu items elsewhere.

Solution 2:

Using template tag

You can simply use the following template tag:

# path/to/templatetags/mytags.py
import re

from django import template
try:
    from django.urls import reverse, NoReverseMatch
except ImportError:
    from django.core.urlresolvers import reverse, NoReverseMatch

register = template.Library()


@register.simple_tag(takes_context=True)
def active(context, pattern_or_urlname):
    try:
        pattern = '^' + reverse(pattern_or_urlname)
    except NoReverseMatch:
        pattern = pattern_or_urlname
    path = context['request'].path
    if re.search(pattern, path):
        return 'active'
    return ''

So, in you your template:

{% load mytags %}
<nav><ul>
  <li class="nav-home {% active 'url-name' %}"><a href="#">Home</a></li>
  <li class="nav-blog {% active '^/regex/' %}"><a href="#">Blog</a></li>
</ul></nav>

Using only HTML & CSS

There is another approach, using only HTML & CSS, that you can use in any framework or static sites.

Considering you have a navigation menu like this one:

<nav><ul>
  <li class="nav-home"><a href="#">Home</a></li>
  <li class="nav-blog"><a href="#">Blog</a></li>
  <li class="nav-contact"><a href="#">Contact</a></li>
</ul></nav>

Create some base templates, one for each session of your site, as for example:

home.html
base_blog.html
base_contact.html

All these templates extending base.html with a block "section", as for example:

...
<body id="{% block section %}section-generic{% endblock %}">
...

Then, taking the base_blog.html as example, you must have the following:

{% extends "base.html" %}
{% block section %}section-blog{% endblock %}

Now it is easy to define the actived menu item using CSS only:

#section-home .nav-home,
  #section-blog .nav-blog,
  #section-contact .nav-contact { background-color: #ccc; }

Solution 3:

I found easy and elegant DRY solution.

It's the snippet: http://djangosnippets.org/snippets/2421/

**Placed in templates/includes/tabs.html**

<ul class="tab-menu">
    <li class="{% if active_tab == 'tab1' %} active{% endif %}"><a href="#">Tab 1</a></li>
    <li class="{% if active_tab == 'tab2' %} active{% endif %}"><a href="#">Tab 2</a></li>
    <li class="{% if active_tab == 'tab3' %} active{% endif %}"><a href="#">Tab 3</a></li>
</ul>

**Placed in your page template**

{% include "includes/tabs.html" with active_tab='tab1' %}