Jump to content
  • Checkout
  • Login
  • Get in touch

osCommerce

The e-commerce.

Categories Menu with multiple subs in the header navbar


Recommended Posts

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.

Link to comment
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.

Link to comment
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.

 

 

Link to comment
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.

 

Link to comment
Share on other sites

  • 3 weeks later...

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

Link to comment
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.

Link to comment
Share on other sites

  • 5 months later...

@@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

Link to comment
Share on other sites

  • 1 month later...

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.

Link to comment
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.

Link to comment
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

KEEP CALM AND CARRY ON

I do not use the responsive bootstrap version since i coded my responsive version earlier, but i have bought every 28d of code package to support burts effort and keep this forum alive (albeit more like on life support).

So if you are still here ? What are you waiting for ?!

 

Find the most frequent unique errors to fix:

grep "PHP" php_error_log.txt | sed "s/^.* PHP/PHP/g" |grep "line" |sort | uniq -c | sort -r > counterrors.txt

Link to comment
Share on other sites

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

 

Never mind but bootsrap multi submenu rendering is different..

 

http://bootsnipp.com/snippets/featured/multi-level-dropdown-menu-bs3

 

:blink:
osCommerce based shop owner with minimal design and focused on background works. When the less is more.
Email managment with tracking pixel, package managment for shipping, stock management, warehouse managment with bar code reader, parcel shops management on 3000 pickup points without local store.

Link to comment
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.

Link to comment
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.

Link to comment
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>';?>
Link to comment
Share on other sites

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

@@wHiTeHaT

 

But some of us do, and have given instruction so anyone else can as well.

 

@@Gergely

 

multi submenu also solved.

 

 

See my post: http://www.oscommerce.com/forums/topic/398284-oscommerce-23-bootstrap-nav-menu/

Follow the community build:

BS3 to osCommerce Responsive from the Get Go!

Check out the new construction:

Admin Gone to Total BS!

Link to comment
Share on other sites

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

Oh my god!

If someone find my solution useful, use it! If not, not!

If someone prefers GLWalkers great solution, use that! If not, not!

If there's any other/better solution for that out there, pls share it with the community........

 

So what?!

Link to comment
Share on other sites

@@wHiTeHaT

Yes I have noticed the original category_tree would not generate the correct path for nesting. Therefore I extended my version to make up for it.

 

I would however like to see the repaired category tree added so that we could better work with the output for unlimited types of menus.

 

Perhaps you could submit it as a bug fix?

Follow the community build:

BS3 to osCommerce Responsive from the Get Go!

Check out the new construction:

Admin Gone to Total BS!

Link to comment
Share on other sites

i can do that, but it requires also to update the bm_categories.box.php, it's layout (my POV), will look slicker.

 

(no bootstrap classes or html inside the category_tree.php gives below result)

 

Hi, sorry for sounding confused, but which files exactly need to be changed to get the exact output you have in your screenshot?

 

Thats exactly what i'm after. To have the Main categories individually show in the navbar, with a dropdown for subcategories.

 

So i would need to upload the new Category_tree.php and have a new modified bm_categories file,

 

Which other files are required to be edited or changed?

 

 

thanks

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...