Jump to content
ljones

How can I keep second level menu item active on third level subpage?

Recommended Posts

I am working on a simple photo gallery site where I have

level one: Home, Galleries, Statement

level two: Set 1, Set 2, Set 3 etc. (under Galleries)

level three: a template that cycles through the child photos of Set 1, Set 2 or Set 3 set by using this code:

if ($page->prev->id) {
 echo "<a href='{$page->prev->url}'></a>";
} else {
 $lastpage = $page->siblings->last();
 echo "<a href='$lastpage->url'></a>";
}

echo "<a href='{$page->parent->url}'></a>";

if ($page->next->id) {
 echo "<a href='{$page->next->url}'></a>";
} else {
 $firstpage = $page->siblings->first();
 echo "<a href='$firstpage->url'></a>";
}

echo "<img class='centered' src ='{$page->images->first()->url}'";

The level one and two navigation menus show the the active links as "on" and highlighted according to my CSS declarations at level two. For example:

Home Galleries Statement

[set One] [set Two] [set Three]

[Thumbnail 1] [Thumnail 2] [Thumbnail 3]

But clicking on the Thumnail 2 and cycling through turns off the Set Two link:

Home Galleries Statement

[set One] [set Two] [set Three]

<prev [image detail] next>

What do I need to add or change to make the second level navigation show as being "on"?

This is the code I am using in the header included in all templates at level 1, 2, and 3:

<div id="primary-menu">
<?php

// Create the top navigation list by listing the children of the homepage.
// If the section we are in is the current (identified by $page->rootParent)
// then note it with <a class='on'> so we can style it differently in our CSS.
// In this case we also want the homepage to be part of our top navigation,
// so we prepend it to the pages we cycle through:

$homepage = $pages->get("/");
$children = $homepage->children;
$children->prepend($homepage);

foreach($children as $child) {
 $class = $child === $page->rootParent ? " class='on'" : '';
 echo "<a$class href='{$child->url}'>{$child->title}</a>";
 echo "       ";
}

?>

</div>
		   <div id="secondary-menu">

<?php
 echo "<hr>";
// Output subnavigation
//
// Below we check to see that we're not on the homepage, and that
// there are at least one or more pages in this section.
//
// Note $page->rootParent is always the top level section the page is in,
// or to word differently: the first parent page that isn't the homepage.
if($page->path != '/' && $page->rootParent->numChildren > 0) {
 // We have determined that we're not on the homepage
 // and that this section has child pages, so make navigation:

 foreach($page->rootParent->children as $child) {
  $class = $page === $child ? " class='on'" : '';
  echo "<a$class href='{$child->url}'>{$child->title}</a>";
  echo "       ";
 }
echo "<hr>";
	 }
  ?>

Share this post


Link to post
Share on other sites

Hello all,

I'm new here and this is my first post. Found PW a couple of weeks ago and working on my first project. So far I can say it is fun developing with PW. I like the whole concept, the documentation is great and the forum is the best I have seen so far.

I had the same problem with highlighting parent menu items when one of their children is the current page. Reading this thread and following the link in previous post, I came up with a solution that is working great for me. Here's my piece of code:

foreach($page->rootParent->children as $child) {

	$class = $page === $child || $child->children->has($page) ? " class='active'" : ''; ?>
						
	<li><a<?php echo $class; ?> href='<?php echo $child->url ?>'><?php echo $child->title ?></a></li>
<?php } ?>

The bit that does the highlighting for parent menu items is

$child->children->has($page)

As easy as that  :)

I was first trying to solve it with native PHP strpos funtion before I realised that the PW API offers the has() function which really makes life a lot easier.

If there are better ways to achieve the same goal, I would love to learn them.

Cheers

gerhard

  • Like 1

Share this post


Link to post
Share on other sites

You can also test for the pages parents $page->parents->has($child)

  • Like 1

Share this post


Link to post
Share on other sites

Thank you diogo for the insight.

I was thinking top to bottom. Your solution looks more logical, though.

But all roads lead to rome, as they say  ;)

  • Like 1

Share this post


Link to post
Share on other sites

In the same line of thought.... I have a menu with months and a submenu with days for every month. The idea is to click on a month's name, then a day's number and see a list of pages that have a matching 'month' and 'day' field. 'months' is a parent with 12 children (january, february,....), every month is a parent with 29, 30 or 31 children (january_01, january_02,....).

When I'm on the 'months' page I see the '12-months' menu, when I click on a month I see the 'days menu'. But when I click on a day, the 'days menu' disappears. While the list of items for that month and day shows up correct.

In the code below, where is my 'days menu' disappearing?

<!-- NAVIGATION FOR MONTHS -->
	<div>
	<ul id='topnavmonths'>
	<?php			
	$monthpage = $pages->get("/events/months/"); 
	$children = $monthpage->children;
	foreach($children as $child) {
	$class = $child === $page ? " class='on'" : '';
	echo "<li><a$class href='{$child->url}'>{$child->title}</a></li>";
	};
	?>
	</ul>
	</div>
	<br>
