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