Search code examples
blazor.net-6.0blazor-server-side

Focus out control in blazor(show/hide <ul> inside <li>) without javascript


I am going to create a DropDown and a DropDownMenu. When DropDown button clicked, DropDownMenu must be shown. If user click outside of each DropDown or DropDownMenu, DropDownMenu must close. Also, if user click on any item in DropDownMenu, it must redirect to url. I create a component, but it not works properly. DropDownMenu not closed when lost focus.

Here is my complete component code:

Main navigation:

<nav class="navbar navbar-expand-lg bg-body-tertiary">
    <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                @Items
            </ul>
        </div>
    </div>
</nav>
@code {
    [Parameter] public RenderFragment Items { get; set; }
}

Menu item:

@if (Items != null)
{
    <li class="nav-item dropdown" @onclick="Toggle" @onblur="Close">
        <a class="nav-link @(Disabled?"disabled":"")" role="button">
            @Text
        </a>

        <ul class="dropdown-menu @(isVisible?"d-block":"d-none")" @onclick="Close">
            @Items
        </ul>
    </li>
}
else
{
<li class="nav-item">
        <a class="nav-link @(Disabled?"disabled":"")" aria-current="page" href="@NavigateUrl">@Text</a>
    </li>
}

@code {
    [Parameter] public RenderFragment Items { get; set; }
    [Parameter] public string Text { get; set; } = "Menu Item";
    [Parameter] public string NavigateUrl { get; set; } = "#";
    [Parameter] public bool Disabled { get; set; } = false;  
    private bool isVisible = false;

    public void Toggle()
    {
        isVisible = true;
        StateHasChanged();
    }

    public void Close()
    {
        //await Task.Delay(200);
        isVisible = false;
        StateHasChanged();
    }
    
}

usage:

 <MLMenu>
            <Items>
                <MLMenuItem Text="Home" NavigateUrl="/" />
                <MLMenuItem Text="Link" NavigateUrl="Counter" />
                <MLMenuItem Text="DropDown" >
                    <Items>
                        <MLMenuItem Text="Counter" NavigateUrl="counter" />
                        <MLMenuItem Text="Fetch Data" NavigateUrl="fetchdata" />
                    </Items>
                </MLMenuItem>
                <MLMenuItem Text="DropDown">
                    <Items>
                        <MLMenuItem Text="Counter" NavigateUrl="counter" />
                        <MLMenuItem Text="Fetch Data" NavigateUrl="fetchdata" />
                    </Items>
                </MLMenuItem>
                <MLMenuItem Text="Disabled" Disabled=true />
            </Items>
        </MLMenu>

Solution

  • li is not a focusable html element by default so you have to set tabindex="-1" to make the element focusable but not reachable via sequential keyboard navigation (Tab key). Try:

    <li class="nav-item dropdown" @onclick="Toggle" tabindex="-1" @onblur="Close">
        ...   
    </li>