Javascript in Blazor -- Only works on first render

Viewed 47

I'm working on a web app using Blazor. I want to make a collapsible navigation menu, with a button that allows me to expand/collapse the menu.

I have a component NavMenu.razor with the following code:

@inject IJSRuntime JSRuntime

<header class="topHeader">

    <div class="mainNavBar" id="mainNavBar"> 
        <a class="nav-link" href="link">LINK</a>
        <a class="nav-link" href="link">LINK</a>
    </div>
    
    <button class="menu-toggler" @onclick="ToggleMenu"/>

</header>



@code {
    IJSObjectReference JSObjectReference { get; set; }
    protected override async Task OnAfterRenderAsync(bool firstTime)
    {
            JSObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "/NavMenu.js");
            await JSObjectReference.InvokeVoidAsync("toggleMenu");
    }
}

My javascript lives inside a NavMenu.js file, which is inside the wwwroot folder and contains the following code:

const mainNavBar = document.getElementById("mainNavBar");
const menuToggler = document.getElementById("menu-toggler");

export function toggleMenu() {
    if (!mainNavBar.classList.contains("toggle-on")) {
        mainNavBar.classList.add("toggle-on");
        console.log(...mainNavBar.classList)
    } else {
        mainNavBar.classList.remove("toggle-on");
        console.log(...mainNavBar.classList)
    }
}

if (menuToggler) {
    menuToggler.addEventListener("click", toggleMenu)
}

I styled mainNavbar in the css file:

.mainNavBar {
    display: flex;
    width: 75%;
    justify-content: space-between;
    
}

@media (max-width: 768px) {
/* when screen < 768px; mainNavbar is hidden, but clicking the button will add the toggle-on class which is not hidden */
    .mainNavBar {
        display: none
    }

    .toggle-on {
        display: flex
    }
}

Expected Behavior: if the view is less than 768px wide, app loads with the menu collapsed (i.e. mainNavBar does not have the "toggle-on" class). Clicking the menu-toggler button will expand/collapse menu. menu-toggler button will work on any page. The function toggleMenu() should only run when the button is clicked.

Actual Behavior: app loads with the menu expanded (i.e. mainNavBar has the "toggle-on" class). The function toggleMenu() runs when the app first starts instead of running when the button is clicked. Clicking the menu-toggler button does expand/collapse menu but on the first page only. If I click on a link (e.g. link 2), the button stops working, i.e. clicking the button won't expand/collapse menu.

Your responses will be appreciated.

1 Answers

I didnt do the console logs but you get the idea. The state is held in menuOpen. The button now calls a C# function. Razor renders the css class's based on the state using MainNavBarCssClass calculated property.

<header class="topHeader">

    <div class="@MainNavBarCssClass">
        <a class="nav-link" href="link">LINK</a>
        <a class="nav-link" href="link">LINK</a>
    </div>

    <button class="menu-toggler" @onclick="ToggleMenu" />

</header>

@code {
    private bool menuOpen = false;
    private void ToggleMenu() => menuOpen = !menuOpen;
    private string MainNavBarCssClass =>
        "mainNavBar" + (menuOpen ? " toggle-on" : "");
}
Related