Rus
Eng
Битрикс - быстрое древовидное многоуровневое меню аккордеон каталога
Order site
By contacting me you will receive a reliable and knowledgeable contractor who will quickly and efficiently implement any task for you and your business.

All English-language materials have been translated fully automatically using the Google service

Once again, the task arose of building a tree-like accordion menu based on the catalog sections. When solving the problem, I wanted to minimize the number of requests, make it as fast and simple as possible, which we managed to do

Template

use Class\Path\Script;

global $sections;
$sections = Script::getSections();

foreach ($sections as $k => $v){
    $sections[$k]['DETAIL_PAGE_URL'] = Script::makeUrl($v['ID']);
}

$sections = Script::buildTree($sections);

if($sections){
    ?><div class="accordion-menu col-menu">
            <?=Script::buildMenu($sections)?>
    </div><?
}

Class

<?php namespace Class\Path\Script;

/**
 * Класс помощник при работу с датами
 */
class Script{

    /**
     * Получаем ссылку на раздел по его id
     * @param $parentId integer
     * @return string
     */
    public static function makeUrl($parentId = 0){

        global $sections;

        $sefFolder = '/catalog/';

        $codes = array();

        while (isset($parentId)) {
            $codes[] = $sections[$parentId]['CODE'];
            if(isset($sections[$parentId])){
                $parentId = $sections[$parentId]['IBLOCK_SECTION_ID'];
            }else{
                $parentId = null;
            }
        }

        if (sizeof($codes)) {
            return $sefFolder . implode('/', array_reverse($codes)) . '/';
        } else {
            return null;
        }
    }

    /**
     * Получаем перечень разделов
     * @param $data array
     * @return array
     */
    public static function getSections(){
        $sections = [];

        \CModule::IncludeModule("iblock");

        $resCatalog = \CIBlockSection::GetList(
            array(
                'LEFT_MARGIN' => 'ASC'
            ),
            array(
                'IBLOCK_ID' =>  Class\Path\Config::getCatalogIB(),
                'ACTIVE' => 'Y',
                'GLOBAL_ACTIVE' => 'Y',
            ),
            false,
            array(
                'IBLOCK_ID',
                'IBLOCK_SECTION_ID',
                'NAME',
                'DEPTH_LEVEL',
                'SECTION_PAGE_URL',
                'UF_*'
            )
        );

        while($arCatalog = $resCatalog->Fetch()){
            $sections[$arCatalog['ID']] = $arCatalog;
        }

        return $sections;
    }

    /**
     * Строим древовидную структуру разделов
     * @param $arResult array
     * @return array
     */
    public static function buildTree($arResult){
        if (!empty($arResult)) {
            $parentID = false;
            $subParentID = false;
            $subsubParentID = false;
            foreach($arResult as $i => $arItem) {
                if ($arItem['DEPTH_LEVEL'] == 1) {
                    $parentID = $i;
                    $arResult[$i]['ITEMS'] = array();
                } elseif ($arItem['DEPTH_LEVEL']==2 && $parentID!==false) {
                    $arResult[$parentID]['ITEMS'][$i] = $arItem;
                    $subParentID = $i;
                    unset($arResult[$i]);
                } elseif ($arItem['DEPTH_LEVEL']==3 && isset($arResult[$parentID]['ITEMS'][$subParentID])) {
                    $arResult[$parentID]['ITEMS'][$subParentID]['ITEMS'][$i] = $arItem;
                    $subsubParentID = $i;
                    unset($arResult[$i]);
                } elseif ($arItem['DEPTH_LEVEL']==4) {
                    $arResult[$parentID]['ITEMS'][$subParentID]['ITEMS'][$subsubParentID]['ITEMS'][$i] = $arItem;
                    unset($arResult[$i]);
                }
            }
            $arResult = array_values($arResult);
        }

        return $arResult;
    }

    /**
     * Строим древовидную структуру разделов
     * @param $arResult array
     * @return array
     */
    public static function buildMenu($array)
    {
        echo '<ul>';

        global $APPLICATION;

        foreach ($array as $item)
        {
            echo '<li>';
            echo '<a href="'.$item['DETAIL_PAGE_URL'].'" '.($item['DETAIL_PAGE_URL'] == $APPLICATION->GetCurPage() ? 'class="selected"' : '').'>'.$item['NAME'].'</a>';
            if (!empty($item['ITEMS']))
            {
                Script::buildMenu($item['ITEMS'],false);
            }
            echo '</li>';
        }
        echo '</ul>';
    }

}

Javascript

