Correct way to implement bootstrap popover with ajax request - javascript

Trying to implement Bootstrap's popover which will appear after response for AJAX request will be received.
Here is HTML code:
<div class="row">
<div class="col-md-3">
<h4>
<strong>Sorted laptops:</strong>
</h4>
</div>
<div class="col-md-2 col-md-offset-7">
<button class="btn btn-info pull-right" data-loading-text="Generating link to share, please wait..." id="share_results">Share results</button>
</div>
</div>
Here is JS code:
$('#share_results').click(function(event) {
var $share_results, delimiter, descriptions, query;
$share_results = $(this);
$share_results.button('loading');
descriptions = $.map($('td.laptop_desc'), function(val) {
return $(val).text().trim().replace(/\s{2,}/g, ' ');
});
delimiter = $('#delimiter').val();
query = descriptions.join(' ' + delimiter + '\n\n');
$.post('/path', {
'query': query,
'delimiter': delimiter
}, function(resp) {
var content, hash, url;
$share_results.button('reset');
hash = resp['hash_string'];
url = window.location.origin + window.location.pathname + '?q=' + hash;
content = "<input class='form-control input-sm' value='" + url + "'>";
return $share_results.popover({
container: '.container',
html: true,
delay: 500,
placement: 'left',
'content': content
}).popover('show');
});
});
No CSS changes were made.
Initial button state:
State after request is received:
Here I am having two problems:
popover always on top - doesn't disappear
popover width is too small
How to fix that?

Related

How to add <div></div> and name+size+delete appear in new div. javascript jquery

I have used ajaxSubmit to upload file to linux server successfully. After upload file, name+size+delete should be appear in my website.
For example, 1.jpg、2.jpg、3.jpg were uploaded to server, my submit website shoule be appeared like:
1.jpg 3k delete
2.jpg 4k delete
3.jpg 5k delete
Before upload files, My html structure is :
<td style="width:30%" id="impPic">
<div class="btn">
<span>addFile</span>
<input id="fileupload" type="file" name="mypic">
</div>
<div class="files"><b>...</b><span>...</span></div>
</td>
After uploaded three files, I wanted html like:
<td style="width:30%" id="impPic">
<div class="btn">
<span>addFile</span>
<input id="fileupload" type="file" name="mypic">
</div>
<div class="files"><b>...</b><span>...</span></div>
<div class="files"><b>...</b><span>...</span></div>
<div class="files"><b>...</b><span>...</span></div>
</td>
My files has fixed css style:
.files{height:10px; font-size:10px;line-height:22px; margin:10px 0}
Here is my ajaxSubmit code:
var divD=1;
$(function () {
var files = $(".files");
newDiv = "<div class='files'+divD+''><b class='dataname'>'+data.name+'('+data.size+'k)</b> <span class='delimg' name='+data.name+'('+data.size+'k)' rel='+data.pic+'>delete</span></div>";
$("#fileupload").change(function(){
$("#myupload").ajaxSubmit({
dataType: 'json',
beforeSend: function() {
......
},
uploadProgress: function() {
},
success: function(data) {
$(newDiv).insertAfter($('#impPic div:eq('+divD+')'));
divD = divD + 1;
$('.files').html("<b class='dataname' >"+data.name+"("+data.size+"k)</b> <span class='delimg' rel='"+data.pic+"'>delete</span>");
},
error:function(xhr){
......
}
});
});
});
But unfortunately, it failed. I suppose files'+divD+'.html is wrong. Who can help me ?
I see two problem with your code...
First was the html string "newDiv" that you are trying to insert and the second is the invocation for jQuery .html() on an string literal object "files'+divD+'".
You are expecting the newDiv object to have a concatenated html string with the value of divD and the data came from the response but object data is undefined until the logic flows in the success callback.
and I believe the second problem would return a jquery TypeError problem, as you are trying to call the .html() function on a non DOM - jQuery object .
Update your code and try it this way:
newDiv = "";
...
success: function(data) {
newDiv = "<div class='files" + divD + "'><b class='dataname'>" + data.name + "(" + data.size + "k)</b><span class='delimg' name='" + data.name + data.size + "k' rel='" + data.pic + "'>delete</span></div>";
$(newDiv).insertAfter($('#impPic div:eq(' + divD + ')'));
divD = divD + 1;
$(".files" + divD).html("<b class='dataname'>" + data.name + "(" + data.size + "k)</b><span class='delimg' rel='" + data.pic + "'>delete</span>");
},
...
EDIT:
You can update the html string and add an Id to have a unique selector on each of the element with files class and instead of the above selector, change the class selector "." to an Id selector "#".
and your code will look like these...
newDiv = "<div id='files" + divD + "'class='files'"...
...
$("#files" + divD).html("<b class='dataname'>" + data.name + "(" + data.size + "k)</b><span class='delimg' rel='" + data.pic + "'>delete</span>");
If that doesn't suit your needs you can just leave the first example above and update your css selector to:
[class^='files'] instead of `.files`.
This will select all of the element with a class that begins with "files".
files'+divD+' is a string, and you try to call method html from string, which does not exist.
Try $('.files' + divD).html('something'); or files.find('.files' + divD).html('something');

