Latest News: (loading..)
De Dokta

Categories Menu with multiple subs in the header navbar

53 posts in this topic

Hi
 
I have long been looking for a way to integrate the categories menu with multiple sub categories into  the header navbar, but I always failed to embed the appropriate code into the category_tree.php.  :(  With the help of the code gadlol has recently posted, some JS and CSS I have now found a way without changing the category_tree.php.
Looks like this:
post-315569-0-42918100-1405688153_thumb.jpgpost-315569-0-59962000-1405688169_thumb.jpg
How it works:

 In includes/functions/html_output.php add a new function:

function tep_show_tree($root_id = 0,$mainUlClass='dropdown-menu',$submenuUlClass='dropdown-menu sub-menu'){
    global $languages_id,$cPath_array;
    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        $loop = !empty( $children[$root_id] );
        $parent = $root_id;
        $parent_stack = array();
    $html=array();//store html code
        $stack=array();//helper array so to know the current level
    $pic=''; //products_in_category string
        $html[]='<ul class="'.$mainUlClass.'">';
        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){
            if ( $option === false ){
                $parent = array_pop( $parent_stack );
                $html[] = '</ul>';
                $html[] = '</li>';
                array_pop( $stack );
            }elseif ( !empty( $children[$option['value']['id']] ) ){
                $stack[]=$option['value']['id'];
                  $rt=$root_id>0 ? $root_id.'_' : '';
                  $cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);
                $html[]= '<li><a class="trigger right-caret" href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
          if (SHOW_COUNTS == 'true') {
            $products_in_category = tep_count_products_in_category($option['value']['id']);
            if ($products_in_category > 0) {
              $pic=' (' . $products_in_category . ')';
            }
          }
          $sm=0;        
          if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
            $sm=1;
            $html[]='<strong>'.stripslashes($option['value']['name']).$pic.'</strong>';
          }else{
            $html[]=stripslashes($option['value']['name']) . $pic;
          }
            $html[]='</a>';
            $html[] = '<ul class="'.$submenuUlClass.'">';
                $parent_stack[]=$option['value']['parent_id'];
                $parent = $option['value']['id'];
          }else{
        $rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
                $html[]= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';
        if (SHOW_COUNTS == 'true') {
          $products_in_category = tep_count_products_in_category($option['value']['id']);
          if ($products_in_category > 0) {
            $pic=' (' . $products_in_category . ')';
          }
        }
                if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {
          $html[]='<strong>'.stripslashes($option['value']['name']).$pic.'</strong>';
        }else{
          $html[]=stripslashes($option['value']['name']).$pic;
        }
        $html[]='</a>';
        }
       }
        $html[]='</ul>';
     $data = '<li class="dropdown">' .
             '<a class="dropdown-toggle" data-toggle="dropdown">' . HEADER_CATS . '</a>' .
               implode($html) .
             '</li>';
      return $data;
  }

includes/footer.php add:

<script type="text/javascript">
$(function(){
    $(".dropdown-menu > li > a.trigger").one("click",function(e){
        e.preventDefault();
        var current=$(this).next();
        var grandparent=$(this).parent().parent();
        if($(this).hasClass('left-caret')||$(this).hasClass('right-caret'))
            $(this).toggleClass('right-caret left-caret');
        grandparent.find('.left-caret').not(this).toggleClass('right-caret left-caret');
        grandparent.find(".sub-menu:visible").not(current).hide();
        current.toggle();
        e.stopPropagation();
    });
    $(".dropdown-menu > li > a:not(.trigger)").one("click",function(){
        var root=$(this).closest('.dropdown');
        root.find('.left-caret').toggleClass('right-caret left-caret');
        root.find('.sub-menu:visible').hide();
    });
})(jQuery);
</script>

user.css add:

.dropdown-menu>li /* To prevent selection of text */
{   position:relative;
    padding-left: 5px;
    -webkit-user-select: none; /* Chrome/Safari */        
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* IE10+ */
    /* Rules below not implemented in browsers yet */
    -o-user-select: none;
    user-select: none;
    cursor:pointer;
}
.dropdown-menu .sub-menu
{
    left: 100%;
    position: absolute;
    top: 0;
    display:none;
    margin-top: -1px;
    box-shadow:none;
}
.right-caret:after,.left-caret:after
 {  content:"";
    border: 5px solid transparent;
    border-top: 5px solid transparent;
    display: inline-block;
    height: 0;
    vertical-align: middle;
    width: 0;
    margin-left:15px;
}
.right-caret:after {   
    border-left: 5px solid #ffaf46;
}
.left-caret:after {
    border-right: 5px solid #ffaf46;
}

