Search code examples
javascriptjquerytwitter-bootstrapjquery-pluginsbootstrap-5

Is it possible to force bootstrap 5 to inject its jquery plugins immediately, not after DOMContentLoaded?


After migration to BS 5 this become broken:

<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.min.js"></script>
<script>
     $('[data-bs-toggle="tooltip"]').tooltip()
</script>

When this works normally:

<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.min.js"></script>
<script>
    document.addEventListener("DOMContentLoaded", function (event) {
        (function () {
            $('[data-bs-toggle="tooltip"]').tooltip()
        })();
    });
</script>

This is absent in migration docs, and looks weird for me, so I'm asking : may be I'm doing something wrong and it is still possible to make plugins injected immediately as it was in BS4?


Solution

  • You’re correct that jQuery plugins become available as soon as they’re loaded.

    A jQuery plugin is created by extending the jQuery.prototype as in this example from Learn jQuery:

    $.fn.greenify = function() {
        this.css( "color", "green" );
    };
    

    As soon as the function is assigned to the jQuery prototype, it’s ready to run, so, as long as the script is at the end, invoking the new jQuery function right away will work.

    $( "a" ).greenify(); // Makes all the links green.
    

    Bootstrap 4

    For Bootstrap 4, since its JavaScript is loaded as a plugin right after jQuery is loaded, the Bootstrap jQuery functionality is available right away.

    You can see the code from the tooltip module for Bootstrap 4.6.0 where it assigns the tooltip function to jQuery:

    $.fn[NAME] = Tooltip._jQueryInterface
    $.fn[NAME].Constructor = Tooltip
    $.fn[NAME].noConflict = () => {
            $.fn[NAME] = JQUERY_NO_CONFLICT
            return Tooltip._jQueryInterface
        }
    

    One caveat — for Version 4, Bootstrap did recommend waiting for the DOM to be loaded.

    $(function () {
      $('[data-toggle="tooltip"]').tooltip()
    })
    

    Running the function right away works as it’s at the bottom of the page.

    Bootstrap 5

    Bootstrap 5 works differently. It creates a bootstrap JavaScript object that you can use right away (as soon as the script file is loaded). Putting the BS-5 code for enabling tooltips right after the BS-5 code will work:

    var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
    var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
        return new bootstrap.Tooltip(tooltipTriggerEl)
    });
    

    After Bootstrap defines the tooltip functionality, it calls a function to add the tooltip to jQuery:

    defineJQueryPlugin(Tooltip)
    

    The first thing that function does is check whether the document is still loading or not:

    const defineJQueryPlugin = plugin => {
        onDOMContentLoaded(() => {
            const $ = getjQuery()
            if ($) {
                const name = plugin.NAME
                const JQUERY_NO_CONFLICT = $.fn[name]
                $.fn[name] = plugin.jQueryInterface
                $.fn[name].Constructor = plugin
                $.fn[name].noConflict = () => {
                    $.fn[name] = JQUERY_NO_CONFLICT
                    return plugin.jQueryInterface
                }
            }
        })
    }
    

    The function that checks whether the DOM is loaded or not is:

    const onDOMContentLoaded = callback => {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', callback)
        } else {
            callback()
        }
    }
    

    If the DOM is still loading, an event listener for the DOMContentLoaded event is created and that listener runs the code to create the jQuery plugin after the DOM is loaded.

    So, yes, jQuery plugins become available as soon as they’re loaded, but Bootstrap is delaying the plugin assignments until after the DOMContentLoaded event. Why they’re doing that, I don’t know, but that’s how the code currently works.