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>
关键修改点
- 生成唯一ID:利用每个
content对象的唯一标识(比如id)生成专属的triggerId,确保每个触发按钮的ID不重复。 - 替换触发选择器:将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
相关产品推荐
相关产品推荐