let AccordionMenu = function(selector) {
        this.colMenu = document.querySelectorAll(`${selector} li`);
        let This = this;
        this.colMenu.forEach(function(items) {
            if (items.querySelector('ul')) {
                items.firstElementChild.insertAdjacentHTML('beforeend', '<div class="menu--svg"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 451.847 451.847" xml:space="preserve"> <g> <path d="M225.923,354.706c-8.098,0-16.195-3.092-22.369-9.263L9.27,151.157c-12.359-12.359-12.359-32.397,0-44.751 c12.354-12.354,32.388-12.354,44.748,0l171.905,171.915l171.906-171.909c12.359-12.354,32.391-12.354,44.744,0 c12.365,12.354,12.365,32.392,0,44.751L248.292,345.449C242.115,351.621,234.018,354.706,225.923,354.706z"/> </g> </svg></div>');

                items.querySelector('.menu--svg').onclick = function(e) {
                    if(e.target.closest('.menu--svg')){
                        e.preventDefault();

                        let isTrue = this.closest('li').classList.toggle('open');

                        if (isTrue) {
                            This.show(e.target.closest('a').nextElementSibling);
                        } else {
                            This.hide(e.target.closest('a').nextElementSibling);
                        }
                    }
                }
            }
        })
    }

    // Show an element
    AccordionMenu.prototype.show = function(elem) {
        // Get the natural height of the element
        var getHeight = function() {
            elem.style.display = 'block'; // Make it visible
            var height = elem.scrollHeight + 'px'; // Get it's height
            return height;
        };

        var height = getHeight(); // Get the natural height
        elem.style.height = height; // Update the height

        setTimeout(function() {
            elem.style.height = 'auto';
        }, 350);
    };

    // Hide an element
    AccordionMenu.prototype.hide = function(elem) {
        // Give the element a height to change from
        elem.style.height = elem.scrollHeight + 'px';

        // Set the height back to 0
        setTimeout(function() {
            elem.style.height = '0';
        }, 110);

        setTimeout(function() {
            elem.style.display = '';
        }, 700);
    };

    new AccordionMenu('.col-menu');

    /* Простите за этот участок jQuery - мне хотелось быстрого и лаконичного решения */
    (function($) {
        $( document ).ready(function() {
            $('.col-menu a.selected').parents('li').addClass('open');
            $('.col-menu a.selected').parents('ul').css({'display' : 'block', 'height':'auto'});
            $('.col-menu a.selected').next().css({'display' : 'block', 'height':'auto'});
        });
    })(jQuery);

css

/* Multilevel Accordion Menu with Plain HTML & CSS Start */
.accordion-menu {
    padding: 4px 4px 3px;
    background: rgba(255,255,255,0.5);
}

.accordion-menu ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.accordion-menu li.open > ul {
    display: block;
}

.accordion-menu li > ul {
    /* position: absolute; */
    display: none;
    height: 0;
    overflow: hidden;
    transition: height 350ms ease-in-out;
}

.accordion-menu ul.is-visible {
    display: block;
    /* height: auto; */
}

.accordion-menu li {
    border-bottom: 1px solid rgba(194, 194, 194, 0.333);
    position: relative;
    overflow: hidden;
    transition: all .4s ease;
}

.accordion-menu li:last-child {
    border: none;
}

.accordion-menu li::after {
    content: "";
    display: block;
    clear: both;
}

.accordion-menu li > a > .menu--svg > svg {
    display: block;
    position: relative;
    fill: rgb(48, 48, 48);
    width: 10px;
    float: right;
    transition: all .3s ease;
}

.accordion-menu li.open > a > .menu--svg > svg {
    transform: rotate(90deg);
}

.accordion-menu a {
    background: rgba(255,255,255,0.7);
    color: #444;
    padding: 7px 20px;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    text-decoration: none;
    text-transform: uppercase;
    margin: 0 0 1px 0;
}

.accordion-menu li.open > a.selected {
    /*background: rgba(255,255,255,1);*/
    background: rgba(0,0,0,0.1);
    color: #bc0003;
    font-weight: 600;
}

.accordion-menu li.open > a {font-weight: 600;background: rgba(0,0,0,0.1);}

.accordion-menu a:hover{background: rgba(50,50,50,1);color: #fff;}
.accordion-menu a:hover svg{fill: #fff !important;}

.accordion-menu li li a{padding-left: 30px;}
.accordion-menu li li li a{padding-left: 40px;}
.accordion-menu li li li li a{padding-left: 50px;}

.accordion-menu * {box-sizing: border-box;}

.menu--svg {width:20px;height: 20px;display: flex;align-items: center;justify-content: center;flex-direction: column;}
.menu--svg:hover {background-color: #000}
/* Multilevel Accordion Menu with Plain HTML & CSS Ends */

The result looks like this:

Layered Accordion Tree Menu

 

Based on materials:

Comments

There are no comments yet, you can be the first to leave it

Leave a comment

The site uses a comment pre-moderation system, so your message will be published only after approval by the moderator

You are replying to a user's comment

Send

FEEDBACK

Email me

Please message me to discuss the task. I work remotely and officially (as a self-employed professional); my hourly rate is 1,800 RUB, and I can dedicate 3–4 hours a day to the project. I respond quickly, and the consultation is free.

An error occurred while sending, please try again after a while
Message sent successfully

Phones

+7(993) 007-18-96

Email

info@tichiy.ru

Address

Россия, г. Москва

By submitting the form, you automatically confirm that you have read and accept the Privacy Policy site