jQuery: Древовидное меню c памятью, возможность добавления подуровней

    Скрипт, — реакция на частые просьбы о подобной разработке на форуме javascript.ru. Появились мысли, что интерес проявится
    и у Хаброжителей.

    Плагин делался быстро, — заинтересовала идея реализации произвольнной вложенности табов без наращивания скриптов. Большую помощь оказал devote. Позже, по просьбе, были добавлены: cookie-память и установка классов к активным вкладкам.


    Об HTML cоставляющей:

    HТML код для вкладок первого уровня выглядит так:
    <ul>
    <li><a href="#">категория 1</a>V</li>
    <li><a href="#">категория 2</a></li>

    </ul>

    Красным показана место вставки очередного подуровня меню (метка вставки только для первой ссылки, для остальных пунктов, — по аналогии; уровень вложенности может различаться по пунктам меню), — уровень вложенности регулируете сами, *имхо более трёх-4x уровней затрудняет юзабилити…

    В последнем уровне каждой ветки, в тегах <a ...> проставляем в атрибутах href="#" реальные ссылки
    (если это необходимо).

    Плагин запускается установкой кода <script>$('#nav_menu_content').tree_menu()</script>, после последнего тега меню; где — #nav_menu_content, идентификатор меню.

    Код HTML — странички вместе со скриптом, под спойлером, к версии библиотек jQuery, — вроде не чувствительно, проверялось от версии 1.4.1 до 1.9.1.
    HTML + скрипт
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru" dir="ltr">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
    <script type="text/javascript" src="http://yandex.st/jquery/1.9.1/jquery.min.js"></script>
    
    <style>
        #nav_menu_content * {
            margin: 0;
            padding:  0;
            margin: 0;
            padding:  0;
            -moz-user-select: none;
            -khtml-user-select: none;
            user-select: none;
        }
        #nav_menu_content{
            list-style: none;
            padding-left: 12px;
            display:inline:block;
            width:180px;
        }
        #nav_menu_content ul {
            list-style-position: outside;
            text-align:left;
            padding-left: 12px;
            list-style: none;
            display: none;
        }
        #nav_menu_content li {
            list-style: none;
            margin:4px auto 4px 4px;
            padding:2px 4px;
        }
    
        #nav_menu_content li {
            border:1px solid #C4C4C6;
            background-color: #E0EDED;
            font-size:120%;
            text-shadow:1px 1px 1px #83A0A0;
        }
        #nav_menu_content li ul li{
            border:1px solid blue;
            text-shadow:none;
            font-size:80%;
            background-color: #C1C1E5;
        }
        #nav_menu_content li ul li ul li{
            font-size:90%;
            border:1px solid green;
            background-color: #BCDEBC;
        }
       #nav_menu_content > li > a:before {
           background: url(http://hostjs-mybb2011.narod.ru/img/icon.png) 0 0 no-repeat transparent;
           content: " ";
           display: block;
           float: left;
           height: 16px;
           margin-left: 2px;
           margin-top: 2px;
           width: 16px;
        }
       #nav_menu_content > li > a.active:before {
           background-position:0 -16px;
        }
    
       #nav_menu_content a {
           padding: 1px 4px!important;
           -moz-user-select: none;
           -khtml-user-select: none;
           user-select: none;      
        }
    
       #nav_menu_content > li > ul > li > a.active {
            -moz-box-shadow: 0 0 3px #620D62; /* Для Firefox */
            -webkit-box-shadow: 0 0 3px #620D62;
             box-shadow: 0 0 3px #620D62; /* Параметры тени */
             font-weight:700;
        }
    
        /*Cтиль Ссылки на текущую страницу*/
       #nav_menu_content li ul li.a-active {
    
            background-color:#D6D6FA!important;
            border:1px dotted #9D9DBA!important;
            -moz-box-shadow: 0 0 3px rgba(0,0,0,0.5); /* Для Firefox */
            -webkit-box-shadow: 0 0 3px rgba(0,0,0,0.5);
            box-shadow: 0 0 3px rgba(0,0,0,0.5); /* Параметры тени */
        }
       li.a-active a{
            color:#0000CC!important;
            font-weight:700;
        }
        #nav_menu_content li ul li ul li ul li{
            border:1px solid green;
    
            background-color: #C1C1E5;
            color:#fff!important;
        }
        #nav_menu_content li ul li ul li ul li a{
           padding: 1px 0 1px 4px!important;
            color:#000;
        }
    </style>
    
    <script type="text/javascript">
    function setcookie(a,b,c) {if(c){var d = new Date();d.setTime(d.getTime()+c);}if(a && b) document.cookie = a+'='+b+';path=/'+(c ? '; expires='+d.toUTCString() : '');else return false;}
     function getcookie(a) {var b = new RegExp(a+'=([^;]){1,}');var c = b.exec(document.cookie);if(c) c = c[0].split('=');else return false;return c[1] ? c[1] : false;}
    </script>
    
    <script type="text/javascript">
    
    $.fn.tree_menu = function() {
    
    
            var nav = this;
            var uls = nav.find("ul");
    
    
    	var coci_MENU= getcookie( "MENU" );
    	if (coci_MENU) {
    	setcookie( "MENU",coci_MENU ,30*3600*24*1000 );
    
    	// тут получаем индексы из куки и перебираем их:
            var showedElems = ( coci_MENU || "" ).split(",");
            for( var i = 0; i < showedElems.length; i++ ) {
                // отображаем при загрузке то что надо.
                $( uls[ showedElems[ i ] ] ).prev('a').addClass('active')  //Добавляем класс к Активным эл-там меню;
                $( uls[ showedElems[ i ] ] ).show();
            }}
    
    
            //Добавляем класс к ссылке на текущую страницу, если она есть;
            var Url = document.URL.split('#')[0];
            $('> li ul',nav).find('a:not([href^="#"])').each(function() {
                var S = $(this).attr('href').split('#')[0];
                if(S&&Url.indexOf(S)!=-1) $(this).parent().addClass('a-active');
            });
    
            nav.find("a").click(function() {
                var Lnk=$(this).attr("href");
                if(Lnk==''||Lnk.indexOf('#')==0) setcookie( "MENU",1 ,-1);
                var self = $(this).next();
                if ( self.length == 0 ) return;
    
    
                var showedElems = [];
                uls.each(function( index ){
    
                        if ( this === self[0] ) {
                            if ( self.css('display') == "none" ) {
                               showedElems.push(index);$(this).prev('a').addClass('active');
                            }  else $(this).prev('a').removeClass('active');
                            $( this ).slideToggle(400);return true;
                        } 
                        if ( jQuery.inArray( this, self.parents( "ul" ) ) == -1 ) {
                            $(this).prev('a').removeClass('active');$(this).slideUp(400);return true;
                        }
                        showedElems.push(index);
               });
    
                    // эту переменную  суём в куки,
                    setcookie( "MENU", showedElems.join(",") ,30*3600*24*1000 );
    
                    return false;
    
            });
    }
    </script>
    </head>
    <body style="padding-left:23%;padding-top:60px;">
    
    <ul id='nav_menu_content'>
        <li><a href='#0'>Категория 1</a>
            <ul>
                <li><a href="#">Субкатегория 1</a>
                    <ul>
                        <li><a href="view_cat.php?...">Подкатегория 1</a></li>
                        <li><a href="view_cat.php?...">Подкатегория 2</a></li> 
                    </ul>
                </li>
               <li><a href="#">Субкатегория 2</a>
                    <ul>
                        <li><a href="view_cat.php?...">Подкатегория 3</a></li>
                        <li><a href="view_cat.php?...">Подкатегория 4</a></li>
                    </ul>
                </li>
               <li><a href="#">Субкатегория 3</a>
                    <ul>
                        <li><a href="#">Подкатегория 5</a>
                            <ul>
                                <li><a href="view_cat.php?...">№1.  категория 1</a></li>
                                <li><a href="view_cat.php?...">№3.  категория 2</a></li>
                           </ul>
                        </li>
                        <li><a href="view_cat.php?...">Подкатегория 6</a></li>
                    </ul>
                </li>
            </ul>
        </li>
        <li><a href='#0'>Категория 2</a>
            <ul>
               <li><a href="#">Субкатегория 4</a>
                    <ul>
                        <li><a href="view_cat.php?...">Подкатегория 7</a></li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>   <script>$('#nav_menu_content').tree_menu();</script> <!-- Запуск -->
    
    
    </body>
    </html>
    
    

    ссылка на демо
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 17

      +2
      А какой смысл вставлять картинку и еще и onload на неё ставить? Почему не написать просто
      <script>InitFunk();</script>
      

      Да и вообще как-то загадочно выглядит, тот же CSS в котором используются префиксы khtml, но при этом нет тех же свойств для webkit, не говоря о том, что тот же box-shadow давно уже без префиксов работает. Хранение в куки? Это при том, что даже IE8 поддерживает Web Storage. Про сам скрипт промолчу :)
        0
        А какой смысл вставлять картинку и еще и onload на неё ставить? Почему не написать просто
        Считаем, что скрипт устанавливается подгружаемым файлом в хидер, Скрипт должен запускаться после наличия HTML кода самого меню, типично запускают по событию загрузки DOM, но при длинном контенте страницы восстановление меню в нужное состояние может затянуться. Поэтому иницируем быстрый запуск по onload картинки сразу после подгрузки меню.
          0
          Хранение в куки? Это при том, что даже IE8 поддерживает Web Storage. Про сам скрипт промолчу :)
          Были просьбы не мудрствуя сделать именно на куках с поддержкой и 6 версии ИE (*перейти на Storage — пару строк, *скрипт куков спецом вынесен
            +1
            Если скрипт должен запускаться после кода меню, то и запускайте сразу после кода меню, просто вызовом нужной функции, не нужно захламлять dom бесполезными картинками и событиями. Скрипт с функцией запустится сразу же независимо от того загрузилась ли страница, а вашем случае затупит картинка — затупит и меню.
              0
              Поправил.
                +1
                Кстати не знают может это тоже задумка из серия поддержки IE6. Но обычно в древовидных меню не сворачиваются дети когда сворачиваешь родителя. А то залез ты куда-то в дебри 4 уровня, потом нажал случайно на категорию 2, в итоге категория 1 свернулась, а при попытке вернуться назад — видишь, что все ей внутренности, тоже сложились и заново ищешь нужный пункт.
                  0
                  Странно — могу задемить скриншоты, проверялось с 4 машин в ИЕ6 -7 -8
                  Я думаю, что тестили в эмуляторах ИЕ — там были подобные эффекты
                    +1
                    Я вообще-то в несколько более свежих браузерах смотрел IE10, Chrome 24 :) Возможно вы не поняли мысль. Откройте к примеру Категория 1 -> Субкатегория 3 отображаются Подкатегория 5 и 6, а теперь промахнитесь мимо Подкатегория 6 и нажмите на Категория 2, в итоге первая категория свернулась, жмем быстро обратно на Категория 1, но видим что Субкатегория 3 также свёрнута (хотя обычно так не принято делать, попробуйте в том же виндовом проводнике похожие действия с деревом каталогов).
                    0
                    Но обычно в древовидных меню не сворачиваются дети когда сворачиваешь родителя.
                    Тут был выбор, либо простота и сворачивать всё, при последующем клике, либо функция запоминания разрастается до неимоверных размеров из-за многоциклов проверки по всем массивам, сейчас она помнит только последнюю открытую последовательность пути.
                      0
                      В принципе Вы первый, кто это отметил, подумаю над реализацией
            0
            Если скрипт должен запускаться после кода меню, то и запускайте
            Понял ( тут на форумах майб нет поддержки загрузки скриптов не в хидер или футер, извращение отсюда. Да, действительно: легко заменить картинку на <script>InitFunk();</script>
              0
              Не понимаю, почему в одном случае вы используете #nav_menu_content, в другом .left_nav_menu
              А почему скрипт нельзя оформить в виде плагина?

              Ведь можно же записать $('> li ul', nav) вместо $('.left_nav_menu > li ul')
                0
                Я думаю что автор надееться что заданый селектор буде искаться в данном контексте (nav). Но это не так, оно всеравно будет подставлять ".left_nav_menu" к "> li ul"
                Вот накинул пробный тест cssdeck.com/labs/c0itf3wb/0
                  0
                  ButscH, не вылизовал код, посколь он для такого совместного действия: как память и произвольная вложенность, достаточно короток, ближайщий похожий плагин с достаточной степенью вложенности без запоминания 6 кб
                  0
                  почему скрипт нельзя оформить в виде плагина
                  Поправил
                    0
                    Чуть ускорил
                      0
                      спасибо, реализовал ваш код на кулинарном сайте

                      Only users with full accounts can post comments. Log in, please.