I have a menu in which, on hover, the main submenu appears in full screen, in which the rest of the submenus are shown. I need to add a wrapper for the first level submenu so that there is a container around the ul. I have code but it adds wrapper for all sub-menu and i need to wrap only first level submenu.
class My_Custom_Walker extends Walker_Nav_Menu
{
function start_lvl(&$output, $depth = 0, $args = array())
{
$indent = str_repeat("\t", $depth);
$output .= "\n$indent<div class='sub-menu__wrapper'><ul class='sub-menu'>\n";
}
function end_lvl(&$output, $depth = 0, $args = array())
{
$indent = str_repeat("\t", $depth);
$output .= "$indent</ul></div>\n";
}
}
//Example
<ul>
<li>...</li>
<li>
<div class="sub-menu__wrapper">
<ul class="sub-menu">
<li>...</li>
<li>
<ul class="sub-menu"></ul>
</li>
</ul>
</div>
</li>
<li>...</li>
</ul>
Related
I'm trying to build a navigation in php and add an .active class to the <li> element if the page displays. I found this code and it works for the top level elements, i.e. it will apply the .active class to the <li> when the selected page loads.
<?php
$nav = <<<NAV
<nav id="nav">
<ul>
<li>Home</li>
<li>Section 1</li>
<li>Section 2</li>
</ul>
</nav>
NAV;
$lines = explode("\n", $nav);
foreach ($lines as $line) {
$current = false;
preg_match('/href="([^"]+)"/', $line, $url);
if (substr($_SERVER["REQUEST_URI"], 0, 5) == substr(#$url[1], 0, 5)) {
$line = str_replace('<a h', '<a class="active" h', $line);
}
echo $line."\n";
}
?>
However, when I add some child pages to the html these child pages also inherit the .active class even when the parent page is displayed. Likewise, if a child page is displayed the parent also inherits the .active class.
<?php
$nav = <<<NAV
<nav id="nav">
<ul>
<li>Home</li>
<li>Section 1
<ul>
<li>Child Page 1</li>
<li>Child Page 2</li>
</ul>
</li>
<li>Section 2</li>
</ul>
</nav>
NAV;
$lines = explode("\n", $nav);
foreach ($lines as $line) {
$current = false;
preg_match('/href="([^"]+)"/', $line, $url);
if (substr($_SERVER["REQUEST_URI"], 0, 5) == substr(#$url[1], 0, 5)) {
$line = str_replace('<a h', '<a class="active" h', $line);
}
echo $line."\n";
}
?>
Is there a way to adapt the php so only the <li> to the corresponding page inherits the .active class. Also if the .active element was a child then the parent <li> would get a class name like .active-trail?
Alternatively is there a better way to do this via another language like jQuery?
Really appreciate any help on this.
I have build a tree menu in PHP, using a recursive function, wich works ok.
My menu structure is something like this:
Root
----Categ1
-------Categ11
---------Categ111
---------Categ112
-------Categ12
---------Categ121
---------Categ122
----Categ2
----Categ3
----Categ4
I am using bootstrap.
I need to show only the active nodes and the top level childrens. For my example I need the menu to be opened like this (I clicked on Categ 112 and subcategories of Categ12 to be hidden):
Root
----Categ1
-------Categ11
---------Categ111
---------Categ112
-------Categ12
----Categ2
----Categ3
----Categ4
The php function that generate my menu tree is:
/** show all subcategs of the selected category
* #param $iCategIDSelected
* #param null $arrCategs
* #param bool $bIsOnTheLeaf
* #return string
*/
static function getHTMLCategsForMenuBySelected($iCategIDSelected, $arrCategs=null, $arrAllParents=null){
$bIsVisible = false;
if($arrCategs == null){
$oController = new ShopcategoriesController();
$arrCategs = $oController->getShopCategories(0);
}
//find if we make the current node visible or not
//if the current node id is on the parent's of the selected node then make it visible
//take all nodes starting with root until the selected node
if($arrAllParents==null){
$arrAllParents = ShopcategoriesController::getParentsBySelectedCategID($iCategIDSelected,$arrCategs);
}
$sHTML = '<ul class="nav nav-pills nav-stacked">';
foreach($arrCategs as $oneCateg){
//find if is active or not
$bActiveClass = '';
if(in_array($oneCateg['id'],$arrAllParents)){$bActiveClass = 'active';}
$sHTML .= '<li class="'.$bActiveClass.'">';
$sHTML .= ''.ucfirst($oneCateg['name']).'';
if(count($oneCateg['subCategories'])>0){
$sHTML .= ShopcategoriesController::getHTMLCategsForMenuBySelected($iCategIDSelected, $oneCateg['subCategories'],$arrAllParents);
$sHTML .= '</li>';
}else{
$sHTML .= '';
}
}
$sHTML .= '</ul>';
return $sHTML;
}
The html structure is:
ul.nav > li > a{
padding-left: 30px;
}
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div id="menuCateg">
<ul class="nav nav-pills nav-stacked">
<li class="active">
<a class="list-group-item" href="http://localhost:8000/showcategory/1">Categ1</a>
<ul class="nav nav-pills nav-stacked">
<li class="active">
<a class="list-group-item" href="http://localhost:8000/showcategory/2">Categ11 </a>
<ul class="nav nav-pills nav-stacked">
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/3">Categ111</a></li>
<li class="active"><a class="list-group-item" href="http://localhost:8000/showcategory/4">Categ112</a></li>
</ul>
</li>
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/18">Categ12</a></li>
</ul>
</li>
<li class="">
<a class="list-group-item" href="http://localhost:8000/showcategory/6">Categ2</a>
<ul class="nav nav-pills nav-stacked">
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/3">Categ121</a></li>
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/4">Categ122</a></li>
</ul>
</li>
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/8">Categ3</a>
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/8">Categ4</a>
</ul>
</div>
I found the solution in order to generate a menu tree with multilevel subcategories and show only the levels that have been selected (without all children from the tree).
THE TRICK were those lines:
if( //if the current subcateg parentid is on the parent that must be active then show his children's otherwise do not
( in_array($oneCateg['parentid'],$arrAllParents) && in_array($oneCateg['id'],$arrAllParents))
)
Thank you for your help!
/** show all subcategs of the selected category
* #param $iCategIDSelected
* #param null $arrCategs
* #param bool $bIsOnTheLeaf
* #return string
*/
static function getHTMLCategsForMenuBySelected($iCategIDSelected, $arrCategs=null, $arrAllParents=null, $iLevel=0){
$bIsVisible = false;
if($arrCategs == null){
$oController = new ShopcategoriesController();
$arrCategs = $oController->getShopCategories(0);
}
//find if we make the current node visible or not
//if the current node id is on the parent's of the selected node then make it visible
//take all nodes starting with root until the selected node
if($arrAllParents==null){
$arrAllParents = ShopcategoriesController::getParentsBySelectedCategID($iCategIDSelected,$arrCategs);
$arrAllParents[]=0;
}
$iLevel++;
$sHTML = '<ul class="nav nav-pills nav-stacked level_'.$iLevel.'">';
foreach($arrCategs as $oneCateg){
//find if is active or not
$bActiveClass = '';
if(in_array($oneCateg['id'],$arrAllParents)){$bActiveClass = 'active';}
$sHTML .= '<li class="'.$bActiveClass.'">';
$sHTML .= ''.ucfirst($oneCateg['name']).'';
if(count($oneCateg['subCategories'])>0){
if( //if the current subcateg parentid is on the parent that must be active then show his children's otherwise do not
( in_array($oneCateg['parentid'],$arrAllParents) && in_array($oneCateg['id'],$arrAllParents))
) {
$sHTML .= ShopcategoriesController::getHTMLCategsForMenuBySelected($iCategIDSelected, $oneCateg['subCategories'], $arrAllParents, $iLevel);
$sHTML .= '</li>';
}
}else{
$sHTML .= '';
}
}
$sHTML .= '</ul>';
return $sHTML;
}
you should add a class when it's not active like .not-active.
.not-active{
display: none;
}
EDIT
I'm not sure to have understood correctly your point but how about this way:
foreach($arrCategs as $oneCateg){
//find if is active or not
$bActiveClass = '';
if(in_array($oneCateg['id'],$arrAllParents)){ //modified
$bActiveClass = 'active';
}else{
$bActiveClass = 'not-active';
}
$sHTML .= '<li class="'.$bActiveClass.'">';
$sHTML .= ''.ucfirst($oneCateg['name']).'';
if(count($oneCateg['subCategories'])>0){
$sHTML .= ShopcategoriesController::getHTMLCategsForMenuBySelected($iCategIDSelected, $oneCateg['subCategories'],$arrAllParents);
$sHTML .= '</li>';
}else{
$sHTML .= '';
}
}
I'm a newbie of Drupal. I'm trying to develop a bootstrap 3 template, but I have a problem with the navbar dropdown menu implementation. I followed these steps:
in my mytheme/templates folder I create a page.tpl.php file with the following code:
if ($page['header'])
...
$main_menu = variable_get('menu_main_links_source', 'main-menu');
$tree = menu_tree($main_menu);
print drupal_render($tree);
...
in mytheme folder I create a template.php file with these functions:
function mytheme_menu_tree($variables) {
return '<ul class="nav navbar-nav">' . $variables['tree'] . '</ul>';
}
function mytheme_menu_link(array $variables) {
$element = $variables['element'];
$sub_menu = '';
$dropdown = '';
if ($element['#below']) {
$sub_menu = drupal_render($element['#below']);
$dropdown = 'class="dropdown"';
$element['#localized_options']['attributes']['class'][] = 'dropdown-toggle';
}
$output = l($element['#title'], $element['#href'], $element['#localized_options']);
return '<li ' .$dropdown. ' >' . $output . $sub_menu . "</li>\n";
}
with this code I'm at a good point, but I need to remove classes "nav navbar-nav" from children and add the class "dropdown-menu".
This is the result of my code:
<ul class="nav navbar-nav">
<li>XYZ</li>
<li>ASD</li>
<li class="dropdown">XXX
<ul class="nav navbar-nav">
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
</li>
</ul>
and this is what I would like to obtain:
<ul class="nav navbar-nav">
<li>XYZ</li>
<li>ASD</li>
<li class="dropdown">XXX
<ul class="dropdown-menu"> <!-- HERE IS THE DIFFERENCE -->
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
</li>
</ul>
Maybe I can do something like this:
function mytheme_menu_tree($variables) {
if ( [check if I'm at the first level] ) {
return '<ul class="nav navbar-nav">' . $variables['tree'] . '</ul>';
} else {
return '<ul class="dropdown-menu">' . $variables['tree'] . '</ul>';
}
}
but I don't know how... Any idea?
I too am a bit of a newbie when it comes to Drupal and was also having this problem. I have tweaked your function and it works for me:
function SeatradeKorea_menu_link(array $variables) {
$element = $variables['element'];
$sub_menu = '';
$dropdown = '';
if ($element['#below']) {
$sub_menu = drupal_render($element['#below']);
$sub_menu = str_replace('nav navbar-nav', 'dropdown-menu', $sub_menu);
$dropdown = 'class="dropdown"';
$element['#localized_options']['attributes']['class'][] = 'dropdown-toggle';
}
$output = l($element['#title'], $element['#href'], $element['#localized_options']);
return '<li ' .$dropdown. ' >' . $output . $sub_menu . "</li>\n";
}
I have added a str_replace() into your #below element to replace the "nav navbar-nav" with "dropdown-menu"
I want to know how to make a WordPress wp_nav_menu() or wp_list_pages() Walker class that will generate nav menu output like this?
<!-- Level1 -->
<li class="current">
Name
<!--Level2-->
<div class="nav-sub">
<ul>
<li>Name</li>
<li>
Name
<!--Level3-->
<div class="nav-sub">
<ul>
<li>
Name
<!--Can Add Unlimited levels If Possible-->
</li>
<li>Name
</li>
</ul>
</div>
</li>
<li>Name</li>
<li>Name</li>
</ul>
</div>
</li>
<!-- This is One Level only -->
<li>
Name
</li>
I tried many ways but can't just figure it out.
Here is the current output using wp_list_pages()
<ul class="myclass">
<li id="home">Home</li>
<li class="page_item page-item-2 page_item_has_children">Sample Page
<ul class="children">
<li class="page_item page-item-9">Home</li>
</ul>
</li>
<li class="page_item page-item-19">page no sub</li>
</ul>
Thanks in advance!
P.S: I have already read this article here http://codex.wordpress.org/Function_Reference/wp_nav_menu But can't figure out :(
Edit :
Well, look the differents parameters and call your menu
<?php
class Child_Wrap extends Walker_Nav_Menu
{
function start_lvl(&$output, $depth)
{
$indent = str_repeat("\t", $depth);
$output .= "\n$indent<div class=\"div-sub\"><ul class=\"sub-menu\">\n";
}
function end_lvl(&$output, $depth)
{
$indent = str_repeat("\t", $depth);
$output .= "$indent</ul></div>\n";
}
}
wp_nav_menu(
array(
'menu' => 'navigation',//(i created this menu on the backend of wordpress)
'container' => 'div',
'container_class' => 'menu-sidebar',
'menu_class' => 'menu-class',
'menu_id' => 'menu-id',
'theme_location' => 'primary',
'walker' => new Child_Wrap()
)
); ?>
You can specified many parameters (id menu, class and id container etc...)
source class walker
Should you find your happiness here
Name menu screen ('menu' => 'navigation')
I have a problem with closing my li's and ul's at the correct moment.
With the code we have we retrieve all childeren of a specific categorie in the magento shop.
Now the problem is that i want to divide all children in lists. So we can have them sorted by category -> sub-category -> sub-sub category. I want my structure to be;
<ul>
<li>
<a>Head Child</a>
<ul>
<li>
<a>Sub child</a>
<ul>
<li><a>Sub sub child</a></li>
<li><a>Sub sub child</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a>Head Child</a>
<ul>
<li>
<a>Sub child</a>
<ul>
<li><a>Sub sub child</a></li>
<li><a>Sub sub child</a></li>
</ul>
</li>
</ul>
</li>
</ul>
The output im getting now is
<ul>
<a title="View the products for the " href="#">Head child</a>
<li class="sub_cat">
<a title="View the products for the " href="#">Sub child</a>
</li>
<li class="sub_cat">
<a title="View the products for the " href="#">Sub sub child</a>
</li>
</ul>
This is our php code;
<?php
$cat = Mage::getModel('catalog/category')->load(9);
$subcats = $cat->getChildren();
foreach(explode(',',$subcats) as $subCatid)
{
$_category = Mage::getModel('catalog/category')->load($subCatid);
if($_category->getIsActive()) {
echo '<ul>'.$_category->getName().'';
$sub_cat = Mage::getModel('catalog/category')->load($_category->getId());
$sub_subcats = $sub_cat->getChildren();
foreach(explode(',',$sub_subcats) as $sub_subCatid)
{
$_sub_category = Mage::getModel('catalog/category')->load($sub_subCatid);
if($_sub_category->getIsActive()) {
echo '<li class="sub_cat">'.$_sub_category->getName().'';
$sub_sub_cat = Mage::getModel('catalog/category')->load($sub_subCatid);
$sub_sub_subcats = $sub_sub_cat->getChildren();
foreach(explode(',',$sub_sub_subcats) as $sub_sub_subCatid)
{
$_sub_sub_category = Mage::getModel('catalog/category')->load($sub_sub_subCatid);
if($_sub_sub_category->getIsActive()) {
echo '<li class="sub_cat">'.$_sub_sub_category->getName().'';
}
}
}
}
echo '</ul>';
}
}
?>
You should consider using recursive function here
$cat = Mage::getModel('catalog/category')->load(9);
$html = '';
$html = getSubCategoriesHTML($cat, $html);
function getSubCategoriesHTML($cat, $html) {
$html .= ''.$cat->getName().'';
$html .= '<ul>';
$subcats = $cat->getChildren();
foreach(explode(',',$subcats) as $subCatid) {
$_category = Mage::getModel('catalog/category')->load($subCatid);
if($_category->getIsActive()) {
$html .= '<li>';
$html .= getSubCategoriesHTML($_category, $html);
$html .= '</li>;
}
}
$html .= '</ul>';
return $html;
}
You can add this function in a helper or in your category model.
I haven't tested it at all so it may not work correctly. But I hope it can help you.