Яндекс.Метрика

    Веб разработка

    Многоуровневое дерево с маркерами (HTML, CSS, jQuery). Запоминаем выбранный узел

    Дерево помнит выбранный узел по urlЭто продолжение темы про дерево. Хочу его довести до ранга «готов к внедрению». Потому повозился с Javascript и сделал запоминание выбранного узла на основе адреса ссылки.
    Если ссылка вложена в поддерево, дерево развернется до её уровня и, если у неё самой есть поддерево — оно тоже будет развернуто.

    Javascript сделал как смог, т.к. давно не использовал. Прошу помощи в доработке и оптимизации.

    В этой версии не менялся HTML и CSS, если хотите — посмотрите его в предыдущей статье.

    Javascript

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
      <script>
      /*<![CDATA[*/
       /*
         Построение дерева по готовому HTML списку.
      
         Выделяем узлы имющие поддеревья и добавляем у ним метку.
         Функция определяет поведение узлов дерева при клике на них.
       - Изменяет состояние маркера раскрытия (открыт/закрыт).
         - Узлы содержащие в себе другие узлы, по клику разворачиваются
          или сворачиваются, в зависимости от текущего состояния.
         - При переходе с одного узла на другой снимается выделение (.current)
          и пеходит на выбранный узел.
         - Определяет последний узел с поддеревом и скрывает соединительную
          линию до следующего узла этого уровня.
       */
       $(document).ready(function () {
       /* Расставляем маркеры на узлах, имющих внутри себя поддерево.
          Выбираем элементы 'li' которые имеют вложенные 'ul', ставим для них
          маркер, т.е. находим в этом 'li' вложенный тег 'a'
          и в него дописываем маркер '<em class="marker"></em>'.
          a:first используется, чтобы узлам ниже 1го уровня вложенности
          маркеры не добавлялись повторно.
       */
         $('#multi-derevo li:has("ul")').find('a:first').prepend('<em class="marker"></em>');
       // вешаем событие на клик по ссылке
         $('#multi-derevo li span').click(function () {
          // снимаем выделение предыдущего узла
          $('a.current').removeClass('current');
          var a = $('a:first',this.parentNode);
          // Выделяем выбранный узел
          //было a.hasClass('current')?a.removeClass('current'):a.addClass('current');
           a.toggleClass('current');
          toggleNode(this.parentNode);
          
         });
         postLoad();
       })
       // Выделил функцию разворачивания дерева в отдельную  
       function toggleNode(Node) {// node= li
         prepareLast(Node);
         // анимация раскрытия узла и изменение состояния маркера
         var ul=$('ul:first',Node);// Находим поддерево
         if (ul.length) {// поддерево есть
          ul.slideToggle(200); //свернуть или развернуть
          // Меняем сосотояние маркера на закрыто/открыто
          var em=$('em:first',Node);// this = 'li span'
          // было em.hasClass('open')?em.removeClass('open'):em.addClass('open');
          em.toggleClass('open');
         }  
       }
       // функция обработки последнего узла в уровне
       function prepareLast(Node) {
         /* если это последний узел уровня, то соединительную линию к следующему
         рисовать не нужно */  
         $(Node).each(function(){
          if (!$(this).next().length) {
            /* берем корень разветвления <li>, в нем находим поддерево <ul>,
             выбираем прямых потомков ul > li, назначаем им класс 'last' */
            $(this).find('ul:first > li').addClass('last');
          } 
         })
       }
       // функция разворачивания дерева до выбранной ранее ссылки
       function postLoad(){
         var url = window.location.toString();
         var max = 0;
         var a = null;
         $('#multi-derevo li span a').each(function(){
          // сравниваем адрес страницы и ссылку из атрибута
          if(url.indexOf(this.href) >= 0 && this.href.length > max){
            a = this;
            max = this.href.length;
          }
         });
         // если узел не виден, то разворачиваем дерево
         if ($(a).is(':hidden') || $(a).parents(':hidden').length) {
          var li = $(a).parents().filter('li');
          prepareLast(li);
          toggleNode(li);
         }
         // выделим выбранный узел
         if (a) {
          $(a).toggleClass('current');
         }
         else { // первый показ, выберем первую ссылку (можно убрать если не нужно)
          $('#multi-derevo li span a:first').toggleClass('current'); 
         }
       }
      /*]]>*/
      </script>


    Почему-то для первого уровня не получилось сделать авто раскрытие поддерева после загрузки, если узел был выбран ранее.
    Нуждается в доработке, с охотой приму все улучшения.

    У этого примера есть недостаток в том, что не запоминается состояние всех узлов, а только положение выбранного.
    Т.е. ранее дерево было развернуто на 3 уровня в глубь, то после перехода по ссылке с самого верха развернется только первый подуровень.

    Посмотрите пример.

    Еще хочу сделать модификацию с cookies, которая будет запоминать все развернутые поддеревья, т.е. если ранее пользователь развернул дерево и потом свернул верхний уровень, то про разворачивании вложенное дерево будет также развернуто. И конечно все состояние благодаря cookies будут сохраняться при переходе между страницами.

    ХабраНарод скажите подходит по тематике этот топика в этот блок или все ж перенести в Web-разработку или в jQuery — я сомневаюсь.