htmltwitter-bootstrapbootstrap-5navbar

Sometimes the navbar works but sometimes it doesn't


I am building a website using Bootstrap 5, and I made a separate page for the top navbar which I place in the content pages using jQuery.

<!-- Nav bar -->
<div id="navbar"></div>
<script>
    $(function () {
        $("#navbar").load("/navbar.html")
    });
</script>

This code is identical for all pages.

The navbar itself has a mix of regular links and dropdowns. The regular links work fine on all the pages I tried, but the dropdowns only work sometimes. I can't find any sort of rhyme or reason as to when the dropdowns work and when they don't - even on the same page they sometimes work and sometimes don't.

Here's the code for the navbar (text obfuscated for obvious reasons):

    <div class="container px-5">
        <!-- Navigation bar -->
        <div class="navbar navbar-expand-lg">
            <a class="navbar-brand" href="/index.html"><img src="/images/logo.jpg" height="100"></a>

            <!-- Further content -->
            <div class="collapse navbar-collapse d-flex align-items-center justify-content-end" id="navbarContent">
                <ul class="navbar-nav me-auto mb-2 mb-md-0">

                    <!-- Home page -->
                    <li class="nav-item">
                        <a class="nav-link active" href="/index.html">Home</a>
                    </li>

                    <!-- Dropdown 1 -->
                    <li class="nav-item dropdown">
                        <a class="nav-link dropdown-toggle active" href="#" id="dropdown1" role="button"
                            data-bs-toggle="dropdown" aria-expanded="false">Dropdown 1</a>

                        <!-- Dropdown 1 listing -->
                        <ul class="dropdown-menu" aria-labelledby="dropdown1">
                            <li><a class="dropdown-item" href="/item1.html">Item 1</a></li>
                            <li><a class="dropdown-item" href="/subfolder1/item2.html">Item
                                    2</a></li>
                            <li><a class="dropdown-item" href="/subfolder1/item3.html">Item
                                    3</a></li>
                            <li><a class="dropdown-item" href="/subfolder1/item4.html">Item
                                    4</a></li>
                            <li><a class="dropdown-item" href="/subfolder1/item5.html">Item 5</a>
                            </li>
                            <li><a class="dropdown-item" href="/subfolder1/item6.html">Item
                                    6</a></li>
                            <li><a class="dropdown-item" href="/subfolder1/item7.html">Item 7</a></li>
                            <li><a class="dropdown-item" href="/subfolder1/item8.html">Item 8</a></li>
                        </ul>
                    </li>

                    <!-- Normal link -->
                    <li class="nav-item">
                        <a class="nav-link active" href="/normal1.html">Normal link 1</a>
                    </li>

                    <!-- Dropdown 2 -->
                    <li class="nav-item dropdown">
                        <a class="nav-link dropdown-toggle active" href="#" id="dropdown2" role="button"
                            data-bs-toggle="dropdown" aria-expanded="false">
                            Dropdown 2
                        </a>

                        <!-- Dropdown 2 listing -->
                        <ul class="dropdown-menu" aria-labelledby="dropdown2">
                            <li><a class="dropdown-item" href="/item9.html">Item 9</a></li>
                            <li><a class="dropdown-item" href="/subfolder2/item10.html">Item 10</a></li>
                            <li><a class="dropdown-item" href="/subfolder2/item11.html">Item 11</a></li>
                            <li><a class="dropdown-item" href="/subfolder2/item12.html">Item
                                    12</a></li>
                        </ul>
                    </li>

                    <!-- Dropdown 3 -->
                    <li class="nav-item dropdown">
                        <a class="nav-link dropdown-toggle active" href="#" id="dropdown3" role="button"
                            data-bs-toggle="dropdown" aria-expanded="false">
                            Dropdown 3
                        </a>

                        <!-- Dropdown 3 listing -->
                        <ul class="dropdown-menu" aria-labelledby="dropdown3">
                            <li><a class="dropdown-item" href="/subfolder3/item13.html">Item 13</a></li>
                            <li><a class="dropdown-item" href="/subfolder3/item14.html">Item 14</a></li>
                        </ul>
                    </li>

                    <!-- Normal link -->
                    <li class="nav-item">
                        <a class="nav-link active" href="/normal2.html">Normal link 2</a>
                    </li>
                </ul>
            </div>
        </div>
    </div>

This is utterly confusing me, and I want to know why it's sometimes working and sometimes not.

First I tried changing the navbar file from a full HTML document to a fragment, hoping that it would get the navbar working consistently, but that didn't help.

Then I tried asking Copilot for help, and while Copilot did find a bug in the navbar HTML (I had accidentally set the aria-labelledby in one of the dropdowns to one of the other dropdowns), that did not provide consistency either.

Finally, I tried getting rid of a lot of the line breaks as kind of a hail Mary, but that didn't help either.

UPDATE: Here's the code I used to load Bootstrap and JQuery:

    <!-- Setup for Bootstrap -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Latest compiled and minified CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- Latest compiled JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
    <!-- JQuery -->
    <script src="https://code.jquery.com/jquery-1.10.2.js"></script>

Solution

  • The problem is that jQuery .load() runs asynchronously, and it runs when DOM is ready, which is also when Bootstrap runs, so jQuery can finish adding navbar while Bootstrap is still initializing its components and event handlers, so you get a race condition, and sometimes dropdown will work, and sometimes not.

    One option would be to initialize dropdowns manually when jQuery is finished loading the navbar, and at this point Bootstrap has detected jQuery (DOM ready, regardless if jQuery script is added before or after Bootstrap's) and added components as plugins, so in load callback, you can find each dropdown and use dropdown plugin to initiate dropdowns (internally it will use getOrCreateInstance, which initializes dropdown on that element, if it's already not initialized):

    <!-- Nav bar -->
    <div id="navbar"></div>
    <script>
        $(function () {
            $("#navbar").load("/navbar.html", function(){
                $('#navbar [data-bs-toggle="dropdown"]').each(_=>$(this).dropdown());
            });
        });
    </script>