<!-- NAVIGATION FOR DAYS -->
	<div>
	<ul id='topnavmonths'>
	<?php
	if ($page->parent->name==months) {
	$daypage = $pages->get("/events/months/$page->name/");
	}
	else {
	$daypage = $pages->get("/events/months/$page->parent->name/");
	};
	$children = $daypage->children;	
	foreach($children as $child) {
	$class = $child === $page ? " class='on'" : '';
	echo "<li><a$class href='{$child->url}'>{$child->bfd_day->title}</a></li>";
	};
	?>
	</ul>
	</div>

For a (not) working example: http://www.birthfactdeathcalendar.net/bfd_processwire/events/

Share this post


Link to post
Share on other sites

@BFD Calendar

what is the $page->parent->name, when you are on a days page?

Share this post


Link to post
Share on other sites

When I'm on '/events/months/january/january-02/' $page->parent->name=january

I added a bit of code on the page to make it visible:

	<?php echo "<br><br>page name = {$page->name} / parent name = {$page->parent->name}<br>";
	 if ($page->parent->name==months) {
	 echo "parent=/months/<br>";
	 }
	 elseif ($page->parent->name==january) {
	 echo "parent=/january/"; 
	 }
	 else {
	 echo "nope";
	 };
	 ?>

Share this post


Link to post
Share on other sites

Try changing this line in your code

$daypage = $pages->get("/events/months/$page->parent->name/");

to this

$daypage = $page->parent;

Give it a try and let us know.

  • Like 1

Share this post


Link to post
Share on other sites

Huh, less is more I guess. Anyway, it works. Thank you!

One more (additional) question: how do I keep the 'class' 'on' for months when events are shown?

$class = $child === $page ? " class='on'" : '';

Keeps the parent 'on' but it should keep the parent of parent 'on' as well to have a better idea of the navigation.

ps @ gebeer -> http://www.birthfactdeathcalendar.net/bfd_processwire/the-eyes/1-january-2000-bangkok-thailand/

Share this post


Link to post
Share on other sites

Glad it helped.

For keeping class on for month when you are on the day page:

$class = $page === $child || $child->children->has($page) ? " class='active'" : ''; 

Worked fine for my case.

Or the other way around like diogo suggested a few posts above:

$class = $page === $child || $page->parents->has($child) ? " class='active'" : ''; 

Hope it helps.

PS: nice picture from Bkk

 

  • Like 3

Share this post


Link to post
Share on other sites

This doesn't change anything. The status of 'events' is 'on', months are off and days is 'on'. Same with all three solutions, my original one, yours and Diogo's.

As soon as I've completed some more coding I'll start adding more content, including more BKK pictures into 'the eyes'.

Share this post


Link to post
Share on other sites

Where did you add the code?

It should go into the code for the month menu

<!-- NAVIGATION FOR MONTHS -->
	<div>
	<ul id='topnavmonths'>
	<?php			
	$monthpage = $pages->get("/events/months/"); 
	$children = $monthpage->children;
	foreach($children as $child) {
	$class = $page === $child || $child->children->has($page) ? " class='on'" : '';
	echo "<li><a$class href='{$child->url}'>{$child->title}</a></li>";
	};
	?>
	</ul>
	</div>

$page and $child represent the month page that you are currently on.

$child->children represents the day pages of that month.

has($page) now checks if the page we are currently on is in the day pages of the month we are in.

This should do the trick.

  • Like 1

Share this post


Link to post
Share on other sites
$class = $page === $child || $child->children->has($page) ? " class='active'" : ''; 

nice one gebeer, thanks for sharing!

didn't made it for me properly because then the homepage menu would be active too. But it pointed me in the right direction

so I'm successfully using this now

$item->child("id=$page->id") === $page

$item would be $child in the above examples

  • Like 1

Share this post


Link to post
Share on other sites

just had to do a simple 1-level menu on my new website and was once again surprised how easy things can be with the great API. i was doing navigations like in the examples above but today i took a slightly different approach that in my opinion is a little bit better to read and maybe easyer/cleaner to understand:

function renderMenu($page) {
    // get the root page
    $hp = wire('pages')->get('/');

    // set pages that should have "active" class
    // remove root page otherwise this would always be active
    // if current page == rootpage it will be appended and set active
    $active = $page->parents
        ->remove($hp)
        ->append($page);

    // set pages that should be shown in the menu
    // easy in this case because it is only 1-level
    $menuitems = $hp->children
        ->prepend($hp);

    // return the markup
    $out = '';
    foreach($menuitems as $item) {
        $cls = ($active->has($item)) ? ' class="uk-active"' : '';
        $out .= '<li' . $cls . '><a href="' . $item->url . '">' . $item->title . '</a></li>';
    }
    return $out;
} 

or with less comments and most on one line:

function renderMenu($page) {
    $hp = wire('pages')->get('/');
    $active = $page->parents->remove($hp)->append($page);
    $menuitems = $hp->children->prepend($hp);

    $out = '';
    foreach($menuitems as $item) {
        $cls = ($active->has($item)) ? ' class="uk-active"' : '';
        $out .= '<li' . $cls . '><a href="' . $item->url . '">' . $item->title . '</a></li>';
    }
    return $out;
} 

