Skip to content

Invoking JS Interop Function for DOM element located in submenu in Blazor

I am trying to invoke a JS function that creates a modal on user click through JS Interop in Blazor. The thing is, the function is using a onclick event for a DOM element that is inside a Blazor submenu. When that DOM Element is outside the submenu in the side navbar, it works fine. When I put that element inside the submenu, Blazor complains that it cannot set property 'onclick' of null

Here is my navbar HTML and JS Interop code

@inject IJSRuntime JS;

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">Test</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="/" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>

        <li class="nav-item px-3">
            <NavLink class="nav-link" @onclick="()=>expandSubNav = !expandSubNav">
                <span class="oi oi-fork" aria-hidden="true"></span> AccountPage
            </NavLink>
            @if (expandSubNav)
            {
        <ul  class="nav flex-column" id="submenu">
            <li class="nav-item px-3">
                <a class="expand-menu" href="page">
                    <span>element1</span>
                </a>
            </li>
            <li class="nav-item px-3">
                <a class="expand-menu" href="page">
                    <span>element2</span>
                </a>
            </li>
            <li class="nav-item px-3">
                <a class="expand-menu" href="page">
                    <span>element3</span>
                </a>
            </li>
            <li class="nav-item px-3">
                <a class="expand-menu">
                   
                    <div   id="addBtn" ><span class="oi oi-plus" aria-hidden="true"></span> Add Element</div>
                    </a>
            </li>
      


        </ul>
            }
        </li>
       
        

    </ul>
</div>

<div id="myModal" class="modal">

    <!-- Modal content -->
    <div class="modal-content">
        <h1 class="modal-title">
            <b>Add Element Here</b>
        </h1>

        <input id="addelementtext" type="text" />
        <button id="add">Add</button>
        <button type="button" class="btn btn-defaultcloseissue" id="closebtn" data-dismiss="modal" onclick="">Close</button>



    </div>
</div>





    </div>
</div>
@code { private bool collapseNavMenu = true;
    private bool expandSubNav;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {


        if (firstRender)
        {
            
                await JS.InvokeVoidAsync("createModal");
            
        }
    }

}

I am trying to use onclick with this id element

<li class="nav-item px-3">
                <a class="expand-menu">
                   
                    <div   id="addBtn" ><span class="oi oi-plus" aria-hidden="true"></span> Add Element</div>
                    </a>
            </li>

It is inside the submenu it created. When I put it outside the submenu into the main nav menu, the JS function does not complain and it works fine.

I have also tried doing this

 protected override async Task OnAfterRenderAsync(bool firstRender)
    {


        if (firstRender)
        {
            if (expandSubNav)
            {
                await JS.InvokeVoidAsync("createModal");

            }
        }
    }

This stops the error but clicking the element does nothing. How can I go about fixing this? I am trying to research about the Blazor LifeCycle as I feel this is a rendering issue but I am having a hard time understanding what I can do.

Here is also the JS function that I am trying to call

window.createModal = function createModal() {
    var modal = document.getElementById("myModal");

    // Get the button that opens the modal
    var btn = document.getElementById("addBtn");
    var closebtn = document.getElementById("closebtn");


    // When the user clicks the button, open the modal
    btn.onclick = function () {
        modal.style.display = "block";
    }



    // When the user clicks anywhere outside of the modal, close it
    window.onclick = function (event) {
        if (event.target == modal) {
            modal.style.display = "none";
        }
    }
    closebtn.onclick = function (event) {

        modal.style.display = "none";
    }
}

Answer

Your first error, cannot set property 'onclick' of null, happens because the subnav portion is not expanded when the navbar is first rendered, so the addBtn element isn’t found in the DOM yet when you call var btn = document.getElementById("addBtn");

The second issue is because your if() statement only runs once, on first render, while expandSubnav is false. So your await JS.InvokeVoidAsync("createModal"); line will never get executed.

If you notice in the @code block there’s a method for toggling the collapseNavMenu:

private void ToggleNavMenu()
{
    collapseNavMenu = !collapseNavMenu;
}

If you did something similar, but for expandSubnav, you could use that as an opportunity to hook in your createModal code. You only want to do it once, so you’d probably want to keep track of that too. Something like:

private bool modalCreated = false;
private void ToggleSubNav()
{
    expandSubnav = !expandSubnav;
    if(!modalCreated)
    {
        await JS.InvokeVoidAsync("createModal");
        modalCreated = true;
    }
}

And then hook that into your NavLink:

<NavLink class="nav-link" @onclick="ToggleSubNav">
    <span class="oi oi-fork" aria-hidden="true"></span> AccountPage
</NavLink>