Как визуализировать меню с одним активным элементом с 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>
У нас есть постоянный список пунктов меню, поэтому он может быть более эффективным способом - создать только одну обобщенную структуру меню, а затем отобразить меню с требуемой опцией для шаблона.
Например, это может быть тег, который позволяет это сделать.
Ответы
Ответ 1
Я нашел легкое и элегантное решение 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' %}
Ответ 2
Выяснил другой способ сделать это, достаточно изящный благодаря этому ответу: fooobar.com/questions/87067/...
Учитывая шаблон 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-адреса меняются, и вам не нужно сохранять список пунктов меню в другом месте.
Ответ 3
Использование тега шаблона
Вы можете просто использовать следующий тег шаблона:
# 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
блоком "section", например:
...
<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; }
Ответ 4
Вы можете создать 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
доступны в каждом шаблоне.
Ответ 5
Я придумал способ использования блочных тегов в родительском шаблоне, содержащем меню, для достижения чего-то подобного.
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
и т.д.
Одним из преимуществ этого подхода является то, что вы можете иметь несколько подстраниц с одним и тем же активным элементом меню. Например, на странице о странице могут быть подстраницы, предоставляющие информацию о каждой команде членов компании. Было бы разумно, чтобы элемент меню "О программе" оставался активным для каждой из этих подстраниц.
Ответ 6
Предполагая, что элемент nav - это ссылка с тем же 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>
Ответ 7
Вот мое решение:
{% 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>
Ответ 8
Основываясь на ответе @vincent, существует более простой способ сделать это, не испортив шаблоны url django.
Текущий путь запроса может быть проверен на пути отображаемого элемента меню, и если они совпадают, то это активный элемент.
В следующем примере я использую django-mptt для рендеринга меню, но каждый путь к нему можно заменить node.path
.
<li class="{% if node.path == request.path %}active{% endif %}">
<a href="node.path">node.title</a>
</li>
Ответ 9
Сегодня я столкнулся с этой проблемой, как динамически активировать "категорию" на боковой панели. В категориях есть слизни, которые из БД.
Я решил это, проверив, чтобы категория 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 %}
Ответ 10
Я использую более простое и чистое решение 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-адреса обычно является дочерним элементом выбранного элемента меню.
Ответ 11
Здесь много хороших ответов, но они довольно жесткие или не очень СУХИЕ. Это позволит вам установить/получить активную навигацию во всех шаблонах, не прибегая к повсеместному включению включений и не полагаясь на совпадения только по URL.
Ваш "активный" элемент может быть представлением, пространственным представлением имен или любым произвольным текстом, который вы хотите проверить, и вы можете передать дополнительные имена классов для атрибутов DRY HTML. Поскольку мы устанавливаем/получаем из контекста шаблона, вы также можете установить активные элементы из представления (с помощью рендера) или процессора контекста.
Мне нравятся представления в пространстве имен, поэтому приведу пример их использования в базовом шаблоне с двумя ссылками.
templatetags/navigation.py:
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag(takes_context=True)
def nav_is_active(context, url_pattern, classes=''):
active_class = ''
try:
if context['nav_active'] == url_pattern:
active_class = 'is-active'
except:
pass
return mark_safe('class="{} {}"'.format(classes, active_class))
@register.simple_tag(takes_context=True)
def nav_set_active(context, url_pattern):
context['nav_active'] = url_pattern
return ''
base.html:
<a {% nav_is_active 'dashboard:index' 'menu-link' %} href="{% url 'dashboard:index' %}">Dashboard</a>
<a {% nav_is_active 'account:index' 'menu-link' %} href="{% url 'account:index' %}">Account</a>
dashboard.html:
{% extends "base.html" %}
{% load navigation %}
{% nav_set_active 'dashboard:index' %}
<h1>Dashboard!</h1>