Как составить дерево меню

Время на прочтение
3 мин

Количество просмотров 18K

В процессе работы над одним из проектов передо мной встала задача создания сворачиваемого дерева папок на основе сведений о нем в базе данных. Для уточнения, это выглядит примерно так:

image

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

Мы имеем следующую структуру таблицы в БД:
id — идентификатор строки
level — уровень элемента
left_key — левый ключ
right_key — правый ключ
caption — название каталога.
Более подробно о создании такой таблицы вы можете узнать здесь.
Посредством AJAX эти данные, отсортированные по значению поля left_key, поступают на клиента, где мы должны построить на их основе вложенный список.
Список должен иметь следующую структуру:

Элемент <ul>, содержащий:
    элемент <li>, представляющий собой каталог. 
        Далее, в нем всегда есть элемент <span>, содержащий название каталога. 
        При условии наличия дочерних папок, в нем есть элемент <ul>
            и так далее...
    и так далее... 

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

var getChilds = function(trgt, arr){
    var childs = Array();
    var length = arr.length;
    for (var i = 0; i < length; i++){
        var curr = getInts(arr[i]);
        var under = (curr.level > trgt.level) ;
        var l_in =  (curr.left_key > trgt.left_key);
        var r_in =  (curr.right_key < trgt.right_key);
        if (under && l_in && r_in) childs.push(arr[i]);
    }
    return childs;
}

Эта функция делает не что иное, как проверку каждого элемента массива по заданному элементу этого же массива, то есть: если уровень рассматриваемого элемента ниже уровня заданного элемента, при том что левые и правые ключи соответственно находятся в промежутке между левым и правым ключом заданного элемента, то записываем рассматриваемый элемент в массив дочерних элементов.
Функция getInts в данном случае занимается преобразованием значений полей level, left_key и right_key в числовые значения. Нужно это для того, чтобы происходило сравнение чисел, а не строк.

