Livewire 3.x+AlpineJS 3.x递归分类下拉保持展开问题求助
问题:递归分类菜单点击子分类时父分类自动收起
我用Livewire 3.x和AlpineJS 3.x实现了数据库存储的递归分类展示功能,其他功能都正常,但存在一个问题:打开父分类后点击其内部的子分类,父分类会自动收起。需要修改代码让父分类保持展开状态。
源代码
resources\views\livewire\category-navigation.blade.php
<div x-data="sidebar" class="sidebar z-50 transition-all duration-300"> <div class="bg-white"> <ul class="p-4 py-0" x-data="{ activeDropdown: null }"> @foreach ($categories as $category) @if ($category->children->count() > 0) <li class="nav-item"> <ul> <li class="nav-item"> <button type="button" class="nav-link group" :class="{ 'active': activeDropdown === '{{ $category->slug }}' }" @click="activeDropdown === '{{ $category->slug }}' ? activeDropdown = null : activeDropdown = '{{ $category->slug }}'"> <div class="flex items-center"> <span class="text-black"> {{ $category->name }} </span> </div> <div class="" :class="{ '!rotate-90': activeDropdown === '{{ $category->slug }}' }"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M9 5L15 12L9 19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> </svg> </div> </button> <ul x-cloak x-show="activeDropdown === '{{ $category->slug }}'" x-collapse class="sub-menu text-gray-500" @click.away="activeDropdown = null" @click.stop> @foreach ($category->children as $child) @include('livewire.category-item', ['category' => $child]) @endforeach </ul> </li> </ul> </li> @else <li class="nav-item"> <a href="{{ url($category->full_path) }}" wire:click.prevent="navigateToCategory('{{ $category->full_path }}')" class="nav-link group" :class="{ 'active': '{{ request()->routeIs("products.byCategory") && request()->route("slug") == $category->slug ? "active" : "" }}'}"> <div class="flex items-center"> <span class="text-black">{{ $category->name }} ({{ $category->products()->count() }})</span> </div> </a> </li> @endif @endforeach </ul> </div> </div>
resources\views\livewire\category-item.blade.php
@if ($category->children->count() > 0) <li x-data="{ subActive: null }"> <button type="button" class="before:bg-gray-300 before:w-[5px] before:h-[5px] before:rounded before:mr-2 hover:bg-gray-100" :class="{ 'active': subActive === '{{ $category->slug }}' }" @click.stop="subActive === '{{ $category->id }}' ? subActive = null : subActive = '{{ $category->slug }}'"> {{ $category->name }} <div class="ml-auto" :class="{ '!rotate-90': subActive === '{{ $category->slug }}' }"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M9 5L15 12L9 19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> </svg> </div> </button> <ul class="sub-menu text-gray-500 ml-4" x-show="subActive === '{{ $category->slug }}'" x-collapse> @foreach ($category->children as $child) @include('livewire.category-item', ['category' => $child]) <!-- REKURZÍV hívás --> @endforeach </ul> </li> @else <li> <a href="{{ url($category->full_path) }}" wire:click.prevent="navigateToCategory('{{ $category->full_path }}')">{{ $category->name }} ({{ $category->products()->count() }})</a> </li> @endif
JS代码
document.addEventListener("alpine:init", () => { Alpine.data("sidebar", () => ({ init() { const selector = document.querySelector('.sidebar ul a[href="' + window.location.href + '"]'); if (selector) { selector.classList.add('active'); const ul = selector.closest('ul.sub-menu'); if (ul) { let ele = ul.closest('li.menu').querySelectorAll('.nav-link'); if (ele) { ele = ele[0]; setTimeout(() => { ele.click(); }); } } } }, })); });
解决方案
1. 移除父分类子菜单的@click.away逻辑
父分类子菜单上的@click.away="activeDropdown = null"会把点击子分类的行为判定为"点击外部",导致父分类收起。只保留@click.stop阻止事件冒泡即可。
修改category-navigation.blade.php中的子菜单标签:
<ul x-cloak x-show="activeDropdown === '{{ $category->slug }}'" x-collapse class="sub-menu text-gray-500" @click.stop>
2. 修复子分类按钮的状态判断错误
category-item.blade.php中按钮的点击逻辑里,判断条件用了$category->id但赋值用了$category->slug,导致状态切换逻辑混乱,统一改成slug:
<button type="button" class="before:bg-gray-300 before:w-[5px] before:h-[5px] before:rounded before:mr-2 hover:bg-gray-100" :class="{ 'active': subActive === '{{ $category->slug }}' }" @click.stop="subActive === '{{ $category->slug }}' ? subActive = null : subActive = '{{ $category->slug }}'">
3. 修正页面初始化的父分类选择器
原JS代码中ul.closest('li.menu')选择器错误,父级元素实际是li.nav-item,修改后确保页面加载时能正确展开当前分类的父菜单:
document.addEventListener("alpine:init", () => { Alpine.data("sidebar", () => ({ init() { const selector = document.querySelector('.sidebar ul a[href="' + window.location.href + '"]'); if (selector) { selector.classList.add('active'); const ul = selector.closest('ul.sub-menu'); if (ul) { let ele = ul.closest('li.nav-item').querySelector('.nav-link'); if (ele) { setTimeout(() => { ele.click(); }); } } } }, })); });
可选:统一外部点击收起逻辑
如果需要点击菜单外部区域才收起所有分类,把@click.away移到最外层容器上,避免影响子分类点击:
<div x-data="sidebar" class="sidebar z-50 transition-all duration-300" @click.away="activeDropdown = null">
内容的提问来源于stack exchange,提问作者Fzol
相关产品推荐
相关产品推荐

