如何使用 DRY 呈现一个活动项的菜单



我想渲染一个这样的结构:

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

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

例如,它可以是一个允许这样做的标签。

想出了另一种方法来做到这一点,由于这个答案,足够优雅: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>

等。

这样,url

可以更改,并且如果 url 参数发生变化,它仍然有效,并且您无需在其他地方保留菜单项列表。

使用模板标签

您可以简单地使用以下模板标签:

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

所以,在你的模板中:

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

还有另一种方法,仅使用HTML和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; }

我找到了简单而优雅的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' %}
您可以使用

名称、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在每个模板中都可用。

我想出了一种方法来利用包含菜单的父模板中的块标签来实现这样的事情。

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,依此类推。

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

这是我的解决方案:

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

假设导航项是与当前页面具有相同URL的链接,则可以使用JavaScript。下面是一个带注释的方法,我使用它在导航菜单中向li添加class="active" 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>

我今天遇到了这个挑战,如何动态激活侧边栏中的"类别"。这些类别具有来自数据库的 slug。

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

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

基于@vincent的回答,有一种更简单的方法可以在不弄乱 django url 模式的情况下做到这一点。

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

在下面的示例中,我使用 django-mptt 来呈现菜单,但可以将node.path替换为每个菜单项路径。

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

我正在使用一个更简单和纯粹的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的"子目录"通常是所选菜单项的子目录。

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

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

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

选项卡 1 模板:

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

选项卡 2 模板:

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

只需使用模板标签

# app/templatetags/cores.py
from django import template
from django.shortcuts import reverse
register = template.Library()
@register.simple_tag
def active(request, url, classname):
    if request.path == reverse(url):
        return classname
    return ""

在你的模板中这样做

{% load cores %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Example</title>
</head>
<body>
    <div>
        <a href="{% url 'myUrl' %}" class="{% active request 'myUrl' 'activeClass' %}">myUrl</a>
    </div>
</body>
</html>

玩得愉快 😎

最新更新