A Django template tag for the current active page

An important part of a usable web site is a well-designed navigation bar. Something that any navigation bar should have is an indication of where the user is. Common practices for this include using a bold face for the link, a different color or adding a small icon next to the current section link. To do that, you could include your navigation code in every page and use an “active” class name on the a element to distinguish it from the other links.


<div id="navigation">
    <a href="/">Home</a>
    <a href="/services/">Services</a>
    <a class="active" href="/contact/">Contact</a>
</div>

Writing this in every template quickly becomes a pain in the neck. With Django, an easy solution would be to move this section to a different file, _nav.html for instance, and include it in every page. Another, even better way, would be to make sure that every template extends a base template (a very common practice with Django) and to add the navigation to that base template.

We face a little problem however: how do we know which page we’re on? If you use the render_to_response shortcut, you can give an extra keyword argument, context_instance. If you use the RequestContext class, you can pass more data to the template (the generic views use RequestContext by default, so you don’t have to worry about them.) Here’s how to do it:


## settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
    'django.core.context_processors.request',
)


## views.py
from django.template import ContextRequest

def home(request):
    return render_to_request('home.html', {}, context_instance=RequestContext(request))

You can now use the request template variable in home.html and its parent templates, which should include your base template. The HttpRequest object has an attribute, path, which contains the current URL (starting after the host name). We can now add the active class to the link for the current section:


<div id="navigation">
    <a class="{% ifequal request.path "/" %}active{% endifequal %}" href="/">Home</a>
    <a class="{% ifequal request.path "/services/" %}active{% endifequal %}" href="/services/">Services</a>
    <a class="{% ifequal request.path "/contact/" %}active{% endifequal %}" href="/contact/">Contact</a>
</div>

This code has two problems: (1) it’s really ugly (it’s probably all that should matter to make you want to rework this code), and (2) imagine that there are subsections under the services section, /services/web-hosting/, /services/tech-support/, etc. When you’re under one of those subsections, the link to the services section will not be highlighted. We’ll fix that by using a custom template tag that will check if request.path begins with a given string.


## tags.py
@register.simple_tag
def active(request, pattern):
    if request.path.startswith(pattern):
        return 'active'
    return ''


<!-- navigation -->

{% load tags %}
<div id="navigation">
    <a class="{% active request "/" %}" href="/">Home</a>
    <a class="{% active request "/services/" %}" href="/services/">Services</a>
    <a class="{% active request "/contact/" %}" href="/contact/">Contact</a>
</div>

Ah, the code is much nicer and now if we go to /services/web-hosting/, the services link will still be highlighted. Another link will be highlighted unfortunately: the home link will ALWAYS be highlighted, because every path begins with a slash. Let’s change the implementation of the active tag slightly to use regular expressions and we’ll also change the calls to {% active %}


## tags.py
@register.simple_tag
def active(request, pattern):
    import re
    if re.search(pattern, request.path):
        return 'active'
    return ''


<!-- navigation -->

{% load tags %}
<div id="navigation">
    <a class="{% active request "^/$" %}" href="/">Home</a>
    <a class="{% active request "^/services/" %}" href="/services/">Services</a>
    <a class="{% active request "^/contact/" %}" href="/contact/">Contact</a>
</div>

Nice! Now it’s how we want it. Home will be highlighted only when we’re viewing the / URL, the subsections still work and the code is still pretty cute.

About these ads

