04
Sep '20

I’m putting this out here right now, as I just spend two hours banging my head against a wall, becoming more and more convinced that witchcraft was afoot.

If you are using boostrap tabs, and have a set displaying generally correctly, but with just one or two that won’t open no matter what you try then I may have the answer for you:

If your target pane ID begins with a digit then it WILL NOT WORK.

Whack on a prefix and all will come right. Good lord that was frustrating!

25
Apr '20

I make a lot of use of bootstrap modals in my code, and I find it incredibly useful to load the content dynamically, rather than have everything embedded in the page just in case it may be needed. To do this I tend to have a modal container (sometimes multiple, allowing for different sizes or styles), and target it from the trigger link.

<a href="/remote/content" class="btn btn-xs btn-primary modal-toggle" data-toggle="modal" data-target="#remote-modal" title="Load remote modal">Load Modal</a>

<div class="modal fade remote-modal" id="remote-modal" role="dialog" aria-hidden="true">
    <div class="modal-dialog" role="document">
        <div class="modal-content">

        </div>
    </div>
</div>

Then all it takes is a modal show listener that grabs the URL from the trigger, and loads the content via AJAX into the modal-content section. This also gives me the opportunity to add a new modal event that is fired when the content has completed loading; ‘loaded.bs.modal’. This new event is hugely useful as the core ‘shown.bs.modal’ event will often trigger before the content is loaded.

$('.remote-modal').on('show.bs.modal', function (e) {
    var trigger = $(e.relatedTarget);
    var modal = $(this);

    $(this).find('.modal-content').load(trigger.attr('href'), function() {
        modal.trigger('loaded.bs.modal', []);
    });
});

Simple as that.

17
Jan '19

In my latest projects I’ve found myself increasingly putting input forms within bootstrap modals. It cleanly separates active and static elements, it maintains a tidy UI, and it reduces page load time to name but a few benefits.

A key drawback however is that it’s very easy to click away from a modal by accident and lose your work. To deal with this I created a quick script that listens for changes in any input elements within a modal, and catches the modal close event accordingly:

var is_clicked = false;
var dirty = false;

$().ready(function() {

    /*
     * Keeps the change listener inactive until at least one element has been clicked on 
     */
    $('.modal').on('click', 'input, select, textarea', function(e) {
        is_clicked = true;
    });

    /*
     * input element change listener
     */
    $('.modal').on('change', 'input, select, textarea', function(e) {

        if(is_clicked) {
            dirty = true;
        }
    });

    /*
     * Handles the modal hide event
     */
    $('.modal').on('hide.bs.modal', function(e) {
        //Prevent date picker submits from trying to close parent modals
        if($(e.target).hasClass('date-box'))
            return false;

        if(dirty) {
            if(confirm('You have unsaved changes. Are you sure you wish to close this dialog?')) {
                is_clicked = false;
                dirty = false;
                return true;
            }

            return false;
        }

        return true;
    });
});

In reality I tend to use pnotify for my confirmation dialogs, but to keep this example as simple as possible I put in the native javascript call.

27
Mar '18

So this one had me puzzled for rather a while. We all know about the bootstrap event ‘shown.bs.modal’ that fires when a modal has been loaded. However, when the modal content is loaded via AJAX, I’ve found that can still be triggered a little bit in advance of contained elements loading in the DOM.

To give a concrete example, I encountered this when trying to activate datatables on a table that I was embedding in a modal. My script was set up as follows:

$('.modal').on('shown.bs.modal', function (e) {
    $('.table-class').DataTable({
        //Datatable options
    });
});

This tended to work about 50% of the time, and after a lot of thought I realised that the times it didn’t run were due to the table element not being loaded into the DOM before the DataTable call is made. Even though the Bootstrap event had been fired, the DOM was yet to catch up.

Now there are various ways around this, most obviously by using ‘setTimeout’ to add a short delay before triggering subsequent logic, although this technique has always struck me as rather imprecise and risky. Fortunately, jQuery provides the ‘ready’ function which gives us all we need to make this logic work perfectly:

$('.modal').on('shown.bs.modal', function (e) {
    $('.table-class').ready(function() {
        $('.table-class').DataTable({
            //Datatable options
        });
    });
});

All we are doing here is adding a subsequent observer to the element so that we can be sure the DOM is aware of it before we try to run any further logic. Simple as that!

13
Nov '16

Easy one this, just copy and paste code if you’re using Bootstrap.
Whenever a tab is changed, this code will take its ID from the trigger and put into the window location hash (AKA address anchor or, if you’re really posh, the fragment identifier).

//Remember tab state
$('ul.nav-tabs > li > a').on('shown.bs.tab', function(e) {
    if($(e.target).attr('href')) {
        var id = $(e.target).attr('href').substr(1);
        window.location.hash = id;
    }
});

The second section of code ensures that whenever the page is reloaded, or the page is linked to using a valid tab ID, the identified tab will be selected automatically.

//Switch tab state
if (window.location.hash) {
    $("a[href='" + window.location.hash + "']").tab('show');
}

Done