Jump to content

Multilevel menu


nikola
 Share

Recommended Posts

How can I accomplish this kind of navigation:


<ul>
<li><a href="#">Top Menu Item 1</a>
<ul>
	<li><a href="#">Submenu Item 1</a></li>
	<li><a href="#">Submenu Item 2</a></li>
	<li><a href="#">Submenu Item 3</a></li>
</ul>
</li>
<li><a href="#">Top Menu Item 2</a>
<ul>
	<li><a href="#">Submenu Item 1</a>				
		<ul>
			<li><a href="#">Sub Submenu Item 1</a></li>                    
			<li><a href="#">Sub Submenu Item 2</a></li>                    
			<li><a href="#">Sub Submenu Item 3</a></li>                    
		</ul>
        </li>
        <li><a href="#">Submenu Item 2</a></li>                    
</ul>
</li>
<li><a href="#">Top Menu Item 3</a></li>
</ul>

News page must not list it's children (news entries).

Selected page in the tree must have an "selected" class appended to it.

I've used to do it this way in WolfCMS:


<ul>
<li><a<?php echo url_match('/') ? ' class="selected"': ''; ?> href="<?php echo URL_PUBLIC; ?>">Home page</a></li>
<?php foreach($this->find('/')->children() as $menu): ?>
	<?php if($menu->slug() == 'news') : ?>
           	<li><?php echo $menu->link($menu->title, (in_array($menu->slug, explode('/', $this->url)) ? ' class="selected"': null)); ?></li>
       <?php endif; ?>
	<li><?php echo $menu->link($menu->title, (in_array($menu->slug, explode('/', $this->url)) ? ' class="selected"': null)); ?>
       <ul>
       <?php foreach($menu->children() as $child):?>
           <?php if($child->slug() == 'news') : ?>
           	<li><?php echo $child->link() ?></li>
           <?php endif; ?>
           <?php if($child->childrenCount() > 0 && $child->slug() != 'news') : ?>
           <li><?php echo $child->link($child->title, (in_array($child->slug, explode('/', $this->url)) ? ' class="selected"': null)); ?>				
               <ul>
                 <?php foreach ($child->children() as $grandchild) : ?>
				<li><?php echo $grandchild->link($grandchild->title, (in_array($grandchild->slug, explode('/', $this->url)) ? ' class="selected"': null)); ?>                   
			  <?php endforeach; ?>
               </ul>
           	<?php endif; ?>       	
           <?php if($child->childrenCount() == 0) : ?>
           	<li><?php echo $child->link() ?></li>
           <?php endif; ?>
	<?php endforeach; ?>
       </ul>
     </li>
<?php endforeach; ?> 
</ul>

Thanks

Link to comment
Share on other sites

Yes, almonk has the right example. Here is my modified version, that is used at lukio.fi site:

<?php
function treeMenu(Page $page = null, Page $rootPage = null, $id = null) {

       if(is_null($page)) $page = wire('page');
       if(is_null($rootPage)) $rootPage = wire('pages')->get('/');
	if(!is_null($id)) $id = " id='$id'";

       $out = "\n<ul$id>";

       $parents = $page->parents;

       // This is where we get pages we want. You could just say template!=news-item 
       foreach($rootPage->children('template!=post, template!=faq-item, template!=gallery') as $child) {
               $class = "level-" . count($child->parents);
               $s = '';

               if($child->numChildren && $parents->has($child)) {
                       $class .= " on on_path";
                       $s = str_replace("\n", "\n\t\t", treeMenu($page, $child));

               } else if($child === $page) {
                       $class .= " on on_page";
                       if($page->numChildren) $s = str_replace("\n", "\n\t\t", treeMenu($page, $page));
               }
	$class .= " page-{$child->id}";

               $class = " class='$class'";
               $out .= "\n\t<li$class>\n\t\t<a$class href='{$child->url}'>{$child->title}</a>$s\n\t</li>";
       }

       $out .= "\n</ul>";
       return $out;
}
Link to comment
Share on other sites

  • 2 months later...

I try to make one but i am not sure with this and i cant fix it ,

i have like this original navi

<ul id="nav" class="dropdown dropdown-horizontal">
<li><a href="./">Gallery</a></li>
        <li><a href="./">Delta</a></li>
<li class="dir rtl"><a href="#">Contact Us</a>
	<ul>
		<li><a class="active" href="#">Enquiry Form</a></li>
		<li><a href="#">Your Feedback</a></li>
	</ul>
</li>
</ul>

and i need to put in processwire  demo here http://demo1.sherbimeonline.com/

thanks,

Link to comment
Share on other sites

Assuming I'm reading it right, I think the only thing that seems different here is the classes you are applying on the opened 'Contact Us' link. What do those classes "dir rtl" mean in terms of how they should affect the behavior of this nav?

Thanks,

Ryan

Link to comment
Share on other sites