var getInts = function(arr){
    temp = Object();
    for (var i in arr){
        if (i!='name') temp[i] = parseInt(arr[i])
        else temp[i] = arr[i]
    return temp
}

После получения дочерних элементов, необходимо очистить от них исходный массив. Я не придумал ничего лучше, чем написать еще одну функцию.

var cleanDuplicates = function(from, elements){
    var length = arr.length;
    for (var i = 0; i < length; i++){
        if ($.inArray(from[i], elements) > -1)
            delete from[i]
    }
    return from;
}

Здесь массив elements содержит элементы, которые требуется удалить из массива from.
И, наконец, строим список, используя вышеприведенные функции.

var ul = function(arr){
    if (!arr[0]) return ''
    else{
        var html = '<ul>';
        var length = arr.length;
        for (var i = 0; i < length; i++){
            var el = arr[i];
            var childs = getChilds(el, arr);
            arr = cleanDuplicates(arr, childs)
            html += '<li>'+'<span id="cat_'+el.id+'">'+el.name+'</span>'
            html += ul(childs)
            html += '</li>'
        }
        return html + '</ul>'
    }
}

Пример использования:

'<div class="tree">' + ul(data) + '</div>'

где data — данные, полученные с сервера.
Для конечного преобразования этого списка в древовидное меню существует достаточно большое количество плагинов для jQuery, к примеру jQuery Treeview.
Работа с ним достаточно проста. Необходимо сперва подключить сам jQuery, затем этот плагин. После подключения достаточно просто натравить его на блок, содержащий ваш сгенерированный список. Если брать из примера, то это элемент $(‘.tree’).

$('.tree').treeview();

В результате мы имеем древовидное меню, которое поддерживает сворачивание/разворачивание по клику на папке (по умолчанию строится развернутое дерево папок, но благодаря настройкам плагина возможны варианты, подробнее читайте здесь).

P. S. Обратите внимание, что плагин jQuery Treeview больше не развивается, но для построения простого древовидного меню функций этого плагина более чем достаточно. Сам разработчик предлагает использовать плагин JqTree.

В справочнике «Дерево меню» должны быть определены все группы меню, которые должны быть доступны гостю для выбора. Т.е. все группы и подгруппы всех групп.
Группы и подгруппы блюд в Дереве меню создаются в виде страниц селекторов.
Все страницы селекторов должны находиться на единственном уровне – первом. В том числе и предустановленная страница селектора «Начальный выбор».
Для создания иерархической структуры меню, страницы следующего уровня вкладываются в страницы предыдущего уровня в виде кнопок селектора.
Например, если группа меню «Основные блюда» содержит подгруппы «Супы» и «Салаты», то страницы селекторов «Супы» и «Салаты» следует вложить в страницу селектора «Основные блюда».
Блюда для Дерева меню создаются в справочнике «Меню» и далее выбираются на нужные страницы селекторов второго уровня в виде кнопок селектора.
Кнопки селектора – это название селекторов или блюд, вложенных на страницу селектора (т.е. это названия групп блюд или блюд, которые будут доступны гостю для выбора блюд из меню).

При стандартных настройках «Дерево меню» имеет максимум трехуровневую структуру и блюда размещаются только на втором уровне селекторов. Но при использовании расширенного свойства возможна бесконечная вложенность страниц селекторов, и размещение блюд на любом уровне селекторов (исключая уровень «Начальный выбор»), не исключая совместное размещение и блюд, и селекторов на одной странице селектора.

Для создания трехуровневой структуры Дерева меню сделайте следующее:

  • Создайте страницы селекторов (групп блюд) первого и второго уровня.
  • Селекторы первого уровня вложите в селектор «Начальный выбор» в виде кнопок селектора, тем самым создать первый уровень иерархической структуры меню.

  • Селекторы второго уровня необходимо вложите в селекторы первого уровня в виде кнопок селектора, тем самым создать второй уровень иерархической структуры меню.

  • Все необходимые блюда из справочника Меню перетащите в нужные селекторы второго уровня в виде кнопок селекторов.

Древовидная структура для вывода многоуровневого меню на php

От автора: приветствую Вас дорогой друг. Меню — это неотъемлемая часть любого сайта и даже в том случае, если он состоит всего лишь из одной страницы. И так сложилось, что, как правило, редко встречаются меню одного уровня. Наибольшее распространение получили как раз многоуровневые меню, так как они позволяют расположить большее количество ссылок на значительно меньшем пространстве. Поэтому в данном уроке мы рассмотрим формирование древовидной структуры данных для отображения на экран меню указанного типа.

скачать исходники

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

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

Профессия PHP-разработчик с нуля до PRO

Готовим PHP-разработчиков с нуля

Вы с нуля научитесь программировать сайты и веб-приложения на PHP, освоите фреймворк
Laravel
, напишете облачное хранилище и поработаете над интернет-магазином в команде.
Сможете устроиться на позицию Junior-разработчика.

Узнать подробнее

Командная стажировка под руководством тимлида

90 000 рублей средняя зарплата PHP-разработчика

3 проекта в портфолио для старта карьеры

Для начала, хотел бы немного остановиться на заготовке того скрипта с которым мы будем работать. Собственно он состоит всего лишь из двух файлов – index.php и functions.php.

Код файла index.php:

<?php

require «functions.php»;

$mysqli = db_connect(‘localhost’, ‘root’, », ‘tree_menu’);

$cats = getCategories($mysqli);

Как Вы видите все предельно просто – подключаем файл functions.php, в котором определены и будут сегодня определяться функции. Далее вызываем функцию подключения к базе данных и после этого вызываем на исполнение функцию getCategories(), которая вернет в виде массива выборку информации из таблицы базы данных, с которой мы будем сегодня работать, в вот таком виде:

Древовидная структура для вывода многоуровневого меню на php

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

Древовидная структура для вывода многоуровневого меню на php

Напомню, что в поле title содержится заголовок категории или ссылки меню, в поле parent_id – идентификатор родительской категории, причем элементы самого верхнего уровня в данном поле содержат 0 и наконец, поле id – это идентификатор таблицы.

Далее, код файла functions.php:

<?php

function db_connect($host, $user, $password, $db_name) {

    $link = mysqli_connect($host, $user, $password, $db_name);

    if (!$link) {

        die(‘Ошибка подключения (‘ . mysqli_connect_errno() . ‘) ‘

                . mysqli_connect_error());

    }

    return $link;

}

function getCategories($link) {

    if ($result = mysqli_query($link, «SELECT * FROM categories»)) {

         return mysqli_fetch_all($result, MYSQLI_ASSOC);

    }

}

Код приведенных функцию, думаю, не нуждается в комментариях. Теперь мы можем приступить к работе и по большому счету мы с Вами напишем ровно три функции и парочку шаблонов для отображения данных на экран. Итак, начинаем писать первую функцию, часть которой будет знакома тем, кто смотрел вышеуказанный урок:

function createTree($arr) {

$parents_arr = array();

foreach($arr as $key=>$item) {

$parents_arr[$item[‘parent_id’]][$item[‘id’]] = $item;

}

return $parents_arr;

}

Хотел бы заметить, что код еще не завершен – это только начало. Обратите внимание, что в качестве аргумента мы будем передавать массив из которого будет сформирована древовидная структура. Как Вы видите в коде, используя цикл foreach, мы обходим массив, переданный в виде аргумента, и формируем новый — $parents_arr, ключами которого, являются идентификаторы родительских категорий. И в каждой ячейке данного массива, содержится дополнительный подмассив – всех категорий (дочерних), у которых идентификатор родительской категории равен ключу соответствующего массива.

В итоге при вызове данной функции и распечатки на экран возвращаемого значения, мы получим следующий результат:

Древовидная структура для вывода многоуровневого меню на php

Теперь, мы подготовили данные и наша задача, состоит в том, что бы сформировать такой многоуровневый массив, в котором на первом уровне вложенности присутствовали только родительские элементы высшего уровня, далее в каждом из них будет определен элемент children, в котором будет содержаться массив дочерних элементов. Которые в свою очередь, если содержат потомков, так же будут их хранить в указанном элементе. Таким образом, нам нужно получить массив вот такого вида:

Древовидная структура для вывода многоуровневого меню на php

А значит, мы можем продолжить писать код функции и несколько изменим возвращаемое значение:

function createTree($arr) {

$parents_arr = array();

foreach($arr as $key=>$item) {

$parents_arr[$item[‘parent_id’]][$item[‘id’]] = $item;

}

$treeElem = $parents_arr[0];

generateElemTree($treeElem,$parents_arr);

return $treeElem;

}

Обратите внимание, что мы создаем новый массив $treeElem, который далее возвращается как результат работы функции. И по умолчанию сохраняем в него элементы самого высокого уровня, которые содержатся в ключе с индексом 0, массива $parents_arr. По сути, сейчас мы получили массив элементов верхнего уровня, в который нужно добавить дочерние элементы, непосредственно в ячейку с ключом children. И сделает это дополнительная функция generateElemTree(), которую мы сейчас с Вами напишем.

Итак, код функции generateElemTree():

function generateElemTree(&$treeElem,$parents_arr) {

foreach($treeElem as $key=>$item) {

if(!isset($item[‘children’])) {

$treeElem[$key][‘children’] = array();

}

if(array_key_exists($key,$parents_arr)) {

$treeElem[$key][‘children’] = $parents_arr[$key];

generateElemTree($treeElem[$key][‘children’],$parents_arr);

}

}

}

Важно! Первый аргумент функции – это ссылка на массив $treeElem , так как нам нужно его изменить – доработать и вернуть как результат работы функции createTree(). Второй же аргумент это массив который мы с Вами получили в выше указанной функции. Собственно работа функции хоть и кажется сложной, но на самом деле очень проста.

Для начала, проверяем есть ли в переданном массиве элемент с ключем ‘children’ и если его нет то мы его создаем и инициализируем пустым массивом:

if(!isset($item[‘children’])) {

$treeElem[$key][‘children’] = array();

}

А далее мы проверим, есть ли в массиве $parents_arr, элемент с ключом $key. При этом, напомню что в $key – содержится идентификатор текущего элемента, а в массиве $parents_arr, ключи это идентификаторы родительских элементов. Соответственно если условие выполнилось, значит, мы нашли дочерние элементы для текущего элемента. Соответственно массив дочерних элементов записываем я ячейку с ключом children. Затем рекурсивно вызываем эту же функцию, передавая при этом как раз найденный массив дочерних элементов, ведь они так же в свою очередь могут быть родителями для других элементов.

Вот собственно и все, если распечатать на экран содержимое массива $treeElem, мы получим желаемую древовидную структуру данных, которая показана на рисунке выше. Теперь необходимо вывести на экран полученные данные. Для этого напишем функцию шаблонизатор:

function renderTemplate($path,$arr) {

$output = »;

if(file_exists($path)) {

extract($arr);

ob_start();

include $path;

$output = ob_get_clean();

}

return $output;

}

Код данной функции я позаимствовал из урока по созданию собственного шаблонизатора, а значит, комментировать ее я не буду, так как в уроке приведено подробнейшее описание.

Теперь, думаю, стоит привести код файла index.php, так как именно в нем мы будем использовать только что написанную функцию:

require «functions.php»;

$mysqli = db_connect(‘localhost’, ‘root’, », ‘tree_menu’);

$cats = getCategories($mysqli);

$cats = createTree($cats);

echo renderTemplate(‘template.php’,[‘cats’=>$cats]);

Как Вы видите, с помощью шаблонизатора, подключается файл template.php , код которого приведен ниже:

<h2>Menu</h2>

<?php if(isset($cats)) : ?>

<?php echo renderTemplate(‘menu_part.php’,[‘cats’=>$cats]); ?>

<?php endif; ?>

Внутри шаблоны мы опять же обращаемся к функции шаблонизатора и подгружаем дополнительный шаблон в который передается все та же переменная cats. Хотел бы отметить что так как мы не знаем сколько уровней вложенности в нашей древовидной структуре, соответственно нам нужно рекурсивно обрабатывать ее, а значит рекурсивно подгружать шаблон который будет выводить один уровень вложенности, что собственно и реализовано, используя дополнительный шаблон, код которого приведен ниже:

<ul style=«margin-left:10px;»>

<?php foreach($cats as $cat) : ?>

<li><a href=«/category/<?php echo $cat[‘id’]; ?>«><?php echo $cat[‘title’]; ?></a></li>

<?php if(count($cat[‘children’]) > 0) : ?>

<?php echo renderTemplate(‘menu_part.php’,[‘cats’=>$cat[‘children’]]); ?>

<?php  endif; ?>

<?php endforeach; ?>

</ul>

Логика работы шаблона сводится к следующему: используя цикл foreach() обходим элементы одного уровня и если у элемента в ячейке ‘children’, содержится массив дочерних элементов – рекурсивно, вызываем функцию renderTemplate(), подгружаем этот же шаблон, но при этом передаем в качестве переменной ‘cats’ ,массив дочерних элементов. При этом на экране, в качестве результата мы увидим следующее:

Древовидная структура для вывода многоуровневого меню на php

Профессия PHP-разработчик с нуля до PRO

Готовим PHP-разработчиков с нуля

Вы с нуля научитесь программировать сайты и веб-приложения на PHP, освоите фреймворк
Laravel
, напишете облачное хранилище и поработаете над интернет-магазином в команде.
Сможете устроиться на позицию Junior-разработчика.

Узнать подробнее

Командная стажировка под руководством тимлида

90 000 рублей средняя зарплата PHP-разработчика

3 проекта в портфолио для старта карьеры

Вот собственно и все. Всего Вам доброго и удачного кодирования!!!

Древовидное меню сайтов

Все SEO оптимизаторы, наверное, слышали про древовидное меню сайта (Site Tree-Like Menu), и про то, что древовидные структуры очень любимы поисковыми системами. Преимуществ у древовидного меню много: тут и отсутствие ссылок типа “все на всех”, и оптимальная передача веса целевым страницам, и удержание посетителей в разделе товара, и даже максимальный рост PR для всех страниц сайта. Но всем ли понятно, как выглядит строго-древовидная структура навигационного меню на сайте, без дополнительного объяснения?

Объяснение древовидных меню с точки зрения паука

Представьте, что Вы … паук. Этакий паучище, который летать не может, но зато как ползает! И цель его — добраться до всех листочков (отдельных страниц) дерева. Сейчас он находится на земле, и перед ним — дерево (сайт). Поползли? Ползать будем с остановками, обращая внимание на то, что видно из каждого текущего положения.

Дерево растет от корня, а сайт — с главной страницы (морды).

Ползем от корня по стволу дерева. Пока все понятно и однозначно: дерево жирное, буквально набито заманчивыми словечками “самые зеленые листочки!”, “свежие листья по сниженным ценам”, “последние модели листьев”, и так далее. Но вот доползаем до первой развилки… эх, сколько же тут ответвлений (разделов сайта)! Мы видим только их, никаких листочков (конечных страниц) пока в поле зрения нет. Выбираем, куда ползти по древовидному меню HTML-вида на сайте. Например, по отростку “самые свежие листья на нашем дереве” (типа новостной раздел).

Ползая по дереву, паук должен знать: обратной дороги нет!

И вот как только паук заполз на это ответвление, все другие перестали быть ему видны. Он же летать не умеет, чтобы порхать с ветки на ветку… Все! И если заботливый садовник не насыпал “хлебных крошек” — то обратной дороги у паука нет. Это первый уровень древовидного меню — разделы, или рубрики сайта. Коммерсанты могут потирать ручки: все, попался клиент! Куда же он теперь денется, кроме, как поползет вперед, к заветной кнопке “купить товар”? А ему и незачем деваться — ведь именно туда он и полз. Были бы указатели расставлены верно!

Перебираемся на сучья дерева: развилки древовидных меню.

Впереди — новые развилки (допустим, это категории товаров). И их так же много, как и в начале, на первом уровне древовидной менюшки. Куда ползти дальше? На помощь приходят указатели — анкоры ссылок древовидного меню. Они должны ПРОДОЛЖАТЬ начатую разделами мысль, чтобы удержать паука: если это были “самые-самые свежие листья” — то теперь “молодые побеги листочков по сниженным ценам“, но никак не “раскрутка сайта при помощи популярных статей про пауков“. Так помогите же паучку найти правильную дорогу по древовидному меню сайта, не делайте лишних указателей и не маскируйте нужных! Теперь паук не видит ничего, кроме дороги назад (”хлебные крошки“), и пути вперед (пункты древовидной менюшки к листочкам-товарам).

Третий клик — и мы у цели! Довольны все, кроме бестолковых.

Если все сделано правильно, то на третьем клике достигается заветный листочек. Вот он: самый свежий, самый зеленый, да к тому же по откровенно дисконтным ценам… Да даже если и не так — неужто не жаль затраченных усилий?! И кто сказал, что на других деревьях растут лучше? Кстати, про “кто сказал”… а вот другие паучки и сказали: В ОБЯЗАТЕЛЬНОМ ПОРЯДКЕ приводите на страницах товаров “отзывы покупателей”! Это важнее, чем даже характеристики товара. Но отвлеклись: перед последним шагом паук видит только выбор листьев. И ничего другого (надеюсь, на Вашем сайте не стоит прямая реклама конкурентов?!).

Цель достигнута. Какие бывают варианты действий?

Собственно, выбор невелик: или схавать листок (купить товар), или свалить куда-то еще. А куда? Где находится корень сайта, паук не знает. И вот тут помогает контекстная перелинковка страниц сайта. Это такая паутина, протянутая рукотворно и исключительно на близкие по контексту страницы, которая поможет пауку, как обычному, так и поисковому, ознакомиться с альтернативными предложениями. Ведь не на сайт же конкурентов их отпускать?! Там им скормят жуткую порнуху, заразят вирусами, и вообще: конкуренты — это такие свиньи, которым все человеческое не паучит. Так предложите же паучку нечто иное! Свое древовидное меню на PHP, которое создается сравнительно несложно. Например, “концентрат из листьев”, что растет на соседней ветке. Да, так, чтоб ему не сползать вниз, а одним движением переползти по натянутой Вами паутинке на целевую страницу. Это и называется КОНТЕКСНАЯ ПЕРЕЛИНКОВКА по сайту.

Подытожим: паук видит только то, что веб-мастер ему предложил. Паука надо удерживать; более того, надо всячески заботиться об его комфортном пребывании на дереве. И ненавязчиво, но целеустремленно вести к цели. Это касается как поисковых роботов, так и обычных посетителей. Здесь идет игра по одним и тем же правилам. Так следуйте же им!

Объяснение древовидного меню сайтов на примере сайта «SEO практика ком».

Объяснение древовидного меню сайта с точки зрения паука должно помочь веб-мастерам и оптимизаторам в продвижении сайтов. На практике — это самый эффективный способ поднять трафик по НЧ и СЧ запросам. К слову: на этом сайте древовидная структура реализована в полном объеме, и даже с элементами перфекционизма: практически каждая внутренняя ссылка — что, вперед, что назад, — имеет уникальный анкор и тем самым получает бонусы. Древовидное меню сайта — справа, хлебные крошки — сверху, контекстная перелинковка — внутри страницы. Все просто и понятно! И если Вы здесь — значит, еще и очень эффективно. Пользуйтесь!

Древовидные меню — вертикально расположенные меню с вложенными подменю, которые становятся видны после нажатия или наведения указателя мыши на их родительские пункты меню. Древовидные меню иногда анимируют с помощью JavaScript или CSS, в этом уроке мы рассмотрим раскрывающийся вариант такого меню, созданный на чистом и простом CSS.


Вероятно, несложная разметка для такого меню будет понятна без дополнительных объяснений:

<ul id="nav">
        <li>
                <a href="#">Home</a>
        <li class="multi">
                <a href="#">Our Products</a>
        <li>
                <a href="#">Contact</a>
        <li>
                <a href="#">Privacy Statement</a>
</ul>

В элементе ненумерованного списка класса multi будет находиться подменю, которое добавим позже. Для начала давайте зададим основные стили CSS:

ul#nav {
        list-style-type: none;
        width: 12em;
        background: black;
        padding-left: 0;
}
ul#nav li a {
        color: #fff;
        text-decoration: none;
        font-size: larger;
        line-height: 200%;
        display: block;
        border-bottom: 1px solid #777;
        padding-left: 1em;
}
ul#nav li a:hover {
        background: #633;
}

