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", "")
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');
}
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();
});
}
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.
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("×").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(); } }
]
};
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/
Related
edit: my code is all held inside a $(document).ready(function() {} because of this, and because my html code is generated inside my javascript file on the fly, i am experiencing issues using .click() when applying the answer that was given to use
$('.movies_cell').click(function(){
var tmp = $(this).index();
});
original:
i have 20 div elements on a page with a class of .movies_cell that are all generated from an ajax file. All of the div's are created within container div called #movies.
any of the .movies_cell div's can be clicked to bring up a modal box, because i am going to place information from my json file in that modal depending on what gets clicked i need to know which div got clicked, for instance, if it was the 5th div i want to know that the 5th div was clicked and then store that number in a variable, if it was the 2nd, or 3rd i want that number to be stored in a variable and then clear when another .movies_cell div gets clicked.
how would i write a javascript or jquery script to accomplish this? :(
thanks!
$('#myMovies').click(function () {
$.getJSON('data/movies.json', function (allData) {
$.each(allData, function (i, field) {
$('#movies').append(function () {
var movies = '<div class="movies_cell">';
movies += '<div class="movies_image">';
movies += '<img src="img/movies/' + (field.image) + '" alt="' + (field.name) + ' Poster" style="width: 100%; height: 100%">';
movies += '</div>';
movies += '<div class="movies_detail">';
movies += '<h1>' + (field.name) + '</h1>';
movies += '<img src="img/rating/' + (field.myRating) + '.png" alt="movie rating" style="margin: auto;">';
movies += '</div>';
movies += '</div>';
counter++;
console.log(counter);
return movies;
});
});
});
});
Use event delegation.(https://api.jquery.com/on#direct-and-delegated-events) At the top
$('#movies').on( "click", ".movies_cell > div", function() {
var tmp = $(".movies_cell > div").index(this);
console.log(tmp);
});
then
$('#myMovies').click(function () {
//rest of code
Can you use .index() ? It is a zero-based index of the collection of items with, for example, class="movies_cell"
$('.movies_cell').click(function(){
var tmp = $(this).index();
});
jsFiddle Demo
I guess I should just be happy because of the general awesomeness of JQuery-Mobile, but I still can't imagine why JQueryMobile would not have a native way to pass variables.
Anyway, I'm trying to pass some variables around to create custom content on dynamically created "pages".
The pages are dynamically created fine. However I haven't been able to pass one single variable and have been trying for over a couple days now.
Any help would be awesome.
For the latest attempt at passing variables, I'm trying CameronAskew's $.mobile.paramsHandler.addPage code which works great out of the box.
Apart from generic jquery-mobile starting point, I Started With This Script which works really well to make pages dynamically, shown here:
// Prepare your page structure
var newPage = $("<div data-role='page' id='page'><div data-role=header><a data-iconpos='left' data-icon='back' href='#' data-role='button' data-rel='back'>Back</a><h1>Dynamic Page</h1></div><div data-role=content>Stuff here</div></div>");
// Append the new page into pageContainer
newPage.appendTo($.mobile.pageContainer);
// Move to this page by ID '#page'
$.mobile.changePage('#page');
Here's the full javascript:
run();
setInterval(function(){
run();
},5000);
function run() {
var output;
$.ajax({
url: "http://[url_here]/mobile_get_data_2.php",
success: function( data ) {
var obj = jQuery.parseJSON( data );
var output='';
var charts='';
var idx=1;
$.each(obj,function(k,v){
output += '<ul data-role="listview" data-count-theme="a" data-inset="true">';
output += '<a data-count-theme="a" href="#chart?param1=212121¶m2=32327"><li>[returned description here]';
output += '<span class="ui-li-count">';
output += [returned count here];
output += '</span></a></li>';
output += '</ul>';
idx = (idx+1);
});
$('#sensors').html(output).trigger("create");
}
});
}
$('#sensors').on('click', function ( e) {
// Prepare your page structure
var newPage = $(
"<div data-role='page' id='page'>"
+ "<div data-role=header>"
+ "<a data-iconpos='left' data-icon='back' href='#' data-role='button' data-rel='back'>Back</a>"
+ "<h1>Chart</h1>"
+ "</div>"
+ "<div data-role=content>Chart Here</div>"
+ "</div>"
);
newPage.appendTo($.mobile.pageContainer); // Append the new page into pageContainer
// Move to this page by ID '#page'
$.mobile.changePage('#page');
});
// CameronAskew
$(function () {
$.mobile.paramsHandler.addPage(
"page", // jquery mobile page id which will accept parameters
["param1", "param2"], // required parameters for that page
[], // optional parameters for that page,
function (urlVars) {
// $("#param1display").html(urlVars.param1);
// $("#param2display").html(urlVars.param2);
alert( urlVars.param1);
}
);
$.mobile.paramsHandler.init();
});
And the HTML:
<div data-role="page" id="summary">
<div data-role="header"><h1>MakerSpace</h1></div>
<ul id="sensors" data-role="listview" data-count-theme="c" data-inset="true"></ul></div>
<div data-role="footer"></div>
</div>
The stars aligned!
Changed this
$('#sensors').on('click', function ( e) {
to this
$('ul').on('click', function () {
put the variable in (obviously)
output += '<a data-count-theme="a" href="#chart?param1=[returned id]"><li>[returned description]';
And now it works.
I hope this helps someone out there...
I have a js function that recieves an array of items. The function itself creates html content dynamicly using a call to jquery html function. The problem is that I don't remember how to embed correctly the array inside the appended html. For example,
var ErrorsNotifier = {
init: function(items) {
var template = '<div id="message">';
template += '<div id="id">some message.<br /><br />';
template += '<div class="errorsList">error desc</div></div>';
template += '';
template += '';
template += '<input type="image" src="someimage" alt="" class="" onclick="window.open("someurl");"/></div>';
$("#content").html(template);
}
}
After the html will be rendered to the body, clicking on the anchor tag return the following error - Uncaught SyntaxError: Unexpected identifier
inspecting the anchor tag in the browser will show the following
a href="#" class="goback" onclick="ErrorsNotifier.Back([object Object]);"
Try
var ErrorsNotifier = {
init: function(items) {
...
var $template = $(template);
$template.find('a.goback').on('click', function(){ErrorsNotifier.Back(items);});
$("#content").empty().append($template);
}
}
notice I used .on() to attach the event handler.
var ErrorsNotifier = {
init: function(items) {
var template = 'Link';
$("#content").html(template);
},
Back: function(x){ alert(x); }
};
Check this
I'm php developer starting to get heavier into ajax and I've come across a problem I am not sure how to solve.
I am creating a form on the fly like this:
function addSearchResult(label, tz) {
var html = '';
html += '<div>'
html += '<form id="product-form" >';
html += '<div class="clock">'
html += '<div class="hour"></div>'
html += '<div class="min"></div>'
html += '<div class="sec"></div>'
html += '<input type="text" id="label" name="Label" placeholder="Label">'
html += '</div>'
html += '<div class="city">GMT</div>'
html += '<a href="#" class="whiteButton submit" id="view-product-button" >View</a>'
html += '</form>'
html += '</div>'
var insert = $(html);
$('#search-results').append(insert.data('tz_offset', tz).find('.city').html(label).end());
}
And I am reading the form results like this:
$('#product-form').submit(function() {
alert('OK');
addProduct('Test Value', 'Test Produlct');
$('input').blur();
$('#add .cancel').click();
this.reset();
return false;
});
The problem is, it does not work. If I put the form directly in the html it works fine. But adding it through ajax, it will not pick up that the form exist.
How should I go about solving this problem?
Using the shortcut event handlers (eg submit() or click()) only works for elements which are placed in the DOM on page load.
For dynamically added elements, you need to use delegate(), or on() for jQuery 1.7+. Try this:
< jQ 1.7
$('#search-results').delegate('#product-form', 'submit', function() {
// rest of your code
});
jQ 1.7+
$('#search-results').on('submit', '#product-form', function() {
// rest of your code
});
I have written a plugin that looks like this so far:
(function($) {
var textarea;
$.fn.bbcode = function(opt)
{
var i = 0;
return this.each(function(i) {
textarea = this;
var strHTML = '<div id="editor" class="editor-instance-' + i + '">';
strHTML += '<div class="btn bold" title="Bold"></div>';
strHTML += '<div class="btn italic" title="Italic"></div>';
strHTML += '<div class="btn underline" title="Underline"></div>';
strHTML += '<div class="btn link" title="Link"></div>';
strHTML += '<div class="btn quote" title="Quote"></div>';
strHTML += '</div>';
$(strHTML).prependTo($(this).parent().parent());
$('div.editor-instance-' + i + ' div').live('click', function(){
var type = $(this).attr('class').substr(4);
switch (type) {
case 'bold':
simpleReplace('b');
break;
}
});
});
};
function simpleReplace(wrapper) {
$(textarea).val(wrapper);
}
})(jQuery);
I call this plugin on 2 textarea's like this:
$(document).ready(function(){
$('.form_add_comment textarea, .form_reply_comment textarea').bbcode({
start: true
});
});
The problem I am having is that clicking on bold which calls simpleReplace() is only setting the value of the 2nd textarea to 'b' despite when I click on the first one.
I think the textarea variable is being overwritten the second time it loops so I probably have my functions in the wrong place.
Any help would be great!
You are right, textarea is overwritten. A solution would be to pass the current textarea to the function:
$('div.editor-instance-' + i + ' div').live('click', {textarea: this}, function(e){
var type = $(this).attr('class').substr(4);
switch (type) {
case 'bold':
simpleReplace(e.data.textarea, 'b');
break;
}
});
Note that the current textarea is passed to the event handler via the event data option.
Then:
function simpleReplace(element, wrapper) {
$(element).val(wrapper);
}
Of course you could also put the simpleReplace function inside the for loop. This way, every textarea gets its "own" simpleReplace function. But this will also increase the memory footprint.