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

Blazor WebAssembly中Bootstrap下拉菜单点击无响应,需刷新页面才生效的问题求助

Blazor WebAssembly中Bootstrap下拉菜单点击无响应,需刷新页面才生效的问题求助

我最近在做Blazor WebAssembly项目时遇到了一个头疼的问题:当我导航到创建/编辑Item的页面时,页面上的Bootstrap下拉菜单点击完全没反应,但只要刷新一下页面,下拉菜单就正常工作了。我已经在Index.html里导入了Bootstrap的相关样式和脚本,实在搞不懂哪里出问题了,希望大家能帮我看看!

我的组件代码(创建/编辑Item的Razor组件)

@page "/items/creer"
@page "/items/modifier/{Id:int}"

@inject IItemService _itemService
@inject IDepartementService _departementService
@inject NavigationManager _navigationManager

@if (IsProcessing)
{
    <div style="position:fixed;top:50%;left:50%;margin-left:-100px;">
        <img src="images/ajax-loader.gif" />
    </div>
}
else
{
    <div class="container mt-5">
        <h3 class="card-title text-primary mb-3 ml-3">@Titre un item</h3>
        <EditForm Model="Item" OnValidSubmit="GererItem">
            <DataAnnotationsValidator />
            <ValidationSummary />

            <div class="form-group mt-3">
                <label>Nom</label>
                <InputText @bind-Value="Item.Nom" class="form-control"></InputText>
                <ValidationMessage For="()=>Item.Nom"></ValidationMessage>
            </div>

            <div class="form-group mt-3">
                <label>Image</label>
                <InputText @bind-Value="Item.Image" class="form-control"></InputText>
                <ValidationMessage For="()=>Item.Image"></ValidationMessage>
                <img src="@Item.Image" class="img-fluid w-25" />
            </div>

            <div class="form-group mt-3">
                <label>Départements</label>
                <div class="dropdown">
                    <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                        @(Item.Departement != null ? Item.Departement.Nom : "Choisir un département...")
                    </button>
                    <div class="dropdown-menu" aria-labelledby="dropdownMenu2">
                        @foreach (var departement in Departements)
                        {
                            <button class="dropdown-item" type="button" @onclick="() => SelectDepartement(departement)">@departement.Nom</button>
                        }
                    </div>
                </div>
            </div>

            <div class="mt-3 mb-3">
                <button class="btn btn-primary py-2">@Titre</button>
                <NavLink href="items" class="btn btn-secondary">Retour</NavLink>
            </div>
        </EditForm>
    </div>
}

@code {
    public bool IsProcessing { get; set; } = false;
    public ItemDto Item { get; set; } = new ItemDto
    {
        Id = 0,
        Nom = "",
        Departement = null
    };

    [Parameter]
    public int Id { get; set; }

    private string Titre { get; set; } = "Créer";
    private IEnumerable<DepartementDto> Departements { get; set; } = new List<DepartementDto>();

    private void SelectDepartement(DepartementDto departement)
    {
        Item.Departement = departement;
    }

    protected override async Task OnInitializedAsync()
    {
        IsProcessing = true;
        if (Id != 0)
        {
            Titre = "Modifier";
            Item = await _itemService.Obtenir(Id);
        }
        Departements = await _departementService.ObtenirTous();
        IsProcessing = false;
    }

    private async Task GererItem()
    {
        if (Id == 0)
        {
            await _itemService.Creer(Item);
        }
        else
        {
            await _itemService.Modifier(Item);
        }
        _navigationManager.NavigateTo("items");
    }
}

Index.html中导入的资源

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Epicerie_Client</title>
    <base href="/" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="Epicerie_Client.styles.css" rel="stylesheet" />
    <link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css">
</head>
<body>
    <div id="app">
        <div id="app" style="position:fixed;top:50%;left:50%;margin-top:-50px;">
            <img src="images/loader.gif" />
        </div>
    </div>
    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss"></a>
    </div>

    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.7/dist/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    <script src="js/common.js"></script>
    <script src="_framework/blazor.webassembly.js"></script>
</body>
</html>

问题原因分析

这个问题其实是Blazor客户端路由和Bootstrap JS初始化机制的冲突导致的:

  • Bootstrap JS的初始化逻辑:Bootstrap的JavaScript组件(比如下拉菜单)是在页面首次加载时,通过扫描DOM元素来完成初始化的。只有被初始化过的组件才能正常响应用户交互。
  • Blazor的路由特性:Blazor WebAssembly使用客户端路由,当你通过NavLink或者NavigationManager导航到新页面时,页面不会重新加载,只是Blazor会替换DOM中的内容。这时候新渲染出来的下拉菜单元素并没有被Bootstrap的JS初始化,所以点击没反应。而刷新页面时,整个页面重新加载,Bootstrap JS会重新扫描所有DOM元素,初始化下拉菜单,所以就能正常工作了。
  • 额外的冲突点:你的Index.html里同时导入了Bootstrap 4.x和Bootstrap 5.x的JavaScript文件,这两个版本的Bootstrap JS是不兼容的,会互相干扰,也可能加剧这个问题。

解决方案

第一步:清理Index.html中的重复Bootstrap脚本

首先把Index.html里重复的Bootstrap JS删掉,统一使用一个版本。比如选择Bootstrap 5.x(因为它不需要jQuery,更适合Blazor),修改后的脚本部分大概是这样:

<!-- 只保留Bootstrap 5的脚本,去掉Bootstrap 4和jQuery相关的脚本 -->
<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="js/common.js"></script>
<script src="_framework/blazor.webassembly.js"></script>

同时注意把Bootstrap 4的CSS也换成Bootstrap 5的,保持样式和脚本版本一致。

第二步:手动初始化Bootstrap下拉菜单

因为Blazor导航不会触发页面重新加载,所以我们需要在组件渲染完成后,手动调用Bootstrap的初始化方法。可以通过Blazor的JS互操作来实现:

  1. 在wwwroot目录下创建一个bootstrap-helpers.js文件,内容如下:
window.initBootstrapDropdowns = function() {
    // 初始化所有未被初始化的下拉菜单
    document.querySelectorAll('.dropdown-toggle').forEach(toggle => {
        new bootstrap.Dropdown(toggle);
    });
}
  1. 在Index.html中引入这个脚本(放在Bootstrap脚本之后):
<script src="bootstrap-helpers.js"></script>
  1. 在你的Razor组件中,注入IJSRuntime,然后在组件渲染完成后调用这个JS方法:
@inject IJSRuntime _jsRuntime

<!-- 组件其他内容 -->

@code {
    // 其他代码

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // 首次渲染完成后初始化下拉菜单
            await _jsRuntime.InvokeVoidAsync("initBootstrapDropdowns");
        }
        await base.OnAfterRenderAsync(firstRender);
    }
}

替代方案:使用Blazor专用的Bootstrap组件库

如果你不想手动处理JS初始化的问题,可以使用专门为Blazor设计的Bootstrap组件库,这类库完全用C#实现,不需要依赖Bootstrap的JS,能完美适配Blazor的生命周期,避免这类DOM初始化问题。

备注:内容来源于stack exchange,提问作者mathdx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 13:53:05