Нам также понадобится дополнительный код CSS, чтобы показать, что в элементе ненумерованного списка класса multi находится больше информации:

ul#nav li.multi::before {
        content: "25B6";
        color: red;
        float: left;
        margin: .5em;
}

Еще поменяем внешний вид этого элемента ненумерованного списка при наведении на него указателя мыши. Для этого воспользуемся необычным сочетанием псевдокласса и псевдоэдемента: :hover и ::before, именно в этом порядке, чтобы добиться следующего результата:

ul#nav li.multi:hover::before {
        content: "25BC";
}

Обратите внимание, что нам не нужно задавать еще раз цвет, внешние отступы и другие стили символа Юникода, так как они наследуются от предыдущего объявления.

Теперь пришло время добавить вложенное подменю.

Обычно правильное древовидное меню практически любой формы — это вложенный список с подменю, расположенными в элементе ненумерованного списка.

<ul id="nav">
        <li>
                <a href="#">Home</a>
        <li class="multi">
                <a href="#">Our Products</a>
                <ul>
                        <li><a href="#">Widgets</a>
                        <li><a href="#">Bidgets</a>
                        <li><a href="#">Bobs</a>
                </ul>
        <li>
                <a href="#">Contact</a>
        <li>
                <a href="#">Privacy Statement</a>
</ul>

Обратите внимание, что элементу ненумерованного списка, содержащему вложенный список, добавлен класс multi, что позволяет легко контролировать появление пунктов подменю, особенно если таких элементов с вложенными списками несколько. В нашем примере для простоты используется только один элемент класса multi.

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

ul#nav li.multi ul {
        display: none;
        padding-left: 0;
}
ul#nav li.multi:hover ul {
        display: block;
}

Вот так будет выглядеть это древовидное разворачивающееся меню в итоге:

Автор урока Dudley Storey

Перевод — Дежурка

Смотрите также:

  • Создание оригинального выпадающего меню
  • Создание интересного выпадающего меню с использованием CSS3
  • Создание скошенных вкладок с использованием трехмерных трансформаций CSS3

Понравилась статья? Поделить с друзьями:
  • Как составить маркетинговый план на месяц
  • Как исправить треснувшее зеркало
  • Как найти человека в херсонской области
  • Как зайти с другого найти
  • Как найти аналог песни