How to render menu with one active item with DRY?
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.
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' %}
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; }
You could make a context variable links
with the name, URL and whether it's an active item:
{% for name, url, active in links %}
{% if active %}
<span class='active'>{{ name }}</span>
{% else %}
<a href='{{ url }}'>{{ name }}</a>
{% endif %}
{% endfor %}
If this menu is present on all pages, you could use a context processor:
def menu_links(request):
links = []
# write code here to construct links
return { 'links': links }
Then, in your settings file, add that function to TEMPLATE_CONTEXT_PROCESSORS
as follows: path.to.where.that.function.is.located.menu_links
. This means the function menu_links
will be called for every template and that means the variable links
is available in each template.