I may be confused, because think the previous example Antti posted should do this? The only thing I can think of is if you don't want it to expand the Gallery or Delta menus (assuming they have subpages) or if you want it to include/exclude certain items from the Contact menu.

The example you posted is how you want it to look, is this correct? If so, can you post another example with how it appears using that treeMenu function? Also note in that treeMenu function you'll want to exclude this text "template!=post, template!=faq-item, template!=gallery", which is specific to the context Antti posted, but wouldn't be to yours. I think once we see what's different from your desired result you posted earlier, then it would be simpler for us to solve it. I think that you will just want to call that function like this:

echo treeMenu($page); 
Link to comment
Share on other sites

mmm look

this is a menu in process wire  Pages and Children

Home

Service

Delta

Alfa

Tango

Help

Tech

Marketing

Analytic

About

Contact

another demo here http://www.sohtanaka.com/web-design/examples/drop-down-menu/

                              http://www.cssplay.co.uk/menus/dd_valid.html

i just like to make drop down menu i try to make it with your suggestions and apesia but i am confuse with class where to add or how they are added in code.

<?php
function treeMenu(Page $page = null, Page $rootPage = null, $id = null) {

        if(is_null($page)) $page = wire('page');
        if(is_null($rootPage)) $rootPage = wire('pages')->get('/');
	if(!is_null($id)) $id = " id='$id'";

        $out = "\n<ul$id>";

        $parents = $page->parents;

        // This is where we get pages we want. You could just say template!=news-item 
        foreach($rootPage->children('template!=post, template!=faq-item, template!=gallery') as $child) {
                $class = "level-" . count($child->parents);
                $s = '';

                if($child->numChildren && $parents->has($child)) {
                        $class .= " on on_path";
                        $s = str_replace("\n", "\n\t\t", treeMenu($page, $child));

                } else if($child === $page) {
                        $class .= " on on_page";
                        if($page->numChildren) $s = str_replace("\n", "\n\t\t", treeMenu($page, $page));
                }
	$class .= " page-{$child->id}";

                $class = " class='$class'";
                $out .= "\n\t<li$class>\n\t\t<a$class href='{$child->url}'>{$child->title}</a>$s\n\t</li>";
        }

        $out .= "\n</ul>";
        return $out;
}
Link to comment
Share on other sites

Put that function somewhere which is loaded by every page. Easiest solution might be to add that to your head.inc file (if you are building from demo site).

That function doesn't do nothing if you don't use it. So if you want it to have some output, put this in your template file, wherever you want you menu:

echo treeMenu();

If you need more help, just post your full template so I can take a better look at it. You can also send it by private message if you don't want to post it here. Also email is ok: antti.peisa -- gmail

EDIT: Also note that treeMenu expands from root level to the current page. You can give it parameters to have it start and end from different pages than root and current page.

Link to comment
Share on other sites

Yes i know for it but the problem is like this i like to know, ok  i will comment classes

<?php
function treeMenu(Page $page = null, Page $rootPage = null, $id = null) {

        if(is_null($page)) $page = wire('page');
        if(is_null($rootPage)) $rootPage = wire('pages')->get('/');
	if(!is_null($id)) $id = " id='$id'";

        $out = "\n<ul$id>";

        $parents = $page->parents;

        // This is where we get pages we want. You could just say template!=news-item 
        foreach($rootPage->children('template!=post, template!=faq-item, template!=gallery') as $child) {
                $class = "level-" . count($child->parents);   // for what is this class 
                $s = '';  // for what is this

                if($child->numChildren && $parents->has($child)) {
                        $class .= " on on_path";  // for what is this class 
                        $s = str_replace("\n", "\n\t\t", treeMenu($page, $child));

                } else if($child === $page) {
                        $class .= " on on_page";
                        if($page->numChildren) $s = str_replace("\n", "\n\t\t", treeMenu($page, $page));
                }
	$class .= " page-{$child->id}";

                $class = " class='$class'";  // for what is this class 
                $out .= "\n\t<li$class>\n\t\t<a$class href='{$child->url}'>{$child->title}</a>$s\n\t</li>";
        }

        $out .= "\n</ul>";
        return $out;
}

Link to comment
Share on other sites

Those classes are just for styling purposes, so you can edit them if needed. It doesn't output any css, that is something you have to write yourself. Also if you are building dropdown menu, then you have to add css/js also (not sure how well the markup suits for dropdown).

Link to comment
Share on other sites

Hello there,

