RSS-стрічка усіх новин
rss_other
Twitter
Twitter

Дворівневе вертикальне меню, що розкривається (Accordion menu) для Joomla 1.5

Скажімо ми маємо меню такої "довгої" структури:

Дворівненве меню

Проте хочемо зробити, щоби підпункти меню відкривались при наведенні чи кліку мишкою. Буржуйською мовою це зветься Accordion menu.

Навчимось це робити самі, без встановлення сторонніх модулів.

Практика

Для цього нам треба стандартний модуль mainmenu і невеликий шматочок кода, який треба вставити в шаблоні всередині тегів <head> ... </head> (хоча не обов'язково, проте бажано саме там) в файлі /templates/YOURTEMPLATE/index.php.

В параметрах модуля треба встановити два параметра:

  • Завжди показувати пункти підменю - Так
    Завжди показувати пункти підменю - Так
  • ID тегу Меню - acmenu (чи інший ідентифікатор - такий самий треба вказати в коді, наведеному нижче.)
    ID тегу Меню - acmenu

А ось і код:

<script type="text/javascript"> 
    window.addEvent('domready', function() { 
         
        var menuid = '#acmenu'; // Set menu id  
        var parents = $$(menuid+' li.parent'); // Get all main level parents 
        var children = $$(menuid+' li.parent ul'); // Get all second level childs 
        var num =-1; // Temporary variable to set what menu has to be open by default 
         
        // The loop below is used to determine which menu is current 
        for (var i=0; i<parents.length; i++) {  
            var cid = parents[i].id; 
            if (cid  == 'current') { 
                num = i; 
                break; 
            } 
            var lis = $ES('ul li',parents[i]); 
 
            for (var j=0; j<lis.length; j++) { 
                if (lis[j].id  == 'current') { 
                    num = i; 
                    break; 
                } 
            } 
 
        } 
        //make the accordion 
        var accordion = new Accordion(parents,children, { 
            opacity: 50, 
            display: num, 
            alwaysHide: false, 
            onActive: function(toggler,element) {  
                element.setStyle('padding-left', '20px');   
                toggler.addClass('active'); 
            }, 
            onBackground: function(toggler,element) {  
                toggler.removeClass('active');   
            } 
        }); 
 
        //make it open on hover 
        $$(menuid+' li').addEvent('mouseenter', function() { this.fireEvent('click'); }); 
    });
</script>

Завантажити файл Завантажити код

Щоби на всіх сторінках сайту підвантажувався mootools, необхідний для роботи цього меню, додайте в шаблоні /templates/YOURTEMPLATE/index.php після тега

<jdoc:include type="head" />

таке:

<?php JHTML::_( 'behavior.mootools' ); ?>

Якщо ж ми хочемо, щоби меню відкривалось по кліку. Якщо клацати по Роздільнку, бо при кліку, скажімо, по Новинах підменю теж розкриється, але ви перейдете по посиланню.

Інакше кажучи, щоби меню розкривалось по кліку, пункт меню верхнього рівня має бути типу Роздільник.

Код треба дещо змінити - alwaysHide: false на alwaysHide: true і закоментувати рядок:

$$(menuid+' li').addEvent('mouseenter', function() { this.fireEvent('click'); });

Отже код в цьому випадку при тому ж самому id меню acmenu:

<script type="text/javascript"> 
    window.addEvent('domready', function() { 
         
        var menuid = '#acmenu'; // Set menu id  
        var parents = $$(menuid+' li.parent'); // Get all main level parents 
        var children = $$(menuid+' li.parent ul'); // Get all second level childs 
        var num =-1; // Temporary variable to set what menu has to be open by default 
         
        // The loop below is used to determine which menu is current 
        for (var i=0; i<parents.length; i++) {  
            var cid = parents[i].id; 
            if (cid  == 'current') { 
                num = i; 
                break; 
            } 
            var lis = $ES('ul li',parents[i]); 
 
            for (var j=0; j<lis.length; j++) { 
                if (lis[j].id  == 'current') { 
                    num = i; 
                    break; 
                } 
            } 
 
        } 
        //make the accordion 
        var accordion = new Accordion(parents,children, { 
            opacity: 50, 
            display: num, 
            alwaysHide: true, //!!!!!!!!!!!!!!!!!!! HERE
            onActive: function(toggler,element) {  
                element.setStyle('padding-left', '20px');   
                toggler.addClass('active'); 
            }, 
            onBackground: function(toggler,element) {  
                toggler.removeClass('active'); 
            } 
        }); 
 
        //make it open on hover 
        //!!!!!!!!!!!!!!!!!!! HERE  $$(menuid+' li').addEvent('mouseenter', function() { this.fireEvent('click'); }); 
    });
</script>

Завантажити файл Завантажити код

Теорія

В Joomla використовується двигун mootools. Ним ми і скористались. Mootools - бібліотека написана на JavaScript, що дозволяє веб-майстрам використовувати вже написані і відладжені функції.

Для початку заглянемо у HTML, що формує наше меню. Це необхідно для розуміння наступних кроків.

<ul class="menu">
  <li class="item51"><a href="/home"><span>Головна</span></a></li>
  <li class="parent item54"><a href="/news"><span>Новини</span></a>
    <ul>
      <li class="item60"><a href="/news/porady"><span>Поради</span></a></li>
    </ul>
  </li>
  <li class="item61"><a href="/forum"><span>Форум</span></a></li>
  <li class="item59"><a href="/downloads"><span>Завантаження</span></a></li>
  <li class="parent item57"><a href="/sitemap"><span>Мапа</span></a>
    <ul>
      <li class="item56"><a href="/sitemap/search"><span>Пошук</span></a></li>
    </ul>
  </li>
  <li class="parent item62"><span class="separator"><span>Роздільник</span></span>
    <ul>
      <li class="item63"><a href="#"><span>Пункт меню</span></a></li>
      <li class="item64"><a href="#"><span>Пункт меню</span></a></li>
      <li class="item65"><a href="#"><span>Пункт меню</span></a></li>
      <li class="item66"><a href="#"><span>Пункт меню</span></a></li>
    </ul>
  </li>
  <li class="parent item62"><span class="separator"><span>Роздільник</span></span>
    <ul>
      <li class="item63"><a href="#"><span>Пункт меню</span></a></li>
      <li class="item64"><a href="#"><span>Пункт меню</span></a></li>
      <li class="item65"><a href="#"><span>Пункт меню</span></a></li>
      <li class="item66"><a href="#"><span>Пункт меню</span></a></li>
    </ul>
  </li>
</ul>
</pre>
<p>Тепер спробуємо розібрати JavaScript-код по блоках:</p>
<pre class="brush:php;">window.addEvent('domready', function() {

domready - це стан, колиструктура HTML-документа вже отримана бравзером, проте графіка може ще продовжувати завантажуватись. Нам саме цей момент і треба.

Вищенаведеним кодом ми вказуємо оглядачеві одразу після настання події domready виконати написану далі функцію.

Встановлюємо ідентифікатор меню, щоби знати з яким саме меню працювати.

var parents = $$(menuid+' li.parent'); // Get all main level parents 
var children = $$(menuid+' li.parent ul'); // Get all second level childs

Функція $$ у mootools дозволяє отримати всі елементи за заданими ідентифікаторами. В нашому випадку ми у змінну parents збираємо всі елементи li що належать елементу з id = "acmenu" і мають клас parent. У змінній parents тепер зберігається масив приблизно такого вигляду:

Елемент 0

  • Новини
  • Елемент 1:

  • Мапа
  • Елемент 2:

  • Роздільник
  • Елемент 3:

  • Роздільник
  • Не важко зрозумти, що у масиві children тепер зберігаються блоки підменю. Приблизно так:

    Елемент 0:

    
    

    Елемент 1:

    
    

    Елемент 2:

    
    

    Елемент 3:

    
    

    Далі ми використаємо масив parents, як список елементів сторінки, що є перемикачами, тобто теги li верхнього рівня. У масив children те, над чим будуть здійснюватись дії у випадку спрацьовування перемикача, тобто списки ul другого рівня.

    Тепер іде допоміжна частина коду:

    var num =-1; // Temporary variable to set what menu has to be open by default 
     
    // The loop below is used to determine which menu is current 
    for (var i=0; i<parents.length; i++) {  
        var cid = parents[i].id; 
        if (cid  == 'current') { 
            num = i; 
            break; 
        } 
        var lis = $ES('ul li',parents[i]); 
    
        for (var j=0; j<lis.length; j++) { 
            if (lis[j].id  == 'current') { 
                num = i; 
                break; 
            } 
        } 
    
    }
    

    Задачею цього блоку є встановити значення змінної num. Нижче воно буде використано, що вказати скрипту, який з пунктів меню має бути розкритий при завантаження сторінки. За замовчуванням встановлено -1, що відображає меню повністю закритим.

    Якщо ж, наприклад, ви перейшли за посилання у Поради, то хотіли би бачити меню із вже відкритим пунктом Новини.

    * Головна
    * Новини
          o Поради
    * Форум
    * Завантаження
    * Мапа
    * Роздільник
    * Роздільник
    

    У поточного пункту меню стандартний модуль mainmenu виводить id = 'current'. Отже вище наведеним кодом ми і визначаємо, де розташований цей current. В результаті спрацьовування цього коду у змінній num буде збережено порядовий номер пункта меню верхнього рівня, що має бути розкритим при завантаженні сторінки. В нумерацію не включаються пункти меню, що не мають підпунктів, наприклад Головна. Тому в даному випадку num може змінюватись від -1 (закриті всі) до 3 (відкритий другй Роздільник).

    Окремо варто зазначити про місце в коді

    var lis = $ES('ul li',parents[i]);
    

    Функція mootools $ES вибирає всі піделементи з певного елемента (тега-контейнера) HTML. Елемент-контейнер задається другим параметром (в нашому випадку це поточний i-й з масиву parents - parents[i] ). У першому параметрі ми вказуємо, що серед піделементів треба обрати лише елементи li спика ul.

    Отже, якщо, наприклад, в поточному parents[i]  (тобто i=1) був

    Елемент 1:

    <li class="parent item57"><a href="/sitemap"><span>Мапа</span></a>
      <ul>
        <li class="item56"><a href="/sitemap/search"><span>Пошук</span></a></li>
      </ul>
    </li>
    

    то у змінній lis опиниться один елемент (у Розділька їх було би 4)

    <li class="item56"><a href="/sitemap/search"><span>Пошук</span></a></li>
    

    Таким чином у циклі перебираються по черзі всі пункти меню верхнього рівня, що мають підменю. Якщо такий пункт меню має атрибут id='current', то у змінній num зберігається його порядковий номер. Якщо атрибута нема, то перебираються пукти підменю. Якщо у пункті підменю зустрічається атрибут id='current', то у num зберігається порядковий номер батьківсього пункта меню. Тобто і для пункта Пошук і для пункта Мапа num буде дорівнювати 1. Це тому, що, чи відкрито сайт за посиланням Мапа, чи за посиланням Пошук - меню Мапа має бути розгорнуте.

    //make the accordion 
    var accordion = new Accordion(parents,children, {
    

    Тепер ми створюємо новий об`єкт accordion класу Accordion.

    Короткий відступ про класи і об'єкти на прикладі аналогії.

    Скажімо, існує розписаний на папері штатний розклад дивизій (аналогія до класу):

    • Стрілкова дивізія
    • Танкова дивізія
    • Моторизована дивізя

    Такий опис (клас) - описує, з чого дивізія повинна складатись, які бойові задачі врішувати. Проте це лише опис, а не сама дивізя. У класі є властивості (командир дивізі - 1 чол, командирів полків - 10 чол. і т.д.) і методи (дивізія повинна вміти: атакувати, окопуватись, захоплювати населений пункт). Зрозуміло, що у стрілкової дивізії властивості інші (кількісні показники), ніж у танкової. Так само вони можуть вирішувати різні бойові задачі (методи класу).

    Штаб може дати наказ формувати дивізію за штатним розкладом:

    var Стрілкова Дивізія ім.Клима Савура = new Стрілкова дивізія();

    У дужках можуть бути за певною формою перелічені параметри створення дивізії, відмінні від тих, що є за замовчуванням. Наприклад, можна вказати іншу кількість бронетехніки.

    Аналогічо, у класі Accordion описано, як буде працювати меню, що розкривається.

    Ми створюємо об'єкт accordion за зразком класа Accordion. Вказуємо йому, які елементи є перемикачами (вони зберігаються у parents), а які, власне, тими, що розкриваються (вони зберігаються children), а потім описуємо у фігурних дужках додаткову поведінку такого меню, відмінну від стандартної.

    opacity: 50,
    

    Прозорість. Не обов'язкова річ. Можна погратись зі значеннями від 0 до 100.

    display: num,
    

    Порядковий номер пункту меню, що має бути розкритий при завантаженні сторінки. Меню, що не розкриваються, в переліку участі не беруть.

    alwaysHide: false,
    

    Якщо встановлено у true, то можливо закрити всі елементи. Інакше - один завжди лишатиметься відкритим.  У випадку з розкриття меню при наведенні мишкою є нюанси, через які цей параметр має бути false. У випадку розкриття мишкою - на ваш смак. Можете погратися і подивитись на результат.

    onActive: function(toggler,element) {  
            element.setStyle('padding-left', '20px');   
            toggler.addClass('active'); 
        }, 
        onBackground: function(toggler,element) {  
            toggler.removeClass('active'); 
        } 
    });
    

    Тут викликаються дві функції onActive і onBackground, що дозволяють впливати на елементи меню у момент відкриття чи закриття відповідно. Функції мають два параметри - перемикач toggler і елемент, що розкривається - element. При розкритті пункта меню, підменю отримує поле ліворуч у 20 пікселів (без нього підменю на одному рівні з меню верхнього рівня і додаєть відкритому пункту меню верхнього рівня клас active - наприклад щоби у CSS для активного пункту прописати свій колір чи своє тло.

    При закритті елемента клас active з нього вилучається.

    Помітьте, що дії на toggler - це дії над елементом меню верхнього рівня, а дії над element - над підменю, що відкривається.

    //make it open on hover 
       $$(menuid+' li').addEvent('mouseenter', function() { this.fireEvent('click'); }); 
    });
    

    Цей рядок вчить меню розкриватись при наведенні миші. Справа в тому, що класс Accordion вміє розкривати/закривати елемент лише по кліку. Тому його довелось обдурити. Всім li елементам нашого меню при наведенні миші вказано запускати ту подію, яка би сталась при кліку мишею (перехід за посиланням тут не вважається подією).

    Коментарі, виправлення, запитання вітаються!

    Коментарі   

     
    Footniko
    #41 Footniko 28.12.2011, 18:43
    А де змінити швидкість розкривання меню, не підкажете?
     
     
    Gruz
    +1 #42 Gruz 28.12.2011, 19:01
    Цитую Footniko:
    А де змінити швидкість розкривання меню, не підкажете?



    [img width=600]http://static.xscreenshot.com/2011/12/28/19/screen_08bfa6d78b7032779e7ffec509c08d84

    Додай duration:500

    500 - це за замовчуванням в мілісекундах, тобто число сам виставляй

    Ілюстрація:
    http://view.xscreenshot.com/08bfa6d78b7032779e7ffec509c08d84


    Документація:
    http://mootools.net/docs/more/Fx/Fx.Accordion
    http://mootools.net/docs/core/Fx/Fx
     
     
    Footniko
    #43 Footniko 28.12.2011, 21:43
    Gruz,
    Супер! Дуже дякую за швидку відповідь! :-)
     
     
    votia
    #44 votia 11.01.2012, 23:44
    Делал все точно написаному. Но в итоге у меня меню постоянно раскрыто все. Что делать, помогите плз!
     
     
    Gruz
    #45 Gruz 12.01.2012, 00:08
    Цитую votia:
    Делал все точно написаному. Но в итоге у меня меню постоянно раскрыто все. Что делать, помогите плз!


    Поставити Web Developer розширення або ж той самий FireBug і подивитись на помилки ява-скрипта.