in includes/header.php after:

<?php echo '<li><a class="store-brand" href="' . tep_href_link(FILENAME_DEFAULT) . '">' . HEADER_HOME . '</a></li>'; ?>

add:

<?php echo '<li>' . tep_show_tree() . '</li>'; ?>

or somewhere else where you like it!

includes/languages/english.php add:

define('HEADER_CATS', '<i class="glyphicon glyphicon-cog"></i><span class="hidden-sm"> Categories</span> <span class="caret"></span>');

or any other glyphicon.

 

I tested it with Mozilla 30. IE 8, Chrome and Opera - seems to work properly in full and collapsed size. 

 

Hope you enjoy it!

 

J.J.

Edited by De Dokta

Share this post


Link to post
Share on other sites

Hi,

 

I overlooked that burt in the bootstrap version has removed the DB entry SHOW_COUNTS . This will simplify the code of the function tep_show_tree in html_output.php a little. ;)
function tep_show_tree($root_id = 0,$mainUlClass='dropdown-menu',$submenuUlClass='dropdown-menu sub-menu'){
    global $languages_id,$cPath_array, $html;
    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id='" . (int)$languages_id ."' order by sort_order, cd.categories_name");
    $items = array();
    while ($categories = tep_db_fetch_array($categories_query))  {
        $items[$categories['categories_id']] = array('name' => $categories['categories_name'], 'parent_id' => $categories['parent_id'], 'id' => $categories['categories_id']);
    }
    $citems=count($items);
    
    if($citems<=0) return '';
    elseif($citems==1) $children[] = $items; //in case we have one category item without subcategories, rare but possible
    else foreach( $items as $item ) $children[$item['parent_id']][] = $item;
        $loop = !empty( $children[$root_id] );
        $parent = $root_id;
        $parent_stack = array();
        $stack=array();//helper array so to know the current level
    $pic=''; //products_in_category string
        $html .='<ul class="'.$mainUlClass.'">';
        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) ){
            if ( $option === false ){
                $parent = array_pop( $parent_stack );
                $html .= '</ul>';
                $html .= '</li>';
                array_pop( $stack );
            }elseif ( !empty( $children[$option['value']['id']] ) ){
                $stack[]=$option['value']['id'];
                  $rt=$root_id>0 ? $root_id.'_' : '';
                  $cpath_new=count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack);
                $html .= '<li><a class="trigger right-caret" href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'">';
          $sm=0;        
          if((isset($cPath_array) && in_array($option['value']['id'], $cPath_array))){
            $sm=1;
            $html .='<strong>'.stripslashes($option['value']['name']).'</strong>';
          }else{
            $html .=stripslashes($option['value']['name']);
          }
            $html .='</a>';
            $html .= '<ul class="'.$submenuUlClass.'">';
                $parent_stack[]=$option['value']['parent_id'];
                $parent = $option['value']['id'];
          }else{
        $rt=$root_id>0 ? $root_id.'_' : '';
                $cpath_new= count($stack)<=0 ? 'cPath='.$rt.$option['value']['id'] : 'cPath='.$rt.implode('_',$stack).'_'.$option['value']['id'];
                $html .= '<li><a href="'.tep_href_link(FILENAME_DEFAULT, $cpath_new).'" >';

                if (isset($cPath_array) && in_array($option['value']['id'], $cPath_array)) {
          $html .='<strong>'.stripslashes($option['value']['name']).'</strong>';
        }else{
          $html .=stripslashes($option['value']['name']);
        }
        $html .='</a>';
        }
       }
        $html .='</ul>';
     $data = '<li class="dropdown">' .
             '<a class="dropdown-toggle" data-toggle="dropdown">' . HEADER_CATS . '</a>' .
               $html .
             '</li>';
      return $data;
  }

J.J.

Share this post


Link to post
Share on other sites

Hi,

 

someone asked me if it's possible to split the pulldown list into rows (could make sense if you have many, many categories).

Ok, I thought this should be possible.

I tried it with column-count tags in the user.css. The results were ok in Firefox, but strange in Chrome and Opera, and what IE did baffles all description! :wacko:

Ok, I thought, if not so, then perhaps with a bit of Javascript.

I added a "column" to the function in html_output.php:

