Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dry way to apply current class to navigation elements in django templates

Say there are navigations, nav1, nav2, nav3 and many subnavs within each of them.

For the css effect, you need to apply class=current to the currently selected navs.

What is the dry way to do that.

For the subnavs, you can check to see if request.get_full_path is the same as the url that subnav refers to, in the base template.

How can you make it dry for the navigation too.

like image 341
lprsd Avatar asked May 26 '10 12:05

lprsd


2 Answers

If you've organized your project into a base.html template that is extended by other templates, e.g. appname/pagename.html, you can use a template-centric approach to highlighting the active navigation element.

This approach gives you some decoupling advantages, which I've noted in detail at the end of this answer.

I've found this approach to be very useful for handling broad navigation items that remain the same across most or all of a site. It is probably not an appropriate solution for more detailed navigation elements, such as rendering a dynamic list of items gleaned from your data store.

In your base.html template, add a block to each navigation element, giving unique names to the blocks:

<ul class="nav">
  <li class="{% block navbar_class-home %}{% endblock %}">
    <a href="#">Home</a>
  </li>
  <li class="{% block navbar_class-about %}{% endblock %}">
    <a href="#">About</a>
  </li>
  <li class="{% block navbar_class-pricing %}{% endblock %}">
    <a href="#">Pricing</a>
  </li>
</ul>

In your appname/pagename.html template, if you want one of the nav elements to appear active, override the appropriate block using active as the content. For example, to highlight the "About" item:

{% block navbar_class-about %} current {% endblock %}

When you use a view that renders that template, it'll render like this:

<ul class="nav">
  <li class="">
    <a href="#">Home</a>
  </li>
  <li class=" current ">
    <a href="#">About</a>
  </li>
  <li class="">
    <a href="#">Pricing</a>
  </li>
</ul>

This provides an initial rendering that doesn't rely on JavaScript. (You can modify the nav bar classes in place using JavaScript if you're doing a single-page app.)

For many (but not all) cases, this can be a better separation presentation from view logic:

  • You can modify views to attach site-navigation data to the template context, but doing so strongly couples the presentation to the view layer, and makes it more difficult to create reusable apps or to integrate third-party apps.

    The view is already selecting a named template, which means that you are already passing some navigation-related information to the template layer. That may be all you need.

  • You can use a template context processor to get some information about the view, but this just moves the strong coupling to another layer of the system, rather than staying within the template layer.

like image 108
gldnspud Avatar answered Sep 24 '22 15:09

gldnspud


In your approach, where you add a class to denote the active nav element to the element itself, you can add the name of the active nav element into the context from your view. Then, use some javascript to add the current class to the appropriate element.

For example, if you have the following nav elements:

<a id="home" href="#">Home</a>
<a id="about" href="#">About</a>

In your view, add a context variable current to denote the id of the current nav element by id:

return render_to_response('template.html',
                          {'current': 'home'})

Then in javascript (jQuery shown here):

$("#{{ current }}").addClass('current')

This will be evaluated as:

$("#home").addClass('current')

Which will in turn apply your #current CSS to the home element.

Another approach is to add a class to the body tag which identifies the currently active nav menu. Then in your css, you define styles for each body class which highlight the appropriate nav item. Then, your template inserts the current variable directly into the body class list, and there is no need for javascript.

like image 40
Chris Lawlor Avatar answered Sep 22 '22 15:09

Chris Lawlor