thanks for that snippet - helped me to make my multilevel menu working! (Although i don't understand the entire code yet...).

I have just one little problem: my homepage ("Home") - the root of the website is not shown in the menu - do i have to add that or should it be displayed with that code-snippet?

Kind regards,

Christian

Link to comment
Share on other sites

I haven't tried it, but I think it would probably work if you added this right above the foreach():

$children = $rootPage->children; 
if($rootPage->id == 1) $children->prepend($rootPage); 

Then your foreach would be changed to this:

foreach($children as $child) {

You'll also need to add something to prevent it from going recursive  on the homepage, otherwise you'll get a double menu. So I think you could take this approach -- anywhere that says $page->numChildren or $child->numChildren, change it to $page->numChildren && $child->id > 1, like this:

if($child->numChildren && $child->id > 1 && $parents->has($child)) {

and

if($page->numChildren && $page->id > 1) $s = str_replace(...); 

Let me know if this works...

Link to comment
Share on other sites

Hm, still not working... To be sure that i did it right, here's the code:

<?php
function treeMenu(Page $page = null, Page $rootPage = null) {
        if(is_null($page)) $page = wire('page');
        if(is_null($rootPage)) $rootPage = wire('pages')->get('/');
        $out = "\n<ul>";
        $parents = $page->parents;
        $children = $rootPage->children; 
        if($rootPage->id == 1) $children->prepend($rootPage);
        foreach($rootPage->children as $child) {
                $class = '';
                $s = '';
               if($child->numChildren && $child->id > 1 && $parents->has($child)) {
                        $class = 'active';
                        $s = str_replace("\n", "\n\t\t", treeMenu($page, $child));

                } else if($child === $page) {
                        $class = "active";
                        if($page->numChildren && $page->id > 1) $s = str_replace("\n", "\n\t\t", treeMenu($page, $page));
                }

                if($class) $class = " class='$class'";
                $out .= "\n\t<li$class>\n\t\t<a href='{$child->url}'>{$child->title}</a>$s\n\t</li>";
        }

        $out .= "\n</ul>";
        return $out;
}

echo treeMenu();
?>
Link to comment
Share on other sites

Change this:

foreach($rootPage->children as $child) { 

to this:

foreach($children as $child) {

The reason why that's necessary is because every time you call Page::children it's returning a new PageArray. So you need to specifically use the one that you ran the prepend($rootPage) on.

Link to comment
Share on other sites

Must spam to catch up (kidding).

I was just looking for some examples of multi-level menus for another project so this is yet another useful thread I've now got bookmarked!

Link to comment
Share on other sites

Hello again...

While setting up a newspage i discovered that all newsitems are listed as sub-pages in the multilevel-menu.

I tried to avoid this by excluding the news-template in the modified code from above:

<?php
function treeMenu(Page $page = null, Page $rootPage = null) {
        if(is_null($page)) $page = wire('page');
        if(is_null($rootPage)) $rootPage = wire('pages')->get('/');
        $out = "\n<ul>";
        $parents = $page->parents;
        $children = $rootPage->children; 
        if($rootPage->id == 1) $children->prepend($rootPage);
        foreach($children('template!=news') as $child) {
                $class = '';
                $s = '';
               if($child->numChildren && $child->id > 1 && $parents->has($child)) {
                        $class = 'active';
                        $s = str_replace("\n", "\n\t\t", treeMenu($page, $child));

                } else if($child === $page) {
                        $class = "active";
                        if($page->numChildren && $page->id > 1) $s = str_replace("\n", "\n\t\t", treeMenu($page, $page));
                }

                if($class) $class = " class='$class'";
                $out .= "\n\t<li$class>\n\t\t<a href='{$child->url}'>{$child->title}</a>$s\n\t</li>";
        }

        $out .= "\n</ul>";
        return $out;
}

echo treeMenu();
?>

but i get the following error:

Unable to complete this request due to an error

Without the part "('template!=news')" everything works fine - just the second level from news is shown.

Kind regards,

Christian

---

Update:

Found a solution - i changed this part:

$children = $rootPage->children; 
        if($rootPage->id == 1) $children->prepend($rootPage);
        foreach($children('template!=news') as $child) {

to this:

$children = $rootPage->children('template!=news_article'); 
        if($rootPage->id == 1) $children->prepend($rootPage);
        foreach($children as $child) {

So the excluding syntax only works when its used in a $abc->xyz context or would there be another way to have the exclusion in the "foreach($children as $child)" - part?

Link to comment
Share on other sites

It has to be done that way because essentially what it's building is a database query.

It's technically a bit more efficient too as you're excluding that template so it's not placing the pages you don't want into an array then.

Link to comment
Share on other sites

Any time you get "Unable to complete this request because of an error", you can get more details about the error by either enabling debug mode (in site/config.php) or looking in your log file at /site/assets/logs/errors.txt. I don't see any reason why it should be erroring out, so am curious to find out more. But I did notice that one one example you referred to a "news" template and another the "news_article" template... are these two separate templates?

Link to comment
Share on other sites

  • 1 year later...

@ryan: Somehow i must have missed your answer in those days, sorry! There were two seperate templates, one for the news section and one for the articles.

At last everything worked fine - but right now I recognized now that hiding the childs the way i did leaves the empty <ul></ul> - tag for those childs and opened a thread about that problem:

After all i found the module "MarkupSimpleNavigation" from Soma now and this makes almost everything possible without digging through code.

I thank you all very much for your help!

  • Like 1
Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...