Jump to content



Photo
* * * * * 2 votes

Categories Menu with multiple subs in the header navbar


  • Please log in to reply
52 replies to this topic

#1   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 18 July 2014 - 13:04

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:
Attached File  collapsed.jpg   30.39KB   54 downloadsAttached File  desktop.jpg   50.25KB   55 downloads
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='&nbsp;(' . $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='&nbsp;(' . $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, 18 July 2014 - 13:08.


#2   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 19 July 2014 - 08:46

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.



#3   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 23 July 2014 - 12:50

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:

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

 

 



#4   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 29 July 2014 - 19:48

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

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

 



#5   CJAnnihilatorCustoms

CJAnnihilatorCustoms
  • Members
  • 8 posts
  • Real Name:Clark

Posted 19 August 2014 - 16:02

Nobody is giving you feedback, so I've gotta step up and say keep at it, this is helping me a ton. 



#6   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 19 August 2014 - 22:46

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


Edited by De Dokta, 19 August 2014 - 22:48.


#7   grandpaj

grandpaj
  • Members
  • 905 posts
  • Real Name:John
  • Gender:Male
  • Location:New Zealand

Posted 19 August 2014 - 23:07

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



#8   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 20 August 2014 - 08:21

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.



#9   wHiTeHaT

wHiTeHaT
  • Community Team
  • 1,135 posts
  • Real Name:Henry
  • Gender:Male
  • Location:Netherlands

Posted 20 August 2014 - 08:42

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

 

There are several examples in the default bootstrap topic.


Do you need an osCommerce website? Do you want to have an Responsive osCommerce CONTACT ME as i am for HIRE

#10   wHiTeHaT

wHiTeHaT
  • Community Team
  • 1,135 posts
  • Real Name:Henry
  • Gender:Male
  • Location:Netherlands

Posted 20 August 2014 - 08:58

Here is the topic post with attachment.


Do you need an osCommerce website? Do you want to have an Responsive osCommerce CONTACT ME as i am for HIRE

#11   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 20 August 2014 - 11:31

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.



#12   vampirehunter

vampirehunter
  • Members
  • 700 posts
  • Real Name:vampire

Posted 22 January 2015 - 11:19

@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



#13   fohenrolle

fohenrolle
  • Members
  • 1 posts
  • Real Name:Leon

Posted 28 February 2015 - 10:13

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...)

:-)

 

#14   vampirehunter

vampirehunter
  • Members
  • 700 posts
  • Real Name:vampire

Posted 02 March 2015 - 11:24

Here is the topic post with attachment.

 

Your zip file doesn't seem to have any categories links in the header.

 

do you have to manually add it in?



#15   vampirehunter

vampirehunter
  • Members
  • 700 posts
  • Real Name:vampire

Posted 02 March 2015 - 11:48

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.



#16   De Dokta

De Dokta
  • Members
  • 329 posts
  • Real Name:Dr. J. Bachmann
  • Gender:Male
  • Location:Germany

Posted 06 March 2015 - 12:41

@vampirehunter

 

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

 

Yes, I have.

 

Attached File  Navbar with cat menu.zip   8.05KB   124 downloads

 

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.



#17   wHiTeHaT

wHiTeHaT
  • Community Team
  • 1,135 posts
  • Real Name:Henry
  • Gender:Male
  • Location:Netherlands

Posted 06 March 2015 - 13:06

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


Do you need an osCommerce website? Do you want to have an Responsive osCommerce CONTACT ME as i am for HIRE

#18   bruyndoncx

bruyndoncx

    osCommerce Teenager

  • Members
  • 3,770 posts
  • Real Name:Carine Bruyndoncx
  • Gender:Female
  • Location:Belgium/ Antwerp/ Turnhout/ Arendonk

Posted 06 March 2015 - 15:53

@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...usel-for-stores
KEEP CALM AND CARRY ON
FYI Just upgraded from PHP 5.3 to PHP 5.5  and saw big performance improvement.
But be aware php 5.5 is more strict about things.
UTF8-without BOM, no extra spaces allowed at the beginning or end of your php file, or your redirects wont work.
No double declarations of functions allowed - used to slip through the cracks ...

#19   wHiTeHaT

wHiTeHaT
  • Community Team
  • 1,135 posts
  • Real Name:Henry
  • Gender:Male
  • Location:Netherlands

Posted 06 March 2015 - 16:55

@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, 06 March 2015 - 16:55.

Do you need an osCommerce website? Do you want to have an Responsive osCommerce CONTACT ME as i am for HIRE

#20   Gergely

Gergely

    Json Juggler

  • Community Team
  • 2,112 posts
  • Real Name:Gergely Tóth
  • Gender:Male
  • Location:Budapest

Posted 06 March 2015 - 17:49

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...opdown-menu-bs3
 


Edited by Gergely, 06 March 2015 - 17:49.

Next PHP changes will kill the current codes on the following years. We should do programing for the future and never stick in the present.

My addons: Conversion Tools::Hungarian Translation::Email Templates::URL redirection
 
Development Works: Setup Languages::Email Templates::Languages from ini files::Parcel Shops::Facebook App
 
What core codes have been complained?

In orders table payment_methods value would be better if payment class name used than payment's language name.
In the orders class we found order status does not contains $order->info['orders_status'] but instead there is $order->info['orders_status_name'], and that property is language dependant.
We can not identify in order the customer language.