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

Svelte {#each}循环中模态框仅打开最后元素的问题咨询

问题解决:Svelte循环中子组件模态框总是打开最后一个的问题

问题根源

问题出在Dropdown组件的触发选择器冲突上:子组件中Dropdown的triggeredBy使用了全局类名.dots-menu,当通过{#each}循环渲染多个Card时,所有Dropdown都会绑定到页面上所有匹配.dots-menu的元素。点击任意一个三点菜单时,实际上最后一个Dropdown的点击事件会被触发,导致打开最后一个Card对应的模态框。

解决方案

为每个Dropdown的触发元素生成唯一ID,让每个Dropdown只绑定到自己对应的触发按钮,避免选择器冲突。

修改后的子组件(Card.svelte)

<script lang="ts">
    import type { ContentListDTO } from '@content-service/content_dto';
    import { formatRelative } from 'date-fns';
    import {
        Card,
        Dropdown,
        DropdownItem,
        GradientButton,
        Input,
        Label,
        Modal
    } from 'flowbite-svelte';
    import {
        ArrowRightOutline,
        DotsVerticalOutline,
        FileInvoiceOutline
    } from 'flowbite-svelte-icons';

    export let content: ContentListDTO;

    let openRenameModal = false;
    // 生成唯一触发ID,基于content的唯一标识
    const triggerId = `dots-menu-${content.id}`;

    function capitalizeFirstLetter(value: string) {
        return value.charAt(0).toUpperCase() + value.slice(1);
    }

    const createdAt = capitalizeFirstLetter(formatRelative(new Date(content.createdAt), new Date()));
</script>

<Card class="relative">
    <div class="img-bg bg-gray-200 rounded-t-lg">
        <FileInvoiceOutline
            strokeWidth="1"
            class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white w-16 h-16"
        />
    </div>

    <h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
        {content.title}
    </h5>
    <p class="mb-8 font-extralight text-gray-500 dark:text-gray-400 leading-tight">
        Created: {createdAt}
    </p>

    <div>
        <GradientButton pill color="blue" class="w-fit">
            Edit <ArrowRightOutline class="w-6 h-6 ms-2 text-white" />
        </GradientButton>

        <!-- 用唯一ID替代全局类名 -->
        <DotsVerticalOutline
            id={triggerId}
            class="float-right mt-2 -mr-3 w-8 h-8 dark:text-white cursor-pointer hover:bg-gray-200 rounded"
        />
        <!-- Dropdown绑定到唯一ID -->
        <Dropdown triggeredBy={`#${triggerId}`}>
            <DropdownItem on:click={() => (openRenameModal = true)}
                ><span class="font-light">Rename</span></DropdownItem
            >
            <DropdownItem><span class="text-red-600 font-light">Delete</span></DropdownItem>
        </Dropdown>
    </div>
</Card>

<Modal size="xs" bind:open={openRenameModal} autoclose outsideclose>
    <form class="flex flex-col space-y-6" action="#">
        <h3 class="mb-4 text-xl font-medium text-gray-900 dark:text-white">Rename content</h3>
        <Label class="space-y-2">
            <span>Current content title</span>
            <Input
                type="text"
                name="currentTitle"
                disabled
                readonly
                class="focus:border-blue-500 focus:ring-blue-500"
                value={content.title}
            />
        </Label>
        <Label class="space-y-2">
            <span>New content title</span>
            <Input type="text" name="title" required class="focus:border-blue-500 focus:ring-blue-500" />
        </Label>
        <GradientButton type="submit" color="blue" class="w-full">Rename content</GradientButton>
    </form>
</Modal>

关键修改点

  1. 生成唯一ID:利用每个content对象的唯一标识(比如id)生成专属的triggerId,确保每个触发按钮的ID不重复。
  2. 替换触发选择器:将Dropdown的triggeredBy从全局类名.dots-menu改为指向唯一ID的选择器#${triggerId},让每个Dropdown只响应自己对应的触发按钮。

补充说明

如果你的ContentListDTO没有id属性,也可以用循环索引生成唯一ID:

<!-- 父组件修改循环,传入索引 -->
{#each data.contents as content, index}
    <Card {content} {index} />
{/each}

<!-- 子组件接收索引并生成ID -->
<script lang="ts">
    export let index: number;
    const triggerId = `dots-menu-${index}`;
</script>

内容的提问来源于stack exchange,提问作者kongand

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 21:09:52