如何使用DRY渲染带有一个活动项目的菜单?

时间:2012-03-20 19:28:30

标签: django django-templates

我想渲染一些结构:

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

选择社区菜单项。我有几个模板相同选项的菜单,但我不想为每个模板创建组合:

<!-- 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>

我们有永久的菜单项列表,因此,它可以是更有效的方式 - 只创建一个菜单的通用结构,然后使用模板的必需选项呈现菜单。

例如,它可能是允许这样做的标记。

11 个答案:

答案 0 :(得分:62)

想出另一种方法,这要归功于这个答案:https://stackoverflow.com/a/17614086/34871

给出一个url模式,例如:

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

my_view_name可通过request用于模板(请记住,您需要使用RequestContext - 使用render_to_response时隐式)

然后菜单项可能如下所示:

<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>

这样,网址可以更改,如果网址参数不同,它仍然可以使用,而且您不需要在其他位置保留菜单项列表。

答案 1 :(得分:48)

使用模板标记

您只需使用以下模板标记:

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

from django import template
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 ''

所以,在你的模板中:

{% 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>

仅使用HTML&amp; CSS

还有另一种方法,仅使用HTML&amp; CSS,您可以在任何框架或静态站点中使用。

考虑到你有一个像这样的导航菜单:

<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>

创建一些基本模板,每个模板用于您网站的每个会话,例如:

home.html
base_blog.html
base_contact.html

所有这些模板都使用“部分”扩展base.html,例如:

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

然后,以base_blog.html为例,您必须具备以下条件:

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

现在只使用CSS定义活动菜单项很容易:

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

答案 2 :(得分:29)

我找到了简单而优雅的DRY解决方案。

这是片段: 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' %}

答案 3 :(得分:4)

您可以使用名称,URL以及它是否为活动项来创建上下文变量links

{% for name, url, active in links %}
    {% if active %}
<span class='active'>{{ name }}</span>
    {% else %}
<a href='{{ url }}'>{{ name }}</a>
    {% endif %}
{% endfor %}

如果所有页面上都有此菜单,您可以使用上下文处理器:

def menu_links(request):
    links = []
    # write code here to construct links
    return { 'links': links }

然后,在您的设置文件中,将该功能添加到TEMPLATE_CONTEXT_PROCESSORS,如下所示:path.to.where.that.function.is.located.menu_links。这意味着将为每个模板调用函数menu_links,这意味着变量links在每个模板中都可用。

答案 4 :(得分:1)

假设导航项是与当前页面具有相同URL的链接,则可以使用JavaScript。这是一个带注释的方法,用于在class="active"的导航菜单中将li添加到class="nav"

// Get the path name (the part directly after the URL) and append a trailing slash
// For example, 'http://www.example.com/subpage1/sub-subpage/' 
//     would become '/subpage1/'
var pathName = '/' + window.location.pathname.split('/')[1];
if ( pathName != '/' ) { pathName = pathName + '/'; }

// Form the rest of the URL, so that we now have 'http://www.example.com/subpage1/'
// This returns a top-level nav item
var url = window.location.protocol + '//' +
          window.location.host +
          pathName;
console.log(url);

// Add an 'active' class to the navigation list item that contains this url
var $links = document.querySelectorAll('.nav a');
$link = Array.prototype.filter.call( $links, function(el) {
    return el.href === url;
})[0];
$link.parentNode.className += ' active';

此方法意味着您只需将其弹出到基本模板中一次就可以忘掉它。没有重复,也没有在每个模板中手动指定页面URL。

一个警告:这显然只有在找到的url与导航链接href匹配时才有效。另外可以在JS中指定几个特殊用例,或根据需要定位不同的父元素。

这是一个可运行的示例(请记住,在StackSnippets上运行代码段):

// Get the path name (the part directly after the URL) and append a trailing slash
// For example, 'http://www.example.com/subpage1/sub-subpage/' 
//     would become '/subpage1/'
var pathName = '/' + window.location.pathname.split('/')[1];
if ( pathName != '/' ) { pathName = pathName + '/'; }

// Form the rest of the URL, so that we now have 'http://www.example.com/subpage1/'
// This returns a top-level nav item
var url = window.location.protocol + '//' +
          window.location.host +
          pathName;
console.log(url);

// Add an 'active' class to the navigation list item that contains this url
var $links = document.querySelectorAll('.nav a');
$link = Array.prototype.filter.call( $links, function(el) {
    return el.href === url;
})[0];
$link.parentNode.className += ' active';
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: black;
  text-decoration: none;
}
.active a {
  color: red;
}
<ul class="nav">
  <li>
    <a href="http://example.com/">Example Link</a>
  </li>
  <li>
    <a href="http://stacksnippets.net/js/">This Snippet</a>
  </li>
  <li>
    <a href="https://google.com/">Google</a>
  </li>
  <li>
    <a href="http://stackoverflow.com/">StackOverflow</a>
  </li>
