
本文讲解如何在 Svelte 中为 #each 循环生成的多个 元素实现「点击展开当前项、自动收起其他项」的单选式交互,避免所有元素同步开关,并通过索引精准控制开闭状态。
本文讲解如何在 svelte 中为 `#each` 循环生成的多个 `
在 Svelte 应用中,当使用 {#each} 渲染一组
✅ 推荐实现方案
核心思路是维护一个响应式变量 opened,用于记录当前处于展开状态的元素索引(初始为 null 表示全部关闭)。在循环中,利用 open={opened === i} 控制每个
<script>
// 示例数据源(实际项目中可替换为任意数组)
const items = Array.from({ length: 5 }, (_, i) => `Item ${i + 1}`);
// 跟踪当前展开项的索引,null 表示无展开项
let opened = null;
</script>
{#each items as item, i}
<details open="{opened" i on:toggle="{(e)"> {
if (e.target.open) {
opened = i; // 仅在展开时更新,收起时不干预(保持 opened 不变,其他项自动关闭)
}
}}
>
<summary>{item}</summary><p>This is the detailed content for {item}. It appears only when expanded.</p>
</details>
{/each}
⚠️ 关键注意事项
- 不要使用 bind:open:它会双向绑定并强制同步所有元素的状态,破坏独立性;
-
on:toggle 是关键事件:元素原生支持 toggle 事件,且在用户点击
时触发,比监听 click 更语义化、更可靠;
-
收起时不重置 opened:当用户再次点击已展开项时,e.target.open 为 false,我们不修改 opened,此时其他 的 open={opened === i} 自动变为 false,实现自然收起;
- 索引一致性保障:确保 #each 中的索引 i 是稳定唯一的(如使用真实数组而非 Array(5) 这类无意义占位符,推荐传入带业务含义的数据数组);
? 扩展建议
- 若需默认展开第一项,初始化设为 let opened = 0;
- 如需支持键盘导航(Enter/Space 触发展开),Svelte 默认已兼容 的无障碍行为,无需额外处理;
- 在复杂列表中,可将逻辑封装为可复用组件(如
),通过 export let index 和 dispatch('toggle') 进一步解耦。
通过该模式,你不仅能精准控制每项的展开状态,还能保持 DOM 行为原生、代码简洁、性能高效——这是构建专业级可访问折叠面板的 Svelte 最佳实践。










