Damienov Posted February 28, 2014 Posted February 28, 2014 Hi everyone, I've made a snippet update for the bootstrap 2.2.2 navbar by Soma to Bootstrap 3.x. I also added a multi level menu fix since it was removed from Bootstrap 3. Screenshot: topnav_inc <?php /* Navigation for ProcessWire using the Bootstrap 2.2.2 markup This menu was written by Soma based on work by NetCarver and a bit thrown in by Joss Navigation Bootstrap 3 update by Damienov, with multi level dropdown support fix */ function renderChildrenOf($pa, $output = '', $level = 0) { $output = ''; $level++; foreach ($pa as $child) { $atoggle = ''; $class = ''; if ($child->numChildren && count($child->parents) == 1) { $class .= 'dropdown'; $atoggle .= ' class="dropdown-toggle" data-toggle="dropdown"'; } else if ($child->numChildren && count($child->parents) > 1 ) { $class .= 'dropdown-submenu'; $atoggle .= ' class="dropdown-toggle"'; } else if ($child->numChildren && $child->id != 1) { $class .= 'dropdown-menu'; } // Makes the current page and it's top level parent add an active class $class .= ($child === wire("page") || $child === wire("page")->rootParent) ? " active" : ''; $class = strlen($class) ? " class='" . trim($class) . "'" : ''; if ($child->numChildren && count($child->parents) == 1) { // Add Caret if have children $output .= "<li$class><a href='$child->url'$atoggle>$child->title <b class='caret'></b></a>"; } else if ($child->numChildren && count($child->parents) > 1) { $output .= "<li$class><a tabindex='-1' href='$child->url'$atoggle>$child->title</a>"; } else { $output .= "<li$class><a href='$child->url'$atoggle>$child->title</a>"; } // If this child is itself a parent and not the root page, then render it's children in their own menu too... if ($child->numChildren && $child->id != 1) { $output .= renderChildrenOf($child->children, $output, $level); } $output .= '</li>'; } $outerclass = ($level == 1) ? "nav navbar-nav" : 'dropdown-menu'; return "<ul class='$outerclass'>$output</ul>"; } // bundle up the first level pages and prepend the root home page $homepage = $pages->get(1); $pa = $homepage->children; $pa = $pa->prepend($homepage); // Set the ball rolling... echo renderChildrenOf($pa); Add to your CSS /* Bootstrap 3 navbar with multi level fix */ .dropdown-submenu{ position:relative; } .dropdown-submenu > .dropdown-menu { top:0; left:100%; margin-top:-6px; ; -webkit-border-radius:0 6px 6px 6px; -moz-border-radius:0 6px 6px 6px; border-radius:0 6px 6px 6px; } .dropdown-submenu:hover > .dropdown-menu{ display:block; } .dropdown-submenu > a:after{ display:block; content:" "; float:right; width:0; height:0; border-color:transparent; border-style:solid; border-width:5px 0 5px 5px; border-left-color:#cccccc; margin-top:5px; margin-right:-10px; } .dropdown-submenu:hover > a:after{ border-left-color:#ffffff; } .dropdown-submenu .pull-left{ float:none; } .dropdown-submenu.pull-left > .dropdown-menu{ left:-100%; margin-left:10px; -webkit-border-radius:6px 0 6px 6px; -moz-border-radius:6px 0 6px 6px; border-radius:6px 0 6px 6px; } note: My php skills are crappy , so feel free to add some fixes on the code 4
Joss Posted February 28, 2014 Posted February 28, 2014 If you want multi level navigation, the system on Foundation works better since it allows a way to navigate back through the levels. But a useful fix you have done here.
Joss Posted February 28, 2014 Posted February 28, 2014 Currently, if you have a header item called About Us that links to a page, if it has a child, the item cannot be linkable because that wont work on mobile devices. What would be nice is in those circumstances to duplicate the About Us link and make it the first child of itself. So you get: About Us About Us Child
Damienov Posted February 28, 2014 Author Posted February 28, 2014 Currently, if you have a header item called About Us that links to a page, if it has a child, the item cannot be linkable because that wont work on mobile devices. What would be nice is in those circumstances to duplicate the About Us link and make it the first child of itself. So you get: About Us About Us Child Since changing it would break the menu on mobile, perhaps adding an empty template+ empty placeholder trough processwire would be best?
Joss Posted February 28, 2014 Posted February 28, 2014 Yes, my way round it previously has been to make parent items nothing. You can actually add a bit of code so that that if an item has children then $childUrl beomes # I added this to a foundation version (based on a later version of the bootstrap menu, I think - cant quite remember now) $has_children = count($child->children) ? true : false; if($has_children && $child !== $root) { $class .= 'has-dropdown'; // sub level Foundation dropdown li class $childUrl = "#"; // stop parents being clickable } else { $childUrl = $child->url; // if does not have children, then get the page url } But repeating the parent item as a child would be the ultimate - then use media queries to show or not to show. That way you can preserve your tree structure but still have it work. I have to say, I am tending to avoid complicated menus entirely now. 1
Damienov Posted February 28, 2014 Author Posted February 28, 2014 I have to say, I am tending to avoid complicated menus entirely now. I know, and simple (2 tier) keeps mobile compatibility.
adrian Posted February 28, 2014 Posted February 28, 2014 Currently, if you have a header item called About Us that links to a page, if it has a child, the item cannot be linkable because that wont work on mobile devices. What would be nice is in those circumstances to duplicate the About Us link and make it the first child of itself. So you get: About Us About Us Child I actually love the way meanmenu handles this issue: http://www.meanthemes.com/demo/meanmenu/demo.html 1
adrian Posted February 28, 2014 Posted February 28, 2014 Thats kind of cute! Cute ? I actually thought it was pretty functional - nicer to be able to click on the parent item or expand it to access the children. You not sold on the idea?
Joss Posted February 28, 2014 Posted February 28, 2014 Over in the uk, Cute used like this is saying it is good. Possibly a London thing ... not sure. 2
adrian Posted February 28, 2014 Posted February 28, 2014 Well the definition of cute that I remember from my Aussie upbringing was: "Ugly, but interesting" Glad we got that cleared up 1
artamteex Posted May 26, 2014 Posted May 26, 2014 EDITED OUT - found my mistake. How to add piece of html at the end of menu, before final </ul>? It doesn't execute additional echo at the end of the function. (want to add separate menu button with Call To Action)Not knowing php sucks :/
Marcel Epp Posted August 4, 2016 Posted August 4, 2016 Hi, how can i change the deepness? I only want to go to level 1 (Tier2). I only need the parent and the child's. How can i do that?
flydev Posted August 5, 2016 Posted August 5, 2016 (edited) @Marcel Epp You could add a new parameter to the function. Assuming you are using the code shown in the first post, the following code should work (not tested) : function renderChildrenOf($pa, $maxDepth = 1, $output = '', $level = 0) // MODIFIED_CODE { $output = ''; $level++; foreach ($pa as $child) { $atoggle = ''; $class = ''; if ($child->numChildren(true) && count($child->parents) == 1) { $class .= 'dropdown'; $atoggle .= ' class="dropdown-toggle" data-toggle="dropdown"'; } else if ($child->numChildren(true) && count($child->parents) > 1 ) { $class .= ($maxDepth > 0) ? 'dropdown-submenu' : ''; // MODIFIED_CODE $atoggle .= ($maxDepth > 0) ? ' class="dropdown-toggle"' : ' '; // MODIFIED_CODE } else if ($child->numChildren(true) && $child->id != 1) { $class .= 'dropdown-menu'; } // Makes the current page and it's top level parent add an active class $class .= ($child === wire("page") || $child === wire("page")->rootParent) ? " active" : ''; $class = strlen($class) ? " class='" . trim($class) . "'" : ''; if ($child->numChildren(true) && count($child->parents) == 1) { // Add Caret if have children $output .= "<li$class><a href='$child->url'$atoggle>$child->title <b class='caret'></b></a>"; } else if ($child->numChildren(true) && count($child->parents) > 1) { $output .= "<li$class><a tabindex='-1' href='$child->url'$atoggle>$child->title</a>"; } else { $output .= "<li$class><a href='$child->url'$atoggle>$child->title</a>"; } // If this child is itself a parent and not the root page, then render it's children in their own menu too... if ($child->numChildren(true) && $child->id != 1 && $maxDepth > 0) { // MODIFIED_CODE $maxDepth--; // MODIFIED_CODE $output .= renderChildrenOf($child->children, $maxDepth, $output, $level); // MODIFIED_CODE } $output .= '</li>'; } $outerclass = ($level == 1) ? "nav navbar-nav" : 'dropdown-menu'; return "<ul class='$outerclass'>$output</ul>"; } // bundle up the first level pages and prepend the root home page $homepage = $pages->get(1); $pa = $homepage->children; $pa = $pa->prepend($homepage); // Set the ball rolling... echo renderChildrenOf($pa); Search the comments MODIFIED_CODE to see whats going on in the function. Edited August 7, 2016 by flydev code tested// added @Horst suggestion : numChildren(true) 2
Marcel Epp Posted August 5, 2016 Posted August 5, 2016 Hi flydev, the code returns an internal server error. I will try to get it to work. Thank you.
flydev Posted August 5, 2016 Posted August 5, 2016 I did a test on a fresh install and it work fine. How did you implemented it ? The tree Result with $maxDepth = 1 Result with $maxDepth = 2 3
horst Posted August 6, 2016 Posted August 6, 2016 On 5.8.2016 at 2:27 PM, flydev said: Assuming you are using the code shown in the first post, the following code should work (not tested) : @flydev: only one suggestion: it should be $child->numChildren(true) instead of $child->numChildren // what is the shortcut to $child->numChildren(false) Read here why: https://processwire.com/talk/topic/13950-show-image-childrens-children/#comment-125410 3
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now