Is a JavaScript function not executed always the same way?

Using javascript to create the content of the body of a modal i notice that the result is not always the same. Sometimes the modal body is empty some times is as expected. The function is this:
function newServiceModal() {
$("#newServicesModal-bd").html("");
$("#newServiceModalHiddenId").val("");
$("#newServiceModal-title").html("New Service - body not created yet ");
var modalbody = "";
var div = document.createElement('div');
//var provideroptions = {
// providerid:providerid
//}
$.ajax({
type: 'post',
url: baseSiteURL + 'home/ReturnProviders', //supposed to return all available providers
//data: options
}).done(function (data) {
$("#newServiceModal-title").html("New Service - body was created");
modalbody = modalbody.concat(" <div class='col-md-4'> <p>Provider</p><select id='newproviderSelect' class='form-control'>");
for (i = 0; i < data.length; i++) {
if (i==0)
modalbody = modalbody.concat(" <option value=" + data[i].id + " selected>Type: " + data[i].providerType + "</option>");
else
modalbody = modalbody.concat(" <option value=" + data[i].id + ">Type: " + data[i].providerType + "</option>");
}
modalbody = modalbody.concat('</select></div>');
div.innerHTML = modalbody;
document.getElementById('editServicesModal-bd').appendChild(div);
$("#newServiceModal-title").html("New Service - body was created");
})
//var customeroptions = {
// customerid: customerid
//}
$.ajax({
type: 'post',
url: baseSiteURL + 'home/ReturnCustomers', //supposed to return all available customers
//data: options
}).done(function (data) {
modalbody = modalbody.concat(" <div class='col-md-4'> <p>Customer</p><select id='newcustomerSelect' class='form-control'>");
for (i = 0; i < data.length; i++) {
if (i==0)
modalbody = modalbody.concat(" <option value=" + data[i].id + " selected>" + data[i].company + "</option>");
else
modalbody = modalbody.concat(" <option value=" + data[i].id + ">" + data[i].company + "</option>");
}
modalbody = modalbody.concat('</select></div>');
div.innerHTML = modalbody;
document.getElementById('editServicesModal-bd').appendChild(div);
})
//var applicationroptions = {
// applicationid: applicationid
//}
$.ajax({
type: 'post',
url: baseSiteURL + 'home/ReturnApplications', //supposed to return all available applications
//data: options
}).done(function (data) {
modalbody = modalbody.concat(" <div class='col-md-4'> <p>Application</p><select id='newapplicationSelect' class='form-control'>");
for (i = 0; i < data.length; i++) {
if (i==0)
modalbody = modalbody.concat(" <option value=" + data[i].id + " selected>" + data[i].name + "</option>");
else
modalbody = modalbody.concat(" <option value=" + data[i].id + ">" + data[i].name + "</option>");
}
modalbody = modalbody.concat('</select></div>');
div.innerHTML = modalbody;
document.getElementById('newServicesModal-bd').appendChild(div);
})
$("#newServiceModal").modal('show');
}
the modal html is this:
<div id="newServiceModal" class="modal fade local-modal" role="dialog" aria-hidden="true" position="fixed">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<section id="newService">
#using (Html.BeginForm("Services", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="newServiceModal-title">original title</h4>
</div>
<div id="newServiceModalHiddenId"></div>
<div class="modal-body row" id="newServicesModal-bd" style='margin-right:20px; margin-left:20px;'>
#* bootstrap.min.css applies here this rule : .row{margin-right:-15px;margin-left:-15px} *#
#* services.js puts content here *#
</div>
<div class="modal-footer">
<div class="col-md-6 pull-left">
<button id="savebtn" type="button" class="btn btn-primary ladda-button" data-style="expand-right" data-size="l" onclick="saveNewService()"><span class="ladda-label">Save</span></button>
<button type="button" class="btn btn-warning" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
#*<div class="col-md-6">
<button type="button" class="btn btn-danger" onclick="deleteUser()">Delete</button>
</div>*#
</div>
}
</section>
</div>
</div>
The first line of the function
$("#newServicesModal-bd").html("");
is emptying the modal body (to not have concatenated the previous content with what is about to be created).
I imagine my problem is related with this, because 9 of 10 times the modal is created as expected but some times its body is empty, which makes me suspect that either the body content is not created at all or the
$("#newServicesModal-bd").html("");
is executed after the body content is created.
To verify that the content does get created i have this
$("#newServiceModal-title").html("New Service - body not created yet ");
right after i empty the body of the modal and i put this
$("#newServiceModal-title").html("New Service - body was created");
when i get some content created.
I would see the title always to be "New Service - body was created" so i think that its safe to assume that the body does get created.
I read about hoilsting and scope and i dont think think that they have to do with this matter can't find but in any case i can find any reason for this inconsistent behaviour.
If any one could point out why this happens i would appreciate it.
Thank you for reading this.
Solution (?)
I investigated a little more and i want to make some more comments, maybe i will clarify the problem better.
The function newServiceModal() is making 3 calls to different other functions. ReturnProviders, ReturnCustomers and ReturnApplications(which are in my HomeController class) and they are calling the functions that are actually retrieving the data from the database.
I found that the problem occurs only when they (ReturnProviders, ReturnCustomers and ReturnApplications) are not called with this order. The reason that they are not called with the same order as they are written in the function was shown to me by a seasoned programmer after i explained him the problem and he googled, totally he needed 5 minutes.
I just added
async: false
in my ajax requests and now the results are coming in the order that are expected.
It was pointed out to me that my approach is not correct and i should be making one ajax call for all the data i need instead of 3. So i will post the correct solution as soon as i do it.
Thank you all for your time and help.
I managed to solve the problem by making one ajax call, i think that the solution is irrelevant with my question, if anyone is interested email me to give more information.
As far as my original question is concerned the answer is that the ajax requests are being executed as they are written in the function but sometimes the results are not coming back in the same order unless this is added to the ajax call
async: false
Solution was found in this post
How do I make jQuery wait for an Ajax call to finish before it returns?
Like #ManoDestra said in the comments, you shouldn't execute functions that reference or manipulate DOM elements until after they have been loaded. It's best practice to put your newServiceModal() code at the end of the body or run it inside a window.onload event or $(function(){ ... }) wrapper.

