You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:42:17