function tep_show_tree($root_id = 0,$mainUlClass='dropdown-menu column',$submenuUlClass='dropdown-menu sub-menu'){

some definitions to the user.css:

@[member=media] screen and (min-width: 992px){
.column {
    list-style:none;
    width: 600px;
}
.column > li {
    float: left;
    width: 100%;  /* 1 column - default*/
    height: 2em;
    }
}

and some JS to the footer.php:

<script type="text/javascript">
function smartRows(){

///number of rows
var rows = $( ".dropdown-menu.column > li" ).size();

// number of columns
var columns = Math.ceil(rows/20);

var $ul = $('.column'),
    $elements = $ul.children('li'),
    breakPoint = Math.round($elements.length/columns);
    $ordered = $('<div></div>');

function appendToUL(i) {
    if ($ul.children().eq(i).length > 0) {
        $ordered.append($ul.children().eq(i).clone());
    }
    else $ordered.append($('<li></li>'));
}

function reorder($el) {
    $el.each(function(){
        $item = $(this);
        
        if ($item.index() >= breakPoint) return false;

        appendToUL($item.index());
        for (var i = 1; i < columns; i++) {
            appendToUL(breakPoint*i+$item.index());
        }
    });
    
    $ul.html($ordered.children().css('width',100/columns+'%'));
}

reorder($elements);
};

window.onload=smartRows();

The result: Works fine in Mozilla, Chrome and Opera, but I can find no way to persuade IE8 to follow the other browsers. It makes me crazy!

The problem is that IE persistently refuses to display the <ul> tagged with 'column' with a width of 600px.
Has anyone an idea?

----------------------------------------------------------------------------------------------------------------------------

 

And a small correction to above post. In the header.php replace:
<?php echo '<li>' . tep_show_tree() . '</li>'; ?>

with:

<?php echo tep_show_tree(); ?>
The <li> tags are obsolete here - says Firebug. :D
 
J.J.

 

 

Share this post


Link to post
Share on other sites
To continue and bring to an end my soliloquy: Sometimes the solution ist that simple! I moved the script from the footer to the header und now it works as it should - also in IE (8 in my case).
 
Finally it looks like that:
- end of header.php

<script type="text/javascript">
function smartRows(){
var rows = $('.dropdown-menu.row > li').size();
   if(rows > 20) {                      /// script only runs with 20 or more rows/categories
var columns = Math.ceil(rows/20);       /// maximum of rows per column
var $ul = $('.dropdown-menu.row'),
    $elements = $ul.children('li'),
    breakPoint = Math.round($elements.length/columns);
    $ordered = $('<ul></ul>');
function appendToUL(i) {
    if ($ul.children().eq(i).length > 0) {
        $ordered.append($ul.children().eq(i).clone());
    }
    else $ordered.append($('<li></li>'));
}
function reorder($el) {
    $el.each(function(){
        $item = $(this);
        if ($item.index() >= breakPoint) return false;
        appendToUL($item.index());
        for (var i = 1; i < columns; i++) {
            appendToUL(breakPoint*i+$item.index());
        }
    });
     $ul.removeClass('row').addClass('column');
     $ul.html($ordered.children().css('width',100/columns+'%'));
}
reorder($elements);
}
};
document.onload=smartRows();
</script>

- user.css

@[member=media] screen and (min-width: 768px){
.column {
    list-style:none;
    width: 500px;
}
.column > li {
    float: left;
    width: 100%;  /* 1 column - default*/
    height: 2em;
    }
}
.row{}

- first line of function tep_show_tree in html_output.php

function tep_show_tree($root_id = 0,$mainUlClass='dropdown-menu row',$submenuUlClass='dropdown-menu sub-menu'){

In that form the script splits the menu only on the first level into columns. It's possible to split also submenus - but that looks ugly and is somewhat confusing.....

 

J.J.

 

Share this post


Link to post
Share on other sites

Thanks for your feedback. But the main credit goes to gadlol whose code has enabled me to this solution .... :rolleyes:

Edited by De Dokta
ce7 likes this

Share this post


Link to post
Share on other sites

Hi

 

Here's a little more feedback.

 

Ive got this installed on our new site, and it works a treat.

 

Hopefully it will work on 2.3.4 etc.

 

Cheers

Grandpa

ce7 likes this

Share this post


Link to post
Share on other sites

Hopefully it will work on 2.3.4 etc.

Hi,

 

I've updated my installation of burts 2334BS to 2.3.4, where it works without any problems.

 

J.J.

ce7 likes this

Share this post


Link to post
Share on other sites

The same can be accomplished with the category_tree.php class.

 

There are several examples in the default bootstrap topic.

Share this post


Link to post
Share on other sites

The same can be accomplished with the category_tree.php class.

@@wHiTeHaT

 

Sorry, not the same! Your solution is a little bit different from mine. And I could not get your solution run with multiple levels of subcategories. Maybe I've overseen something - I always only get the main level and one sublevel, but sublevels below do not appear.

Share this post


Link to post
Share on other sites

@@wHiTeHaT

 

Sorry, not the same! Your solution is a little bit different from mine. And I could not get your solution run with multiple levels of subcategories. Maybe I've overseen something - I always only get the main level and one sublevel, but sublevels below do not appear.

 

can you point me to the latest version of your code? thanks

Share this post


Link to post
Share on other sites

Thanks for the compilation De Dokta!

I have changed it a bit and am using it here: test.fairtragen.de

Not quite finished yet, and only visible in xs or sm (phone or pad-sizes...)

:-)

 

Share this post


Link to post
Share on other sites

Hi,

 

I've updated my installation of burts 2334BS to 2.3.4, where it works without any problems.

 

J.J.

 

Hi

I was wondering whether you have made this into a module?

 

Id prefer to have this as a completely seperate module navbar which has the categories dropdown, and then to just install via admin.

 

I think it would be easier to manage long term.

I will see if i can put the files into a module and post them here if you haven't already done so.

Share this post


Link to post
Share on other sites

@@vampirehunter

 

I was wondering whether you have made this into a module?

 

Yes, I have.

 

Navbar with cat menu.zip

 

No files are overwritten. If your user.css is empty, rename the file for_user.css to user.css, else copy the content to your user.css.

 

I added an option to display the navbar cat menu in full view, collapsed view or both, selectable in admin. Personally I use it only in collapsed view. In full view I prefer the boxed nav menu with gadlols JS.

 

J.J.

vampirehunter likes this

Share this post


Link to post
Share on other sites

i still cannot understand why no one uses the category_tree.php build in for this?

burt likes this

Share this post


Link to post
Share on other sites

@@wHiTeHaT

 

Thanks to your insights from yesterday, i did ... but it is not yet fully functional, but getting close.

 

Got the main categories like tabs, got the view all (category) functioning, but need to finish the what's new in category carousel

 

Anyone interested, I'm trying to replicate this example, but listing the main categories in the navigation bar eg Hardware / Software / DVD / Gadgets

http://bootsnipp.com/snippets/featured/mega-menu-with-carousel-for-stores

vampirehunter, burt and gvv like this

Share this post


Link to post
Share on other sites

@@bruyndoncx very good, consider to also use in your cm_  module:

 

$oscTemplate->addBlock('<link href="mega_coriny.css" rel="stylesheet">' . "\n", 'header_tags');

Edited by wHiTeHaT

Share this post


Link to post
Share on other sites

@@vampirehunter

 

 

Yes, I have.

 

attachicon.gifNavbar with cat menu.zip

 

No files are overwritten. If your user.css is empty, rename the file for_user.css to user.css, else copy the content to your user.css.

 

I added an option to display the navbar cat menu in full view, collapsed view or both, selectable in admin. Personally I use it only in collapsed view. In full view I prefer the boxed nav menu with gadlols JS.

 

J.J.

Cool, thanks.

Share this post


Link to post
Share on other sites

i still cannot understand why no one uses the category_tree.php build in for this?

Correct me if im wrong, but I think was it because De Dokta said the category tree won't go past a certain number of sub levels?

 

I suppose it depends on how many sub categories are required. For mine, I think the default category_tree.php will be ok as i only need single sub categories, but others with tons of categories may need more.

Share this post


Link to post
Share on other sites

@@wHiTeHaT

 

Thanks to your insights from yesterday, i did ... but it is not yet fully functional, but getting close.

 

Got the main categories like tabs, got the view all (category) functioning, but need to finish the what's new in category carousel

 

Anyone interested, I'm trying to replicate this example, but listing the main categories in the navigation bar eg Hardware / Software / DVD / Gadgets

http://bootsnipp.com/snippets/featured/mega-menu-with-carousel-for-stores

yes, i'd love that mega menu!

 

I've seen these on some shops, and they look great.

 

 

I wanted to ask also,

 

is this still the correct way to link a specific category if hardcoding the specific links to some category?

<?php echo '<a href="' . tep_href_link(FILENAME_DEFAULT,'cPath=1') . '">Top</a>';?>
Edited by vampirehunter

Share this post


Link to post
Share on other sites

@@vampirehunter

 

there is no limitations in the category_tree class.... you can do all your want with it.

 

I see. I don't know then. Maybe De Dokta can explain the differences of the code he used.

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