Как сделать меню с одним активным элементом с СУХОЙ?

Я хотел бы сделать такие конструкции, как:

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

У нас есть постоянный список пунктов меню, так что это может быть более эффективным способом - создать только одну обобщенную структуру меню, затем отобразить меню с необходимой опцией для шаблона.

Например, это может быть тег, который позволяет это сделать.

12 ответов

Решение

Я нашел простое и элегантное СУХОЕ решение.

Это фрагмент: 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' %}

Выяснил другой способ сделать это, достаточно элегантно благодаря этому ответу: /questions/3781755/how-to-get-the-current-url-name-using-django/3781775#3781775

Учитывая шаблон 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
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 %}

Теперь легко определить пункт меню actived, используя только CSS:

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

Я придумал способ использовать теги блоков в родительском шаблоне, содержащем меню, для достижения чего-то подобного.

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 может иметь подстраницы, содержащие информацию о каждом члене команды компании. Может иметь смысл, чтобы пункт меню "О программе" оставался активным для каждой из этих подстраниц.

Вы можете сделать контекстную переменную links с именем, URL и активным элементом:

{% 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 доступно в каждом шаблоне.

Вот мое решение:

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

Сегодня я столкнулся с этой проблемой, как динамически активировать "категорию" на боковой панели. В категориях есть слизни из БД.

Я решил это, проверив, что категория пули была в текущем пути. Слизни уникальны (стандартная практика), поэтому я думаю, что это должно работать без каких-либо конфликтов.

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

Предполагая, что элемент навигации является ссылкой с тем же 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://stackru.com/">Stackru</a>
  </li>
</ul>

Просто используйте теги шаблона

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

Веселиться

Судя по ответу @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 %}>
...

Затем в каждом относительном шаблоне объявите эту ссылку как "активную", например:

шаблон tab1:

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

tab2 шаблон:

{% block tab2_active %}"active"{% endblock %}
Другие вопросы по тегам