We've found a lot of answers for this but don't fully understand them (or the strpos
manual page) so sorry if this is already answered.
Consider the following urls...
http://www.domain.com/whats-on/show-name/
http://www.domain.com/whats-on/show-name/events
And how we're applying the .active
class in the following list item markup...
<li>
<a class="<?= (strpos($_SERVER['REQUEST_URI'], '/whats-on') === 0 ? 'active' : ''); ?>" href="<?= $ShowWhatsOn; ?>">What's on</a>
</li>
<li>
<a class="<?= (strpos($_SERVER['REQUEST_URI'], '/events') === 0 ? 'active' : ''); ?>" href="<?= $ShowEvents; ?>">Events</a>
</li>
What's On gets the active class on the What's On page but also on the Events page because /whats-on is also in the url: /whats-on/show-name/events
Events never receives the active class
.active
class is applied on the correct page?We're trying to keep this short for the menu markup so hopefully there's a way to do this on one line?
Any help or pointers in the right direction would be much appreciated.
Cheers
Ben
strpos
gives you the position of a string (needle) in another string (haystack). So by doing
(strpos($_SERVER['REQUEST_URI'], '/whats-on') === 0
You are checking if the REQUEST_URI
starts with "/whats-on" (is at position 0). Since both URLs start with "/whats-on", the first item will always be active and the second never.
One fix would be to add a check for "/events":
<li>
<a class="<?= (strpos($_SERVER['REQUEST_URI'], '/whats-on') === 0 && strpos($_SERVER['REQUEST_URI'], '/events') === false ? 'active' : ''); ?>" href="<?= $ShowWhatsOn; ?>">What's on</a>
</li>
<li>
<a class="<?= (strpos($_SERVER['REQUEST_URI'], '/whats-on') === 0 && strpos($_SERVER['REQUEST_URI'], '/events') !== false ? 'active' : ''); ?>" href="<?= $ShowEvents; ?>">Events</a>
</li>
Although this gets a bit verbose for your template, so you may want to break the logic out into a function like isActive()
.
OK, for my future reference (and others) this is how I solved it.
I made this in the /helpers/ directory of my CMS and called it npe_nav.php (if using the Concrete5 CMS, don't name the file navigation.php as that name is taken by c5 so didn't work for me).
// Get URL segments
public function getURLSegments() {
return explode("/", parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
}
// Get a specific URL segment
// Takes a number which corisponds to a segment in the URL e.g. consider:
// http://www.domain.com/whats-on/show-name/events
// $navHelper->getURLSegment(1); will return whats-on
// $navHelper->getURLSegment(3); will return events
public function getURLSegment($n) {
$segs = $this->getURLSegments();
return count($segs) > 0 && count($segs) >= ($n-1)?$segs[$n]:'';
}
In Concrete5, I loaded it like this (package-name refers to the package this tools is saved in within the CMS):
// Load nav helper
$navHelper = Loader::helper('npe_nav','package-name');
This also checks if the third segment is null before applying .active
because /whats-on/
appears in all URLs in this section - so don't want it to be active on other pages.
<li>
<a class="<?= (($navHelper->getURLSegment(1) == 'whats-on') && (is_null($navHelper->getURLSegment(3))) ? 'active' : ''); ?>" href="<?= $ShowWhatsOn; ?>">What's on</a>
</li>
For the URL: http://www.domain.com/whats-on/show-name/eventsAnd other pages at segment 3
<li>
<a class="<?= ($navHelper->getURLSegment(3) == 'events' ? 'active' : ''); ?>" href="<?= $ShowEvents; ?>">Events</a>
</li>