101 thoughts on “A Django template tag for the current active page

  1. Thats a neat solution. I just worked around the same problem just this morning by writing a custom middleware (to intercept process_view), a context processor func and a decorator to decorate every view func. Your solution is a whole lot simpler and elegant.

  2. Pingback: Common web features in Django at The Best of Marc Garcia

  3. Pingback: The new and improved {% active %} tag « Occasionally sane

  4. This is very nice for my main navigation.

    But what about the Side navigation that keeps track of active liknks like:
    /events/1
    /events/2

    where each links is active depending on the current event beign displayed.

    I can’t really do {% active request “/events/” + event.id %} here :-(

  5. Thanks for an excellent and simple example!
    My tags.py file ended up looking like this:

    from django import template
    register = template.Library()
    @register.simple_tag
    def active(request, pattern):
    import re
    if re.search(pattern, request.path):
    return ‘active’
    return ”

  6. Thanks, this was almost exactly what I was planning on writing. You not only you saved me the time but also made a more flexible version by using regular expressions.

  7. Can you tell me, where do you put the code? Does it belong in the view.py file? If it belongs in its own file, where does that file go?

    Thank you very much.

  8. Pingback: django template tag for active CSS class « 110j

  9. This is a good implementation. The only problem I see is that the url pattern is coupled with the template page. To decouple it you can use url names in the urls.py and variables in the template page. I have a post on my blog on how to do the decoupling, it based on your article obviously.

    110j.wordpress.com/2009/01/25/django-template-tag-for-active-class

  10. This is a great solution. I haven’t seen anything similar posted anywhere else.

    Some argue that this is a job for base_* and some CSS but I disagree. What happens when you are using a mix of normal views and flatpages? regex is at the heart of this tag which makes it so much more flexible than most other solutions.

    Thanks!

  11. Just for the record, I’m using Django 1.0.2 and it seems that ‘request’ is available by default in every template, so there’s no need to do the ‘RequestContext’ thing.

  12. Thanks for a good post, but I found some ways to imporve further. It would be much better to use the reverse method to get the urls that you need and instead use the path of the methods.

    #tags.py:
    from django import template
    from string import Template
    from django.core.urlresolvers import reverse
    from django.utils.translation import ugettext_lazy as _

    register = template.Library()

    @register.simple_tag
    def active(request, url, title):
    s = Template(‘$title‘)
    import re
    print reverse(url), request.path
    #if re.search(reverse(url), request.path):
    if reverse(url) == request.path:
    active = ‘active’
    else:
    active = ”
    return s.substitute(active=active, link=reverse(url), title=unicode(_(title)))

    #in the template
    {% active request “mmp.form.views.default” “Home” %}

    This tag gets the right url from urls.py, so if you change it, you just have to change it in urls.py and nowhere else. The tag also uses djangos methods for translation at the same time.

    Cheers!

  13. I found this extremly useful, thanx
    my variation:

    from django.template import Library
    from django.core.urlresolvers import reverse
    import re

    register = Library()

    @register.simple_tag
    def active(request, pattern, return_value):
    import re
    if re.search(pattern, request.path):
    return return_value
    return “

    @register.simple_tag
    def reverse_active(request, url, return_value):

    print reverse(url), request.path
    if reverse(url) == request.path:
    return return_value
    return “

    # template
    class=”top-menu-element{% reverse_active request “catalog.views.browse” ” top-menu-element-active” %}”

    class=”top-menu-element{% active request “/help/” ” top-menu-element-active” %}”

  14. I came up with a much simpler solution for my site.

    Inheriting templates can mark the active page by defining the corresponding nav* style, e.g. .navHome {background-color: #4A91C3;} Otherwise, .navHome is not defined and the link is not marked as active.

    Home
    Users
    Billing

  15. Once again:

    <div class=”navigation”>
    <a class=”navHome” href=”home”>Home</a>
    <a class=”navUsers” href=”users”>Users</a>
    <a class=”navBilling” href=”billing”>Billing</a>
    </div>

  16. If anyone having troubles with missing “request”, it’s not this code’s fault, it’s yours :D
    Spent 10mins figuring it out:

    In every request on views.py, never forget:

    context_instance=RequestContext(request)
    as last argument

    Even in render_to_response :D

    Thanks for this idea for tab switching

  17. @Carlos “In every request …” So much for the DRY principle. I think “in every” is the keyword for boilerplate code. There must be a better solution!

  18. Pingback: emson… » Blog Archive » How To Set Up and Build a Django Web Site

  19. Pingback: Django request.path应用,以及自定义站点全局配置信息 | 中文Python

  20. This is a good but not generic solution, only a minimal one. There should be some middleware to keep track of the menu hierarchy and apps should be able to expose and register their (sub)menus.

  21. Pingback: Как получить url представления

  22. Here’s what I did that didn’t require any tags. In my base template I added classes to my menu using blocks:

    class=”{% block active_aboutus %}{% endblock %}”
    class=”{% block active_services %}{% endblock %}”

    In my inherited template I simply set that to have a value:

    {% block active_aboutus %}active{% endblock %}

    It works well and I don’t have to fiddle with URLs or tags.

  23. Just had a go at this but it appears that its not up to date with more recent versions of Django:

    >>> from django.template import ContextRequest
    Traceback (most recent call last):
    File “”, line 1, in
    ImportError: cannot import name ContextRequest

    I guess I’ll go google!

  24. Pingback: e newsletter templates

  25. Pingback: Acai Berry Review

  26. Pingback: UK Adult Webcams

  27. Brilliant snippet of code. Took care of deleting all the extra template nestings. Especially the transparant use of regex’s in the pattern is great.

    Regards,

    Gerard.

  28. Pingback: pirater msn

  29. Pingback: After Effects Templates Free

  30. Pingback: Creating base.html and improving the navigation bar. | Anthony Honstain SDE

  31. Pingback: Highlighting current active page in Django | Michał Ochman

  32. Pingback: Get django to check current URL and if contains a particular string, to add in a class - How-To Video

  33. Pingback: Wi Fi Horizon, use copoun inside

  34. An intriguing discussion is worth comment. I think that you should publish more about this issue, it might not be a taboo subject but typically people do not discuss these subjects. To the next! Kind regards!!

  35. Another problem you may have to pay then my opinion is
    that we visit to any place for the purpose of roaming in that city.
    But the big surprise is that depositors in Cypriot banks, a decision to introduce capital controls by Paphos Car Hire will mean at least a few times.

  36. An NHS Berkshire East spokesperson said:” Existing patients are prescribed four tablets a day. Also set for release under the EVO View 4 G moniker on Sprint in the summer as the Himalayan Farmacia On Line without the known Farmacia On Line side effects. Asexuals have pushed to get HSDD removed from the trial.

  37. Sleep apnea pillows offer ample neck & back assistance, even though men and women undergoing CPAP treatment make use of a mask which could cover
    the nose and mouth. The special pillow helps these using the CPAP
    mask by keeping the head from moving around and possibly interfering
    with the action from the mask. The cpap pillow is
    perfect not only since it can comfortably prop up the head but also because it may be used in tandem with any type of CPAP mask, making the treatment more comfortable and effective.

  38. GOSH I greatly like paleo diet… I’ve been on it for 6 months and have noticed such drastic positive changes…. I strongly recommend this toanyone I started off by watching this youtube vid for great recipes!!! watch this video trust me

  39. Greetings from California! I’m bored at work so I decided to browse your site on my iphone during lunch break. I really like the knowledge you present here and can’t wait to take a look when I get home.
    I’m surprised at how fast your blog loaded on my phone .. I’m not even using WIFI, just
    3G .. Anyhow, good blog!

  40. s truly really worth going back house and getting the credit card
    for purchasing that merchandise. Credit unions can often offer better rates and more alternatives than
    larger banks, because they base their decisions on the local economy instead of the national
    situation. Creditors analyze your score to determine how likely you will be to repay your debt.

  41. Online Bodybuilding Forum – Professional Anabolic Steroid Cycles
    Advice – Hardcore bodybuilder, creator of 2 steroids
    top selling publications, steroids and bodybuilding journal publisher.

    My Auntie used to say this to me a lot when I started going to the gym at 16.
    BSN NO-Xplode solves this annoying problem by upping your bodies creatine limit.

  42. Dental Problems won’t go away without attention Electric Toothbrushes verses Manual Toothbrushes Find out if you should floss once per day and brush your teeth once more with no need of a lot trouble. Laying down strict rules about bunk beds for your dog. There are some interesting Valentine’s Day trivia facts you may not be aware
    of.

  43. Thank you for any other fantastic article.
    Where else could anybody get that type of info in such an ideal manner of writing?

    I have a presentation next week, and I am at the look for such info.

  44. This is very interesting, You are a very
    skilled blogger. I’ve joined your rss feed and look forward to seeking more of your great post. Also, I’ve shared your web site in my social networks!

  45. I do believe all the ideas you have presented to your post.

    They are very convincing and can definitely work.
    Nonetheless, the posts are very short for beginners.
    Could you please extend them a little from next time? Thanks for the post.

  46. Pingback: Live Streaming Κάλυψη Συνεδρείων

  47. Pingback: [Django] Mettre un élément de navbar en « Active » | Scrat iPhone

  48. Ahaa, its pleasant dialogue concerning this article here at this weblog, I have read all that, so now me also commenting at
    this place.

  49. Farmville (FV) allows it’s players to plow, plant and harvest crops and animals.
    These sites not just allow you to play some of your favorite
    games but also allow downloading of Game cheats.

    You can use them to make your games more difficult or easier.

  50. Whoa! This blog looks just like my old one! It’s on a totally different subject but it has pretty much the same page layout
    and design. Excellent choice of colors!

  51. “/element/” using only the url works well
    “/element/3″ how do I make this work /1 /2 /etc?

    button type=”button” class=”btn {% if request.path == ‘/element/’ %}active{% endif %}”>element /button

  52. You really make it seem so easy with your presentation but I find this topic
    to be really something that I think I would never understand.

    It seems too complicated and very broad for me. I’m looking forward for your next post, I will
    try to get the hang of it!

  53. What i don’t realize is in truth how you are now not actually much more
    neatly-appreciated than you may be now. You’re so intelligent.
    You know therefore significantly in terms of this matter, produced me personally consider
    it from so many varied angles. Its like men and women don’t seem to be interested until it is one thing to
    do with Girl gaga! Your own stuffs excellent. At all times handle it up!

  54. I believe everything said made a lot of sense. But, what about
    this? what if you composed a catchier title? I am
    not saying your content isn’t good., however what if you added something
    to maybe grab a person’s attention? I mean A Django template tag for the current active page | Occasionally sane is kinda plain. You might look at Yahoo’s front
    page and watch how they create news headlines to grab viewers interested.
    You might add a related video or a picture or two to grab people excited about
    everything’ve got to say. In my opinion, it might make your website a little bit more
    interesting.

  55. I’m now not positive the place you are getting your info,
    however great topic. I must spend a while learning more
    or understanding more. Thanks for excellent info I used to be searching
    for this information for my mission.

  56. I believe what you posted was very logical. But, what about this?
    suppose you typed a catchier title? I mean, I don’t wish to tell you how to run your website, but suppose you added a
    title to maybe get folk’s attention? I mean A Django template tag for the current active page | Occasionally sane is kinda plain.
    You ought to look at Yahoo’s front page and watch how they create news headlines to grab viewers interested.
    You might add a video or a related picture or two to grab people
    interested about what you’ve got to say. In my
    opinion, it could make your blog a little livelier.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s