Svelte Accordion组件实现可选同组单次仅展开一项的方法
手风琴单开功能实现方案
核心用Svelte自带的上下文API做同组组件的状态通信,额外加一个组容器做配置入口,完全兼容你现有代码的写法,不需要改动已经写好的插槽结构,支持按需给任意手风琴组开启互斥效果。
第一步:新建组容器组件 AccordionGroup.svelte
这个组件只做两件事:接收「是否同组仅开一个」的配置,统一管理组内所有手风琴项的展开状态,给子组件暴露注册、切换的方法,嵌套使用时不同组的状态完全不会串。
<script> import { setContext } from 'svelte'; // 配置项:传true就是同时间仅允许展开1个,默认false兼容原有多开逻辑 export let exclusive = false; // 存储组内所有手风琴实例 const items = new Set(); // 注册实例到组 const register = (item) => items.add(item); // 组件销毁时从组移除 const unregister = (item) => items.delete(item); // 切换展开状态 const toggle = (targetItem) => { if (!exclusive) { // 非排他模式直接切换自身状态 targetItem.open = !targetItem.open; return; } // 排他模式:先关闭其他所有项,再切换当前项 items.forEach(item => { if (item !== targetItem) item.open = false; }); targetItem.open = !targetItem.open; } // 把方法下发给所有子级手风琴组件 setContext('accordionGroup', { register, unregister, toggle }); </script> <slot />
第二步:修改原有 Accordion.svelte 组件
改完的组件既可以单独使用(和你原来的多开效果完全一致),也可以放在组容器里受组规则控制,不需要改现有模板部分的结构。
<script> import { linear } from 'svelte/easing'; import { slide } from 'svelte/transition'; import { getContext, onMount, onDestroy } from 'svelte'; export let open = false; // 取最近一层的手风琴组实例,没有就是单独使用模式 const group = getContext('accordionGroup', null); // 当前实例的状态引用,给组用来控制展开收起 const self = { get open() { return open }, set open(val) { open = val } } function handleClick() { if (group) { group.toggle(self); return; } // 单独使用走原有切换逻辑 open = !open; } onMount(() => group?.register(self)) onDestroy(() => group?.unregister(self)) </script> <div class="accordion"> <div class="header" on:click={handleClick}> <div class="text"> <slot name="head"></slot> </div> </div> {#if open} <div class="details" transition:slide="{{duration: 500, easing: linear}}" > <slot name="details"></slot> </div> {/if} </div> <style> div.accordion { margin: 1rem 0; } div.header { display:flex; width:100%; } div.header .text { flex: 1; } div.details { background-color: transparent; padding:1rem; } </style>
第三步:按需使用
想让哪组手风琴开启互斥效果,就用AccordionGroup包起来传exclusive={true},不包的话和原来的多开效果完全一致,嵌套组之间互不干扰。举个适配你现有代码的例子:
<script> import Accordion from "./Accordion.svelte" import AccordionGroup from "./AccordionGroup.svelte" </script> <!-- 外层手风琴不包组,保留多开模式 --> <Accordion> <div slot="head"> <h2>Test one</h2> </div> <div slot="details"> <ul> <!-- 内层两个子手风琴包组开排他,同时只能开一个 --> <AccordionGroup exclusive={true}> <Accordion> <div slot="head"> <h4>The test of subitem?</h4> </div> <div slot="details"> <li>1</li> <li>2</li> <li>3</li> </div> </Accordion> <Accordion> <div slot="head"> <h4>Test of subitem 2</h4> </div> <div slot="details"> <li>one</li> <li>two</li> <li>three</li> </div> </Accordion> </AccordionGroup> </ul> </div> </Accordion> <!-- 同级手风琴包组开排他,这两个同时只能开一个 --> <AccordionGroup exclusive={true}> <Accordion> <div slot="head"> <h4>Second test</h4> </div> <div slot="details"> <ul> <li>again one</li> <li>two again</li> <li>three repeat</li> </ul> </div> </Accordion> <Accordion> <div slot="head"> <h4>Third test</h4> </div> <div slot="details"> <ul> <li>test content</li> </ul> </div> </Accordion> </AccordionGroup> <style> li { margin: 0; padding: 1em; text-align: left; list-style-type: none; cursor: pointer; color: black; } h4, h2 { cursor: pointer; } </style>
额外调整提示
如果需要排他模式下始终保持有一个项展开、不允许全部收起,只需要在AccordionGroup.svelte的toggle方法里加个判断:当前点击的是已展开项时直接return,不执行收起逻辑就行。
内容的提问来源于stack exchange,提问作者Tomca z Brodku
相关产品推荐
相关产品推荐

