Как сделать меню с одним активным элементом с СУХОЙ?
Я хотел бы сделать такие конструкции, как:
<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 %}