of course that is really a little difference to the examples above, but sometimes for me it got a little confusing to handle the "active" class with some IFs and ORs and ANDs and the approach with prepend() append() add() or remove() may be more elegant.

another thread for reference if anybody is having trouble https://processwire.com/talk/topic/288-solved-navigation-current/

  • Like 3

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Sten
      Sorry I can't find any submit button to save my configuration on a template in the admin interface. The file is fine.
    • By nabo
      Hello
      this is my snippet
      public function init() { if($this->wire('user')->isSuperuser()) { $this->wire()->addHookAfter('ProcessPageEdit::buildFormSettings', $this, 'buildForm'); $this->wire()->addHookBefore('ProcessPageEdit::processInput', $this, 'saveForm'); } } public function buildForm(HookEvent $event) { $p = $event->object->getPage(); $inputfields = $event->return; $fieldset = $this->wire('modules')->get("InputfieldFieldset"); $fieldset->attr('id', 'my_fieldset'); $fieldset->label = __("My Renders"); $fieldset->collapsed = Inputfield::collapsedYes; $field = $this->wire('modules')->get("InputfieldTextarea"); $field->attr('name', 'renders'); $field->attr('value', $p->renders); $field->label = $this->_('Renders'); $fieldset->append($field); $inputfields->append($fieldset); } public function saveForm($event) { $page = $this->pages->get($this->input->post->id); $page->set('renders', $this->input->post->renders); } It builds correctly the inputs, I edit the field renders but when I save the page the value of this inputfield remain blank.
      What's wrong?
    • By carlitoselmago
      I'm building a fieldtype/inputfield to visually select a grid like Bootstrap since it's the first time coding such thing, I'm basing my code in FieldtypeColorPicker since the base structure is the same, a graphical UI helper to finally output a string as value.
      The problem is I try to save the value as something like "1,2,3" referencing the index of each column selected but it gets converted as an int value so "1,2,3" becomes "1" if input random text it gets converted as "0".
      Here is the code:
      https://github.com/carlitoselmago/FieldtypeGridSpace
      Here's an screenshot of how it looks

    • By bramwolf
      Hi Guys,

      I found that my site is producing this notice on the front end, even when $config->debug = false and also when admin is
      not even logged in. How do I keep the site from displaying this message to front end users? The notice is about a few vars
      being tested, which in this case aren't defined, how would I stop that from happing all together when testing vars? if I for instance
      wrap the $discountCode = blabla in a if($order) { ...  } statement than I'm again testing for $order which isn't a object in this case
      which throws another notice..

      This is the notice:


      Here is my current code:
      // PAD DISCOUNT CODE HOOK // First we see if we have active discount code if ($this->session->orderId) { $order = $this->pages->get($this->session->orderId); } if($product->geen_korting == 1) { $nodisc = $product->geen_korting; } else { $nodisc = $product->parent->geen_korting; } $discountCode = $this->sanitizer->selectorValue($order->pad_discount_code); $dc = $this->pages->get("template=paddiscount, title=$discountCode"); if ($dc->id) { if ($nodisc) { $discount = 0; } elseif ($dc->pad_percentage) { $discount = $newprice * ($dc->pad_percentage / 100); } } $event->return = $newprice - $discount;
      Thanks in advance 🙂
    • By shadowkyogre
      [EDIT]: After sitting down and planning out my site according to the ragged hierarchy information, I settled on the following schematics.
      /$world/$template/$content_of_template_type/... for my pretty URLs /roster/$character for my characters a generic Repeater field with depth on most content types for custom positions for child pages to connect to instead of it directly a few Repeater fields on each content that have (PageReference[1], other fields) to establish associations A few FieldsetGroups to help me manage the fields that I needed to copy across a bunch of content types. Kept the original post below for context and tagged the OP for searchability.
      ---
      Hi everyone! I'm working on a personalized worldbuilding wiki to host my art and story stuff.
      Right now my site architecture looks like...
      /$world/cosmology/$cosmology /$world/locations/$location /$world/factions/$faction /$world/history/$history /$world/species/$species /roster/$character So far the layout works, but there's one problem. I need to make sub-sections for an organization. Organizations can appear under cosmology, locations, and factions. Sounds straightforward until... I run into the problem of figuring out how to represent subfactions.
      Key factors in this are...
      Characters should be able to be part of multiple organizations Characters should have an explicit role assigned to their membership. Character pages should be able to query the organization pages to display their ranks across organizations. Editing an organization's hierarchical layout should be visible while editing the root organization page. From what I've read of the ProcessWire documentation, the best use case for each way of representing the organization's subsections are...
      Child Pages:: Works best for menu presentation and dedicated editing. PageTables:: Works if you want control over where to place the PageTable fields, but requires opening a modal for the pages you want to edit. It's also kind of like normal pages. Repeaters:: Works great for inline editing and easy control over hierarchy, but the page urls become obscure. Sections in the body field:: Works for copypasting from my note files. But it doesn't expose relationships for easy querying. It looks like my best case for this is child pages since it allows displaying suborganization in the URL easily. But also I lose out on quickly reordering and editing the child pages. Any advice for people running into similar use cases?
×
×
  • Create New...