professormeowingtons
professormeowingtons

Reputation: 3514

Dynamically build Twitter Bootstrap modal

I am building a Rails application, and I want to place the content from a Rails partial into the modal via AJAX.

In a Twitter Bootstrap 2.3.2 modal, I read via the documentation that you can load content via ajax using the remote key.

http://getbootstrap.com/2.3.2/javascript.html#modals

However, this only allows content to be injected into the .modal-body, rather than building the whole modal dynamically.

Is there a way to build the entire modal, including .modal-header, .modal-footer, dynamically with JS?

It seems very clunky to do this with a string, like follows:

partial = render_to_string(:partial => 'some-partial').gsub(%{"}, %{'}).gsub(/'/,"\\\\'").gsub("\n", "")

Upvotes: 35

Views: 73825

Answers (5)

Amnon
Amnon

Reputation: 2888

Update:

Since posting this, I've found an elegant bootstrap 3 modal wrapper function here, which doesn't require adding a div to the html code.


Here's a code sample that demonstrates this. To use, just add a div in your <body> (inside bootstrap's <div class="container">, for example:

<div id="idMyModal"></div>

and then you can use it via:

var header = "This is my dynamic header";
var content = "This is my dynamic content";
var strSubmitFunc = "applyButtonFunc()";
var btnText = "Just do it!";
doModal('idMyModal', header, content, strSubmitFunc, btnText);

To close the modal, issue a call to hideModal, also defined below:

function doModal(placementId, heading, formContent, strSubmitFunc, btnText)
{
    var html =  '<div id="modalWindow" class="modal hide fade in" style="display:none;">';
    html += '<div class="modal-header">';
    html += '<a class="close" data-dismiss="modal">×</a>';
    html += '<h4>'+heading+'</h4>'
    html += '</div>';
    html += '<div class="modal-body">';
    html += '<p>';
    html += formContent;
    html += '</div>';
    html += '<div class="modal-footer">';
    if (btnText!='') {
        html += '<span class="btn btn-success"';
        html += ' onClick="'+strSubmitFunc+'">'+btnText;
        html += '</span>';
    }
    html += '<span class="btn" data-dismiss="modal">';
    html += 'Close';
    html += '</span>'; // close button
    html += '</div>';  // footer
    html += '</div>';  // modalWindow
    $("#"+placementId).html(html);
    $("#modalWindow").modal();
}


function hideModal()
{
    // Using a very general selector - this is because $('#modalDiv').hide
    // will remove the modal window but not the mask
    $('.modal.in').modal('hide');
}

Upvotes: 45

Craig Harshbarger
Craig Harshbarger

Reputation: 2243

Update

I recently stumbled upon bootbox.js which is a whole library dedicated to dynamically creating bootstrap modals and reacting to users interaction with them. Though different than the method below, bootbox accepts callbacks rather than a function name. I haven't personally used it yet because I can't justify a 26kb library to essentially do what the function below does. But I thought somebody might find it useful.

Update 8/17/2016

I now use bootbox for pretty much every project I need dynamic modals. Works great a I highly recommend it.

Update 10/1/2018

Bootbox doesn't officially support bootstrap 4 yet but there is a bootbox v5.x branch where they are working on bootstrap 4 support. According to the 5.0.0 roadmap and Bootbox 5.0 ship list ticket it sounds like the branch is pretty much ready to go but they haven't released it yet. But there are some instructions on how to use it. Disclaimer: I have not yet used to v5.x branch and can't vouch for its completeness.

Update 3/25/2019

Bootbox 5.0 was released which supports Bootstrap 4.

Original Post

Code take from Ammon's answer above. Update for bootstrap 3.0

function doModal(placementId, heading, formContent, strSubmitFunc, btnText)
{
    html =  '<div id="modalWindow" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal" aria-hidden="true">';
    html += '<div class="modal-dialog">';
    html += '<div class="modal-content">';
    html += '<div class="modal-header">';
    html += '<a class="close" data-dismiss="modal">×</a>';
    html += '<h4>'+heading+'</h4>'
    html += '</div>';
    html += '<div class="modal-body">';
    html += formContent;
    html += '</div>';
    html += '<div class="modal-footer">';
    if (btnText!='') {
        html += '<span class="btn btn-success"';
        html += ' onClick="'+strSubmitFunc+'">'+btnText;
        html += '</span>';
    }
    html += '<span class="btn" data-dismiss="modal">';
    html += <?php echo "'".__t("Close")."'"; ?>;
    html += '</span>'; // close button
    html += '</div>';  // footer
    html += '</div>';  // content
    html += '</div>';  // dialog
    html += '</div>';  // modalWindow
    $("#"+placementId).html(html);
    $("#modalWindow").modal();
    $("#dynamicModal").modal('show');
}

This is what I ended up using for my needs. Also includes an event handler for removing the modal from the DOM once it is closed. I just needed an info modal, so I took out the submit function and button text arguments.

function doModal(heading, formContent) {
    html =  '<div id="dynamicModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal" aria-hidden="true">';
    html += '<div class="modal-dialog">';
    html += '<div class="modal-content">';
    html += '<div class="modal-header">';
    html += '<a class="close" data-dismiss="modal">×</a>';
    html += '<h4>'+heading+'</h4>'
    html += '</div>';
    html += '<div class="modal-body">';
    html += formContent;
    html += '</div>';
    html += '<div class="modal-footer">';
    html += '<span class="btn btn-primary" data-dismiss="modal">Close</span>';
    html += '</div>';  // content
    html += '</div>';  // dialog
    html += '</div>';  // footer
    html += '</div>';  // modalWindow
    $('body').append(html);
    $("#dynamicModal").modal();
    $("#dynamicModal").modal('show');

    $('#dynamicModal').on('hidden.bs.modal', function (e) {
        $(this).remove();
    });

}

Upvotes: 35

Adi.P
Adi.P

Reputation: 400

I had the same problem, after researching a lot, I finally built a js function to create modals dynamically based on my requirements. Using this function, you can create popups in one line such as:

puyModal({title:'Test Title',heading:'Heading',message:'This is sample message.'})

Or you can use other complex functionality such as iframes, video popups, etc.

Find it on https://github.com/aybhalala/puymodals For demo, go to http://pateladitya.com/puymodals/

Upvotes: 1

Khushboo Mulani
Khushboo Mulani

Reputation: 389

Using DOM, I have created the Button as well as the Bootstrap modal that pops up as soon as the Button is clicked.

Also include these in the head section of HTML page:

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href=      
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script  
src= "https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js">
</script>
<script  
src= "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js">     
</script>

Whole of this code needs to be written in JS file.

//Firstly, creating a button which on click displays the Bootstrap Modal

var button = document.createElement("input");
button.className = 'btn btn-info btn-lg';
button.setAttribute("type", "button");
button.setAttribute("data-toggle", "modal");
button.setAttribute("data-target", "#myModal");              
button.setAttribute("value", "More Information...");
document.getElementsByTagName('body')[0].appendChild(button);   

//MODAL CREATION:

var div1 = document.createElement('div');
div1.id = 'myModal';
div1.className = 'modal fade';
div1.setAttribute("role", "dialog");     

var innerDiv1m = document.createElement('div');
innerDiv1m.className = 'modal-dialog modal-sm';
div1.appendChild(innerDiv1m);              

var innerDiv2m = document.createElement('div');
innerDiv2m.className = 'modal-content';
innerDiv1m.appendChild(innerDiv2m);

var innerDiv3 = document.createElement('div');
innerDiv3.className = 'modal-header';
innerDiv2m.appendChild(innerDiv3);

var buttonM = document.createElement("button");
buttonM.className = 'close';
buttonM.setAttribute("data-dismiss", "modal");
buttonM.setAttribute("aria-hidden", "true");
buttonM.setAttribute("value", "Close");
innerDiv3.appendChild(buttonM); 

var headerM = document.createElement("H4");
headerM.className = 'modal-title';
innerDiv3.appendChild(headerM);

var innerDiv31 =  document.createElement('div');
innerDiv31.className = 'modal-body';
innerDiv2m.appendChild(innerDiv31);

var para =  document.createElement('p'); 
innerDiv31.appendChild(para);
para.innerHTML = "paragraph";

var innerDiv32 =  document.createElement('div');
innerDiv32.className = 'modal-footer';
innerDiv2m.appendChild(innerDiv32);

var closeButton = document.createElement("input");
closeButton.className = 'btn btn-default';
closeButton.setAttribute("data-dismiss", "modal");
closeButton.setAttribute("type", "button");
closeButton.setAttribute("value", "Close");
innerDiv32.appendChild(closeButton);

document.getElementsByTagName('body')[0].appendChild(div1); 

//Therefore, on clicking the created button, the modal pops up on the sreen.

Upvotes: 5

Morvael
Morvael

Reputation: 3567

Very similar theme to accepted answer but written as a jQuery plugin. I was looking for some logic to build into a toolkit I'm working up but couldn't find any so wrote this.

There's a lot of code below, but its designed to be written once and then called easily afterwards, so as a spoiler, once you have it all set up it's as easy to use as:

$.fn.alert("utils.js makes this so easy!");

And as a full working example:

https://jsfiddle.net/63zvqeff/

There is no need for any existing <div /> to be on the page, and it works with nested dialogues, its taken from a tool kit I'm working up so I have included all the relevant bits so it's a working copy / paste example.

(function ($)
{
    $.utils = {
        // http://stackoverflow.com/a/8809472
        createUUID: function ()
        {
            var d = new Date().getTime();
            if (window.performance && typeof window.performance.now === "function")
            {
                d += performance.now(); //use high-precision timer if available
            }
            var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c)
            {
                var r = (d + Math.random() * 16) % 16 | 0;
                d = Math.floor(d / 16);
                return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
            });
            return uuid;
        }
    }

    $.fn.dialogue = function (options)
    {
        var defaults = {
            title: "", content: $("<p />"),
            closeIcon: false, id: $.utils.createUUID(), open: function () { }, buttons: []
        };
        var settings = $.extend(true, {}, defaults, options);

        // create the DOM structure
        var $modal = $("<div />").attr("id", settings.id).attr("role", "dialog").addClass("modal fade")
                        .append($("<div />").addClass("modal-dialog")
                            .append($("<div />").addClass("modal-content")
                                .append($("<div />").addClass("modal-header")
                                    .append($("<h4 />").addClass("modal-title").text(settings.title)))
                                .append($("<div />").addClass("modal-body")
                                    .append(settings.content))
                                .append($("<div />").addClass("modal-footer")
                                )
                            )
                        );
        $modal.shown = false;
        $modal.dismiss = function ()
        {
            // loop until its shown
            // this is only because you can do $.fn.alert("utils.js makes this so easy!").dismiss(); in which case it will try to remove it before its finished rendering
            if (!$modal.shown)
            {
                window.setTimeout(function ()
                {
                    $modal.dismiss();
                }, 50);
                return;
            }

            // hide the dialogue
            $modal.modal("hide");
            // remove the blanking
            $modal.prev().remove();
            // remove the dialogue
            $modal.empty().remove();

            $("body").removeClass("modal-open");
        }

        if (settings.closeIcon)
            $modal.find(".modal-header").prepend($("<button />").attr("type", "button").addClass("close").html("&times;").click(function () { $modal.dismiss() }));

        // add the buttons
        var $footer = $modal.find(".modal-footer");
        for(var i=0; i < settings.buttons.length; i++)
        {
            (function (btn)
            {
                $footer.prepend($("<button />").addClass("btn btn-default")
                    .attr("id", btn.id)
                    .attr("type", "button")
                    .text(btn.text)
                    .click(function ()
                    {
                        btn.click($modal)
                    }))
            })(settings.buttons[i]);
        }

        settings.open($modal);

        $modal.on('shown.bs.modal', function (e) {
            $modal.shown = true;
        });
        // show the dialogue
        $modal.modal("show");

        return $modal;
    };
})(jQuery);

I then wrote a helper function for those times when you just wanted a basic alert()

(function ($)
{
    $.fn.alert = function (message)
    {
        return $.fn.dialogue({
            title: "Alert", 
            content: $("<p />").text(message),
            closeIcon: true,
            buttons: [
                { text: "Close", id: $.utils.createUUID(), click: function ($modal) { $modal.dismiss(); } }
            ]
        });
    };

})(jQuery);

Otherwise you need to build your content as a jQuery object and then pass it in in the form of an object like:

{
    title: "", // what ever you want in the title bar
    content: $("<p />"), // any DOM structure you can build as a jQuery object
    closeIcon: false, // does the dialogue have a X in the tilte bar to close it
    id: $.utils.createUUID(), // a reference id 
    open: function () { }, // a function called after the DOM structure is built but BEFORE rendering
    buttons: [ // an array of buttons to include in the footer
        // example "close" button, all buttons get a reference to $modal passed into them 
        // .dismiss() is a function attached to $modal to revert the DOM changes
        { text: "Close", click: function ($modal) { $modal.dismiss(); } }
    ]
};

Upvotes: 4

Related Questions