How to auto hide multiple alerts one by one. first in - first out?

I have a notification area in a webpage, which can contain multiple bootstrap alerts.
<div class='notification-area> </div>
I am trying to display multiple alerts as they come, and make the oldest ones auto close after 5 seconds, first one first out.
Here's what I have so far. note: it closes everything all at once.
showNotification(header: string, text: string, alertAttribute: string) {
var notificationBoxID: string = "notificationBox" + $('.notification-area').children().length;
//Appends this html into the notification-area class
$('.notification-area').append(
'<section id="' + notificationBoxID + '" class="alert alert- dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
'<p style ="font-weight: bold;" class="notification-message-header"></p>' +
'<p class="notification-message-body"></p>' +
'</section>'
);
// Selects the children of the notificationBoxId section.
var notificationSel = '#' + notificationBoxID;
//set the notification: header, body and css style dynamically
$(notificationSel + ' > .notification-message-header').text(header);
$(notificationSel + ' > .notification-message-body').text(text);
$(notificationSel).addClass(alertAttribute);
// Auto hides alerts, oldest first
$(".alert").show(() => {
setTimeout(() => {
$(".alert").fadeTo(500, 1).slideUp(500, () => {
$(notificationBoxID).hide();
})
}, 5000)
});
Anyone knows how I can approach this issue? I have tried everything. Thanks so much.
toastr has timeouts, so they are handled fifo.
<script src="toastr.js"></script>
var options = {
"preventDuplicates": true,
"timeOut": 5000
};
toastr.options = options;
toastr.warning('this is the message, 'Warning:');

Javascript (JQuery) and Bootstrap modals

I am currently working on a project that displays some information about movies(pulled from omdbapi.com) in a modal depending on the link that is clicked. Previously this was all done using PHP in the back end and was working without any issues, but as a page can have anywhere from 5-100 movie links on it, creating all of the modals was causing the page to take a while to load.
Now I want to move the modal creation/popup to the front with JS so that it is only created when and if that particular movie link is clicked. Moving it to use Javascript I have encountered two issues I am not able to solve.
The movie title link must be clicked twice in order for the modal to appear and I am not sure what is causing this. Looking at the dev tools I can see that on the first click a modal is created, but for some reason not displayed. On the second click another is created and displayed.
Once a modal is displayed I am unable to get my "X" button to close it. The code I was using in PHP"<button type=\"button\" class=\"close exit-button\" data-dismiss=\"modal\">×</button>"
no longer works. Bootstrap does have the ability to show/hide a modal with $("#movieInfo").modal("hide"), but if I create a function to run this when the button is clicked it still does not hide.
Here is the JS code that I am using:
window.addEventListener("load", function() {
var movielink = document.getElementById("movie");
movielink.addEventListener("click", function(){generateModal(this.innerHTML);}, false);
}, false);
function generateModal(movieT) {
var movieTitle = encodeURIComponent(movieT).replace(/%20/g, '+');
var url = "https://www.omdbapi.com/?t="+movieTitle+"&y=&plot=short";
$.getJSON(url, function(json) {
$('#modalContainer').html("<div id = \"movieInfo\" class = \"modal fade\" role = \"dialog\">"
+ "<div class \"modal-dialog modal-sm\">"
+ "<div class = \"modal-content\">"
+ "<div class = \"modal-body\">"
+ "<div class = \"poster\">"
+ "<img src = "+json.Poster+" width = 200 height = 300>"
+ "</div>"
+ "<div class = \"movie-specs\">"
+ "<button id = \"closebtn\" type=\"button\" class=\"close exit-button\"s onclick = \"closeModal()\">×</button>"
+ "<h3 class = \"movie-title\">"+json.Title+" ("+json.Year+")</h3>"
+ "<p class = \"topmargin-gone\">"+json.Rated+" | "+json.Runtime+" | "+json.Genre+"</p>"
+ "<p class = \"topmargin-gone\">"+json.Released+"</p><hr>"
+ "<div class = \"ratingInfo\">"
+ "<h4 class = \"topmargin-gone\"><strong>Ratings</strong></h4><p class = \"topmargin-gone\">Rated: "+json.imdbRating+"/10 "
+ "from "+json.imdbVotes+" users | Metascore: "+json.Metascore+"</p>"
+ "</div><hr>"
+ "<div class = \"plot-actors\">"
+ "<p>"+json.Plot+"</p><p><strong>Director: </strong>"+json.Director+"</p>"
+ "<p><strong>Writers: </strong>"+json.Writer+"</p>"
+ "<p><strong>Actors: </strong>"+json.Actors+"</p>"
+ "</div>"
+ "</div>"
+ "</div>"
+ "</div>"
+ "</div>"
+ "</div>");
});
$("#movieInfo").modal("show");
}
This is how the the links are created inside a loop in PHP:
$allDisplay .= "<li><em><a id ='movie' href = '#'>"
. "$title[$z]</a></em> - $nominees[$z]</li>";
And finally this is how the HTML looks where the links and modal reside:
<html>
<head>
<meta charset = "utf-8">
<title>Award Search</title>
<!-- jQuery Library -->
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!--Bootstrap CSS -->
<link rel = "stylesheet" href = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<!-- Styles CSS sheet -->
<link rel = "stylesheet" href = "/src/styles/styles_awardQuery.css">
<!--Bootstrap JavaScript -->
<script src = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src = "/src/php/modalCreation.js"></script>
</head>
<body>
<div class = "site-wrapper">
<div class = "site-wrapper-inner">
<div class = "cover-container">
<div class = "masthead clearfix">
<div class = "inner">
<h3 class = "masthead-brand">Award Search</h3>
<ul class = "nav masthead-nav">
<li>Home</li>
<li>About</li>
<li>Logout</li>
</ul>
</div>
</div>
<div class = "inner cover">
<h1 class = "cover-heading">Academy Award Show Number <?php echo $academyNumber; ?>
<?php echo "($year[1])<br><span class = \"small\">Hosted by $acHost</span>"; ?></h1>
<?php echo $allDisplay; ?>
<div id ="modalContainer"></div>
</div>
</div>
</div>
</div>
</body>
</html>
I realize this looks messy, but just want to get it functioning right now. If anyone has any insight into either of the issues it would be much appreciated.
Thanks.
The $("#movieInfo").modal("show"); is triggered before the bloc was actually append to your web page. This is due to the network latency and the Async behaviour of AJAX call that allow your script to continue without waiting to get an answer.
To avoid this, you should put this line at the end of the network callback :
function generateModal(movieT) {
var movieTitle = encodeURIComponent(movieT).replace(/%20/g, '+');
var url = "https://www.omdbapi.com/?t="+movieTitle+"&y=&plot=short";
$.getJSON(url, function(json) {
$('#modalContainer').html('<div id="movieInfo" class="modal fade" role="dialog">'
+ '<div class "modal-dialog modal-sm">'
+ '<div class="modal-content">'
+ '<div class="modal-body">'
+ '<div class="poster">'+'<img src='+json.Poster+' width=200 height=300>'+'</div>'
+ '<div class="movie-specs">'
+ '<button id="closebtn" type="button" class="close exit-button"s onclick="closeModal()">×</button>'
+ '<h3 class="movie-title">'+json.Title+' ('+json.Year+')</h3>'
+ '<p class="topmargin-gone">'+json.Rated+' | '+json.Runtime+' | '+json.Genre+'</p>'
+ '<p class="topmargin-gone">'+json.Released+'</p><hr>'
+ '<div class="ratingInfo">'
+ '<h4 class="topmargin-gone"><strong>Ratings</strong></h4><p class="topmargin-gone">Rated: '+json.imdbRating+'/10 '
+ 'from '+json.imdbVotes+' users | Metascore: '+json.Metascore+'</p>'
+ '</div><hr>'
+ '<div class="plot-actors">'
+ '<p>'+json.Plot+'</p><p><strong>Director: </strong>'+json.Director+'</p>'
+ '<p><strong>Writers: </strong>'+json.Writer+'</p>'
+ '<p><strong>Actors: </strong>'+json.Actors+'</p>'
+ '</div>'+'</div>'+'</div>'+'</div>'+'</div>'+'</div>');
// after we have made the modal, we show it
$("#movieInfo").modal("show");
});
// code here will be fired before the end of the AJAX call
}
For your other bug, I think the double modal may explain the behaviour, so patch this first, and told us if you still can't close your modal ;)

Replacing a div that is called by jQuery onclick

I have a list object and each contains an upvote div that is called onclick by my jQuery (I essentially flip the vote button in each div and asynchronously change the vote for that div via Ajax).
All object divs are contained within row.replace which I use to sort the objects asynchronously. The thing is that once I click on the sorter and sort the content of the .row.replace div, the upvote divs in the sorted list of objects stop getting called onclick ie. I can upvote and remove my upvote before sorting with jQuery+ajax, once the sort is applied and the contents of the div are replaced, my upvote button stops working.
Here is the jQuery:
$(document).ready(function () {
$('.sorter').click(function () {
$('.row.replace').empty();
$('.row.replace').append("<br><br><br><br><p align='center'><img id='theImg' src='/media/loading1.gif'/></p><br><br><br><br><br><br><br><br>");
var sort = $(this).attr("name");
$.ajax({
type: "POST",
url: "/filter_home/" + "Lunch" + "/" + "TrendingNow" + "/",
data: {
'name': 'me',
'csrfmiddlewaretoken': '{{csrf_token}}'
},
dataType: "json",
success: function (json) {
//loop through json object
//alert("yoo");
$('.row.replace').empty();
for (var i = 0; i < json.length; i++) {
$('.row.replace').append("<div class='showroom-item span3'> <div class='thumbnail'> <img class='food_pic' src='/media/" + json[i].fields.image + "' alt='Portfolio Image'> <div class='span3c'> <a><b>" + json[i].fields.name + "</b> </a> </div> <div class='span3d'> posted by <a><b>" + json[i].fields.creator.username + "</b></a> </div> <div class='span3c'> <div class='btn-group'> <div class='flip flip" + json[i].pk + "'> <div class='card'> {% if 0 %} <div class='face front'> <button type='button' class='btn btn-grove-one upvote' id='upvote' name='" + json[i].pk + "'>Upvoted <i class='glyphicons thumbs_up'><i></i></i><i class='vote-count" + json[i].pk + "'>" + json[i].fields.other_votes + "</i></a></button> </div> <div class='face back'> <button type='button' class='btn btn-grove-two upvote' id='upvote' name='" + json[i].pk + "'>Upvote <i class='glyphicons thumbs_up'><i></i></i><i class='vote-count" + json[i].pk + "'>" + json[i].fields.other_votes + " </i></a></button> </div> {% else %} <div class='face front'> <button type='button' class='btn btn-grove-two upvote' id='upvote' name='" + json[i].pk + "'>Upvote <i class='glyphicons thumbs_up'><i></i></i><i class='vote-count" + json[i].pk + "'>" + json[i].fields.other_votes + " </i></a></button> </div> <div class='face back'> <button type='button' class='btn btn-grove-one upvote' id='upvote' name='" + json[i].pk + "'>Upvoted <i class='glyphicons thumbs_up'><i></i></i><i class='vote-count" + json[i].pk + "'>" + json[i].fields.other_votes + "</i></a></button> </div> {% endif %} </div> </div> </div> <div class='btn-group'> <button type='button' class='btn btn-grove-two'><i class='glyphicons comments'><i></i></i>" + json[i].fields.comment_count + "</a></button> </div> </div> </div> </div>");
}
//json[i].fields.name
},
error: function (xhr, errmsg, err) {
alert("oops, something went wrong! Please try again.");
}
});
return false;
});
$('.upvote').click(function () {
var x = $(this).attr("name");
$.ajax({
type: "POST",
url: "/upvote/" + x + "/",
data: {
'name': 'me',
'csrfmiddlewaretoken': '{{csrf_token}}'
},
dataType: "json",
success: function (json) {
var y = "vote-count" + x;;
$('i[class= "' + y + '"]').text(json.vote_count);
//flip button
$('.flip' + x).find('.card').toggleClass('flipped');
},
error: function (xhr, errmsg, err) {
alert("oops, something went wrong! Please try again.");
}
});
return false;
});
});
The click event handler only binds to elements that exist in the DOM when the function is actually called. You need to use a delegated on() event listener to bind to future elements as well. So for your code:
$('.upvote').click(function(){
change to:
$("body").on("click", '.upvote', function(event){
Should catch future click events. I'm using "body" as the outer selector because I don't know what your HTML looks like, but it's best to "outer-bind" to the nearest ancestor to the '.upvoteelements (so if they are all contained in aul` with id "vote-list" bind to that instead of "body").

Categories