Svelte中点击新下拉时自动关闭其他已打开下拉的方法
Svelte多下拉组件互斥展开实现方案
你当前的问题根源是每个Dropdown组件的展开状态show是组件内部独立维护的,实例之间状态完全隔离,所以点击其他下拉时无法感知到需要关闭已打开的组件。以下是两种可直接落地的实现方案,按需选择即可。
方案1:状态提升至父组件统一管控
适合下拉组件数量少、组件层级简单的场景,逻辑直观易调试。
父组件改造
将展开状态统一存在父组件,通过props向子组件传递当前展开状态、切换方法,给每个下拉分配唯一标识区分:
<script> import Dropdown from './dropdown.svelte'; // 存储当前处于展开状态的下拉ID,值为null时所有下拉全关闭 let activeDropdown = null; const handleToggle = (id) => { // 点击已展开的下拉则关闭,点击其他下拉则切换为当前ID activeDropdown = activeDropdown === id ? null : id; } </script> <div> <Dropdown uid={1} isOpen={activeDropdown === 1} onToggle={() => handleToggle(1)} /> <Dropdown uid={2} isOpen={activeDropdown === 2} onToggle={() => handleToggle(2)} /> <Dropdown uid={3} isOpen={activeDropdown === 3} onToggle={() => handleToggle(3)} /> </div>
子组件改造
移除内部自维护的show状态,改用父组件传入的props控制展开逻辑:
<script> // 接收父组件传入的属性 export let uid; export let isOpen; export let onToggle; </script> <div> <button on:click={onToggle}>Show Dropdown</button> {#if isOpen} <div> <a href="/">Option 1</a> <a href="/">Option 2</a> <a href="/">Option 3</a> <a href="/">Option 4</a> </div> {/if} </div> <style> a { display: block; } </style>
方案2:基于Svelte Store全局管控状态
适合页面下拉数量多、组件嵌套层级深的场景,不需要逐层传递props,还可以很方便扩展点击页面空白处关闭所有下拉的能力。
新建全局状态文件
新建dropdownStore.js,用Svelte内置的store维护全局激活的下拉ID:
import { writable } from 'svelte/store'; // 全局共享的当前展开下拉ID export const activeDropdownId = writable(null);
子组件改造
每个子组件生成唯一ID,直接读写全局store实现状态互斥,父组件不需要做任何改动,原本的引入方式直接保留即可:
<script> import { onMount } from 'svelte'; import { activeDropdownId } from './dropdownStore.js'; // 为当前下拉实例生成唯一ID const selfId = Math.random().toString(36).slice(2); // 绑定当前下拉的DOM节点,用于实现点击外部关闭 let dropdownContainer; const toggleOpen = () => { $activeDropdownId = $activeDropdownId === selfId ? null : selfId; } // 点击组件外部时关闭所有下拉 const handleClickOutside = (e) => { if (dropdownContainer && !dropdownContainer.contains(e.target)) { $activeDropdownId = null; } } onMount(() => { document.addEventListener('click', handleClickOutside); return () => document.removeEventListener('click', handleClickOutside); }) </script> <div bind:this={dropdownContainer}> <button on:click={toggleOpen}>Show Dropdown</button> {#if $activeDropdownId === selfId} <div> <a href="/">Option 1</a> <a href="/">Option 2</a> <a href="/">Option 3</a> <a href="/">Option 4</a> </div> {/if} </div> <style> a { display: block; } </style>
父组件保持原有写法即可,不需要额外传参:
<script> import Dropdown from './dropdown.svelte'; </script> <div> <Dropdown /> <Dropdown /> <Dropdown /> </div>
内容的提问来源于stack exchange,提问作者Mir Stephen
相关产品推荐
相关产品推荐