</ul>

答案 5 :(得分:1)

这是我的解决方法:

{% url 'module:list' as list_url %}
{% url 'module:create' as create_url %}

<ul>
    <li><a href="{% url 'module:list' %}" class="{% if request.path == list_url %}active{% endif %}">List Page</a></li>
    <li><a href="{% url 'module:create' %}" class="{% if request.path == create_url %}active{% endif %}">Creation Page</a></li>
</ul>

答案 6 :(得分:0)

根据@ vincent的回答,有一种更简单的方法可以做到这一点,而不会搞乱django url模式。

可以根据渲染的菜单项路径检查当前请求路径,如果它们匹配,那么这是活动项。

在下面的示例中,我使用django-mptt渲染菜单,但可以用每个菜单项路径替换node.path

<li class="{% if node.path == request.path %}active{% endif %}">
    <a href="node.path">node.title</a>
</li>

答案 7 :(得分:0)

我今天遇到了如何动态激活&#34;类别&#34;在侧边栏中。这些类别有来自DB的slu。

我通过检查类别slug是否在当前路径中来解决它。 slu is是独一无二的(标准做法)所以我认为这应该没有任何冲突。

{% if category.slug in request.path %}active{% endif %}

获取类别并激活当前类别的循环的完整示例代码。

{% for category in categories %}
<a class="list-group-item {% if category.slug in request.path %}active{% endif %}" href="{% url 'help:category_index' category.slug %}">
  <span class="badge">{{ category.article_set.count }}</span>
  {{ category.title }}
</a>
{% endfor %}

答案 8 :(得分:0)

我正在使用更简单纯粹的CSS解决方案。它有其局限性,我知道并可以使用它,但它避免了笨拙的CSS类选择器,如下所示:

<a href="index.html" class="item{% if url == request.path %}active{% endif %}">index</a>

由于缺少active之前的空格字符,因此类选择器会被调用itemactive而不是item active,而这并不是那么容易出错。

对我来说,这个纯CSS解决方案效果更好:

a.item /* all menu items are of this class */
{
    color: black;
    text-decoration: none;
}

a.item[href~="{{ request.path }}"] /* just the one which is selected matches */
{
    color: red;
    text-decoration: underline;
}

注意:如果URL有其他路径组件,这甚至有效,因为href也部分匹配。这可能最终导致与多个匹配的“冲突”,但通常它只是起作用,因为在结构良好的网站上,URL的“子目录”通常是所选菜单项的子目录。

答案 9 :(得分:0)

我想出了一种方法,在包含菜单的父模板中使用块标签来实现这样的效果。

base.html - 父模板:

<a href="/" class="{% block menu_home_class %}{% endblock %}">Home</a>
<a href="/about" class="{% block menu_about_class %}{% endblock %}">About</a>
<a href="/contact" class="{% block menu_contact_class %}{% endblock %}">Contact</a>

{% block content %}{% endblock %}

about.html - 特定网页的模板:

{% extends "base.html" %}

{% block menu_about_class %}active{% endblock %}

{% block content %}
    About page content...
{% endblock %}

如您所见,不同页面模板之间的差异是包含active的块的名称。 contact.html会使用menu_contact_class,依此类推。

这种方法的一个好处是,您可以拥有多个具有相同活动菜单项的子页面。例如,about页面可能有子页面,提供有关公司每个团队成员的信息。对于每个子页面,“关于”菜单项保持活动状态是有意义的。

答案 10 :(得分:0)

我个人认为最简单的方法是为每个链接创建块,如下所示:

    # base.py
...
<a href="{% url 'home:index' %}" class={% block tab1_active %}{% endblock %}>
...
<a href="{% url 'home:form' %}" class={% block tab2_active %}{% endblock %}>
...

然后在每个相对模板中将该链接声明为“活动”,例如:

tab1模板:

{% block tab1_active %}"active"{% endblock %}

tab2模板:

{% block tab2_active %}"active"{% endblock %}