Closed larzknoke closed 3 months ago
Are you using Nunjucks or Liquid or something else?
Nunjucks
Ah, looks like Nunjucks based on the docs.
This worked for me, although it's a bit verbose:
{{ 'active' if '/foo' in page.url or '/bar' in page.url or '/foobar' in page.url }}
Wondering if your original if '/foo' or '/bar' in page.url
is treating it as:
if ('/foo') {...}
else if ('/bar' in page.url) {...}
Which might explain the behavior since if ('/foo') {}
would be truthy and it's not considering those to all be in the context of in page.url
.
If you don't like the non-scaleable verbosity, you might be able to create a custom filter (since I didn't see an obvious Nunjucks one for testing if a value is in an array):
// .eleventy.js
module.exports = eleventyConfig => {
eleventyConfig.addFilter("contains", (value, needle="", haystack=[]) => {
return haystack.some(hay => needle.includes(hay)) ? value : "";
});
return {};
};
And you can use the custom filter like a so:
{{ 'active' | contains(page.url, ['/foo', '/bar', '/foobar']) }}
Since our goofy contains
filter will check to see if our needle (url) is in the haystack (array of slugs), it will either return the original value ("active"
) if found, or an empty string if not.
We could probably also tweak it slightly to give you better control of the else
case by letting you pass an optional value if not found instead of hardcoding the empty string. Which would let you easily negate logic if you'd rather have it say "active" if found or fall back to "inactive" if not found.
In fact, here's what that would look like:
eleventyConfig.addFilter("contains", (value, needle="", haystack=[], def="") => {
return haystack.some(hay => needle.includes(hay)) ? value : def;
});
1. {{ 'active' | contains(page.url, ['/foo', '/bar', '/foobar']) }}
2. {{ 'active' | contains(page.url, ['/foo', '/bar', '/foobar'], "inactive") }}
3. {{ 'active' | contains(page.url, ['/foo', '/home', '/foobar'], "inactive") }}
Where my test page is called /home.njk so my page.url
is /home/
.
1.
2. inactive
3. active
page.url
doesn't match any of the slugs, the default empty string is returned.page.url
doesn't match any of the slugs, my custom default value, "inactive", is returned.page.url
DOES match one of the slugs (replaced '/bar' with '/home'), the original value passed to the filter ("active"
) is returned.@pdehaan I think your right, that its treating the if expression with or
usage as a if-else-if
ladder. Therefore, if the first branch is truthy, then there wouldn't be a reason to execute the second or third branch and statement. The basic syntax for a if
expression in Nunjucks is this:
{{ expression-to-echo if expression-to-match operator value [ else else-expression-here ] }}
I remember using an if
expression to apply an 'active' class for nav menu items based on the page.url
string and encountered this same issue using multiple or
's.
What I did was just used logical &
instead with the negation operator. There should be a way to use multiple or
in an if expression but this is what I remember doing. This didn't feel right but eh,
<li class="{{ 'active' if '/about/' in page.url and not '/contact/' in page.url and not '/writing' in page.url }}">About</div>
Bryan Robinson wrote a good article about creating 'active' state for nav menu items using if
expressions here:
https://bryanlrobinson.com/blog/using-nunjucks-if-expressions-to-create-an-active-navigation-state-in-11ty/
For legibility, I'd chain your filter off of the array rather than active
, and have the filter return true or false. This makes it a bit more general purpose.
Suggestion:
{{ 'active' if (['a','b','c'] | contains(page.url) )}}
https://mozilla.github.io/nunjucks/templating.html#if-expression
The one thing that was confusing me, was that I wasn't able to find any obvious documentation for the "in page.url" syntax in the Nunjucks docs.
@edwardhorsford That's a MUCH better approach! I didn't consider wrapping a filter like that.
eleventyConfig.addFilter("contains", (haystack=[], needle="") => {
return haystack.some(hay => needle.includes(hay));
});
1. {{ 'active' if (['/foo','/bar','/foobar'] | contains(page.url)) }}
2. {{ 'active' if (['/foo','/home','/foobar'] | contains(page.url)) }}
3. {{ 'active' if (['/foo','/bar','/foobar'] | contains(page.url)) else "inactive" }}
1.
2. active
3. inactive
Thanks a lot for the quick and detailed solutions/explanations. The filters are working perfectly for me!
Ah, before I close this tab, I thought of [at least] one potential bug/gotcha in the above snippet:
return haystack.some(hay => needle.includes(hay));
That snippet is using String#includes()
, which means the slug portion can exist anywhere so all of these might be valid:
Not sure if you'd want to tighten up your patterns and logic a bit to maybe do something like including trailing slashes:
{{ 'active' if (['/foo/', '/bar/', '/foobar/'] | contains(page.url)) }}
This will ensure that full paths match, and not stuff like /foo-buz-stuff/ which would currently match since it starts with /foo.
As well as changing it from needle.includes(hay)
(which is a weird way to say that, I guess), to maybe use String#startsWith()
instead:
return haystack.some(hay => needle.startsWith(hay));
This way, you're only checking the top-level directories and not something deeply nested like /a/b/c/foo-bar/bar.
Cool! Thanks a lot for your support.
From a Python perspective (where Nunjucks is inspired from via Jinja2), I'd check if page.url in ['/home', '/about', '/now']
.
I haven't checked, whether this is supported by Nunjucks, though. The filter approach is a sensible approach, too.
This is an automated message to let you know that a helpful response was posted to your issue and for the health of the repository issue tracker the issue will be closed. This is to help alleviate issues hanging open waiting for a response from the original poster.
If the response works to solve your problem—great! But if you’re still having problems, do not let the issue’s closing deter you if you have additional questions! Post another comment and we will reopen the issue. Thanks!
Hi,
I want to set a class in my navigation if a url is visited.
This is working:
{{ 'active' if '/foo' in page.url }}
but this is not working:
{{ 'active' if '/foo' or '/bar or '/foobar' in page.url }}
for the second expression the
active
class is always set.What I'm doing wrong here?