Javascript and JQuery synchronous execute - javascript

I have a function which append some elements if a json file was loaded:
function loadNdraw(title, id){
$("#containerCharts").html("");
var date = $("#dateChart");
var jqJSON = $.getJSON("charts/" + id + "/" + date.val() + ".json");
jqJSON.done(
function(data){
$("#containerCharts").append(
"<div class='divStandard'>"
+ "<div class='titleChart'>"
+ "<div style='float: left;'>" + title + "</div>"
+ "<div style='float: right;'>" + data.lastUpdate +"</div>"
+ "<div style='clear: both;'></div>"
+ "</div>"
+ "<center><div id='" + id + "' class='bodyChart'></div></center>"
+ "</div>"
);
}
);
}
I call this function multiple times
loadNdraw("A", "a");
loadNdraw("B", "b");
loadNdraw("C", "c");
loadNdraw("D", "d");
My problem is that it does not load in this order.
Every time the page is loaded, the order of A,B,C,D is random.
How can I force it to be loaded in order?
I don't want to sort json.

You can use .promise() function in jquery
function loadNdraw(title, id){
var dfd = jQuery.Deferred();
$("#containerCharts").html("");
var date = $("#dateChart");
var jqJSON = $.getJSON("charts/" + id + "/" + date.val() + ".json");
jqJSON.done(
function(data){
$("#containerCharts").append(
"<div class='divStandard'>"
+ "<div class='titleChart'>"
+ "<div style='float: left;'>" + title + "</div>"
+ "<div style='float: right;'>" + data.lastUpdate +"</div>"
+ "<div style='clear: both;'></div>"
+ "</div>"
+ "<center><div id='" + id + "' class='bodyChart'></div></center>"
+ "</div>"
);
}
);
dfd.resolve(true);
}
Then you can simply call
$.when( loadNdraw("A", "a") ).then(
$.when( loadNdraw("B", "b") ).then(
//Some code
);
);
You can simply use .promise as per your need

The JSON scripts are being loaded asynchronously. The order in which the callbacks fire don't depend on the order in which you invoke loadNdraw, but the order in which jqJSON.done gets fired. The file that loads the fastest will cause its jqJSON.done method to be called first, and so on.
If you need these to be in order, you have a few options:
Sort the HTML structure after all results have loaded (or each time a file loads)
Load the charts sequentially, by calling loadNdraw inside of jqJSON.done, meaning that chart B would only start loading after A is done—this is likely to be much slower.
Populate the results in a temporary data structure, keep track of when all requests have completed, and then handle the DOM manipulation based on that data.
Option 3 seems to be your best bet in this situation:
function loadNdraw(title, id) {
$("#containerCharts").html("");
var date = $("#dateChart");
return $.getJSON("charts/" + id + "/" + date.val() + ".json");
}
var options = [
{ title: 'A', id: 'a' },
{ title: 'B', id: 'b' },
{ title: 'C', id: 'c' },
{ title: 'D', id: 'd' }
];
for (var option of options) {
option.promise = loadNdraw(option.title, option.id);
}
$.when(...options.map(o => o.promise)).done(function(...results) {
results.forEach(function(dataArray, idx) {
var title = options[idx].title;
var id = options[idx].id;
$("#containerCharts").append(
"<div class='divStandard'>"
+ "<div class='titleChart'>"
+ "<div style='float: left;'>" + title + "</div>"
+ "<div style='float: right;'>" + dataArray[0].lastUpdate +"</div>"
+ "<div style='clear: both;'></div>"
+ "</div>"
+ "<center><div id='" + id + "' class='bodyChart'></div></center>"
+ "</div>"
);
});
});
Note a couple of things here:
The argument to $.when is a listing of of Thenables—objects like your jqXHR with a then method. You can pass it an array objects by expanding it with the ...spread operator.
The callback for $.when takes a listing of response objects as its parameter, in the order in which the requests were made. They can be collected into an array using the ...rest parameter syntax.

Related

How to add event handlers to dynamically created buttons in jQuery?

I used AJAX to dynamically create the HTML but I've encountered a problem
<script>
function page_loaded(){
jQuery.ajax({
method: "GET",
url: "get_data_dashboard.php",
success: function(data){
var markers = JSON.parse(data);
for(var i = 0; i < markers.length; i++){
var m = markers[i];
var markerHTML = "<div class='marker'>" +
"<span id='naziv'>Naziv zahtjeva: " + m.naziv + "</span></br>" +
"<span id='ulica'>Ulica: " + m.ulica + "</span></br>" +
"<p id='opis'>Opis:</br>" + m.opis + "</p></br>" +
"<span id='email'>Email: " + m.email + "</span></br>" +
"<img id='slika' src='" + m.link_slike + "' />" + "</br>" +
"<textarea rows='5' cols='30' maxlength='500' id='t" + m.marker_id + "' placeholder='Komentar'>" + "</textarea></br>"
+ "<div class='buttons'><a href='odobri_prijavu.php?id=" + m.marker_id + "'>Odobri</a>" +
"<a href='izbrisi_prijavu.php?id=" + m.marker_id + "'>Izbriši</a>" + "</div>" +
"</div><hr>";
$('#content').append(markerHTML);
}
}
})
}
$(document).ready(page_loaded());
</script>
I tried to use buttons first instead of anchor tags but I couldn't figure how to add event handlers to dynamically created buttons that will post a request via AJAX to some php script with the proper id as the value and the value of the textarea. So I used the anchor tag and I was able to send the id, but I can't send the value of the textarea because I don't know how to reference it and even if I referenced it, it will be NULL because its value is set to the anchor tag at the very beginning and I want to type in text in the textarea.
Instead of listening to individual "elements", you can actually listen to a parent of a specific element (You'll need to supply another parameter to on()). A popular pattern is to listen to "body" (because body is a parent to all, technically), but any non-dynamic parent element will work! Here's an example:
//notice the second parameter supplied
$("body").on("click", ".my-dynamic-element", function(e){
//awesome code that makes the world a better place goes here
//this code triggers when .my-dynamic-element is clicked, wootz
});
Event delegation is your friend.
I don`t see any actions that actually do event handling, but a simple solution would be something like:
$(document).on('click', '.your-button-class', function(){
// do your thing
});
<script>
function page_loaded(){
jQuery.ajax({
method: "GET",
url: "get_data_dashboard.php",
success: function(data){
var markers = JSON.parse(data);
for(var i = 0; i < markers.length; i++){
var m = markers[i];
var markerHTML = "<div class='marker'>" +
"<span id='naziv'>Naziv zahtjeva: " + m.naziv + "</span></br>" +
"<span id='ulica'>Ulica: " + m.ulica + "</span></br>" +
"<p id='opis'>Opis:</br>" + m.opis + "</p></br>" +
"<span id='email'>Email: " + m.email + "</span></br>" +
"<img id='slika' src='" + m.link_slike + "' />" + "</br>" +
"<textarea rows='5' cols='30' maxlength='500' id='t" + m.marker_id + "' placeholder='Komentar'>" + "</textarea></br>"
+ "<div class='buttons'>Odobri" +
"<a href='izbrisi_prijavu.php?id=" + m.marker_id + "'>Izbriši</a>" + "</div>" +
"</div><hr>";
$('#content').append(markerHTML);
}
}
})
}
$(document).ready(page_loaded());
function clickHandler(id){
$('#'+id) // selects the button
$('#t'+id) // selects the text area
}
</script>

Javascript forEach return value interpolated into string

stack_html += "<div class='co-stack-layer-title'>Application and Data" +
"<div class='row'>" +
response['Application and Data'].forEach(generateStackItem) +
"</div>" +
"</div>";
stack_html += "<div class='co-stack-layer-title'>Business Tools" +
"<div class='row'>" +
response['Business Tools'].forEach(generateStackItem) +
"</div>" +
"</div>";
stack_html += "<div class='co-stack-layer-title'>DevOps" +
"<div class='row'>" +
response['DevOps'].forEach(generateStackItem) +
"</div>" +
"</div>";
stack_html += "<div class='co-stack-layer-title'>Utilities" +
"<div class='row'>" +
response['Utilities'].forEach(generateStackItem) +
"</div>" +
"</div>";
function generateStackItem(element, index, array) {
var stack_item_html = "";
stack_item_html += "<div class='co-service'>" +
"<a href='" + element['canonical_url'] + "' class='each-service hint hint--top' data-hint='" + element['name'] + "' data-align='left'>" +
"<img src='" + element['image_url'] + "'>" +
"</a>" +
"</div>";
console.log(stack_item_html);
return stack_item_html;
}
I'm trying to use a forEach iteration on a javascript object, and it seems I cannot interpolate the return value into the string addition.
<div class='co-stack-layer-title'>Application and Data<div class='row'>undefined</div></div><div class='co-stack-layer-title'>Business Tools<div class='row'>undefined</div></div><div class='co-stack-layer-title'>DevOps<div class='row'>undefined</div></div><div class='co-stack-layer-title'>Utilities<div class='row'>undefined</div></div>
forEach returns undefined. Use map+join.
"abc" + response['Utilities'].map(generateStackItem).join("") + "def";
When your array is empty, the forEach function will return undefined, e.g.you will get an undefined when you call forEach like this [].forEach(generateStackItem), so check if your response['Application and Data'] is an empty array.
From mozilla
forEach() executes the callback function once for each array element;
unlike map() or reduce() it always returns the value undefined and is
not chainable. The typical use case is to execute side effects at the
end of a chain.
It's common mistake to use return inside forEach in this way but it will not work. You may want to look at map

Difference between executing in JS console and as script

I have a website with links, which should open a Lightbox (3rd party plugin Magnific Popup / jquery). After manipulating content on the site, i have to re-execute some Javascript of the Lightbox plugin, so that it recognizes the new links as links to lightboxes.
function loadLightbox() {
console.log("Lightbox start"); // for debugging
$('a.img').magnificPopup({
type: 'image',
closeOnContentClick: true,
closeBtnInside: false,
image: {
titleSrc: 'title_off',
markup:
'<div class="mfp-figure">' +
'<div class="mfp-close"></div>' +
'<div class="mfp-title"></div>' +
'<div class="mfp-img"></div>' +
'<div class="mfp-bottom-bar">' +
'<div class="mfp-counter"></div>' +
'</div>' +
'</div>' // Popup HTML markup. `.mfp-img` div will be replaced with img tag, `.mfp-close` by close button
}
});
console.log("Lightbox end"); // for debugging
}
I have a function, which manipulates the content on the page changeSuggestion(). Because I want to have the content changed every 6 secondes, I use the setInterval function.
setInterval(function () { changeSuggestion(); loadLightbox(); }, 6000);
My problem is, that it changes every 6s the content on the page, but the lightbox thing doesn't work (although the lightbox function produces output). But when I copy the loadLightbox() function into the firefox console and execute it, the lightbox in does work until it changes the content again. So my question is, what is the difference between executing via setInterval and executing in the Firefox javascript console?
Edit:
changeSuggestion: opens a JSON file, and produces html-formatted output, the page is manipulated by $("#blogSuggestion").html(result)
<!-- language: lang-js -->
function changeSuggestion() {
$.getJSON("blog/blog.json", function (data) {
var n = 0,
showId,
entry,
result;
$.each(data.blogentries, function (key, entry) {
if (entry.id > n) {
n = entry.id;
}
});
showId = randomIntFromInterval(0, n - 1);
if (data.blogentries[showId]) {
entry = data.blogentries[showId];
if (entry.type === "text") {
result = "<a class=\"" + entry.type + "\" title=\"" + entry.title + "\" href= \"#blogID" + entry.id + "\"><b>" + entry.title + "</b>";
} else {
result = "<a class=\"" + entry.type + "\" title=\"" + entry.title + "\" href= \"" + entry.source + "\"><b>" + entry.title + "</b>";
}
if (entry.content) {
result = result + "<br /><p>" + getExcerpt(entry.content, 20, '') + "</p><div id= \"blogID" + entry.id + "\" class=\"blogLightbox mfp-hide\"><img alt=\"" + entry.title + "\" src=\"" + entry.thumb + "\" ><div class=\"blogText\"><h3>" + entry.title + "</h3><p>" + entry.content + "</p></a></div></div>";
} else {
result = result + '<img alt=\"' + entry.title + '\" src=\"' + entry.thumb + '\"></a>';
}
$("#blogSuggestion").html(result);
} else {
console.log("Nothing with ID: " + showId);
}
});
Edit:
As its written in the comments, the content is changed asynchronously. If I put the asynchronously function into the body of changeSuggestion it works!

How to remove entire div from MVC View?

I am developing MVC application.
I have a View in which I show the data row wise.
I have placed a remove link in that row, when user click on that link, that row should be remove.
But its not working...
I display the data in following format.
$('#ProductList').append("<div class='span12' style='margin-left:0px' ><div class='span2'>" +
"<select class='clsProductId ' name='ProductId' id='ddProductList_" + IDD + "' style = 'font-size:12px;width:200px;margin-right:80px;margin-left:20px;' onchange='get(" + IDD + ")'/> </div>" +
"<div id='ProductCode_" + IDD + "' class='span1' style=' margin-left:85px;'></div>" +
"<div id='Weight_" + IDD + "' class='span1' style=' margin-left:55px;'> </div>" +
"<div class='span1'style='margin-left:0px;'><input type='text' id='Quantity_" + IDD + "' class='clsQuantity' name='Quantities' style='width:50px; margin-left:0px;' onblur='StockLinkVisible(" + IDD + ");' /></div>" +
"<div class='span1' style='margin-left:0px;'><a href='#' style='font-size:14px;text-decoration:none;font-weight:bold; color :#ee8929; margin-left:20px;' id='lnkRemove_" + IDD + "' class='clsRemove' onclick='removeElement(" + IDD + ");'>X</a></div>" +
"<div class='span1'style='margin-left:10px; Width:60px;' id='Bandra_" + IDD + "'>123</div>" +
"<div class='span1'style='margin-left:10px; Width:60px;' id='Dadar_" + IDD + "'>123</div>" +
"<div class='span1'style='margin-left:0px; Width:60px;' id='Bhivandi_" + IDD + "'>123</div>" +
"<div class='span1'style='margin-left:10px; Width:40px;' id='Juhu_" + IDD + "'>123</div>" +
"<div class='span1'style='margin-left:10px; Width:40px;' id='Kurla_" + IDD + "'>123</div>" +
"<div class='span1'style='margin-left:10px; Width:60px;' id='Dombivali_" + IDD + "'>123</div>" +
"<div class='span1'style='margin-left:15px; Width:40px;' id='Worli_" + IDD + "'>123</div>" +
"<div class='span1'style='margin-left:15px; Width:60px;' id='Sant_" + IDD + "'>123</div>" +
"<div class='span1'style='margin-left:0px; Width:40px;' id='Bandra_" + IDD + "'>123</div>" +
"<hr></div>");
for removing the row I have written the below code...
function removeElement(cnt)
{
$("#ProductList").on('click', '#lnkRemove_'+cnt, function () {
$(this).closest("div").remove();
});
}
Please check image below for better idea...
On the click event, you call a function
onclick='removeElement(" + IDD + ");
This function binds another click handler to itself, but doesn't actually do anything. Furthermore, the click handler will look for the closest div, which only wraps the delete button, and so doesn't quite do what you want.
$("#ProductList").on('click', '#lnkRemove_'+cnt, function () {
$(this).closest("div").remove();
});
Here's my suggestion: remove the onclick event and the function, and on document ready, register a generic click action as follows:
$('#ProductList').on('click', '.clsRemove', function() {
$(this).closest('div.span12').remove();
});
I chose the class span12 as the selector simply because it's the only one I saw that the top-level wrapping div (the "row") had. Feel free to add a more descriptive selector and using that instead.
You no need to call removeElement for each element. Just configure the below code in document.ready
$(document).ready(function(){
$("#ProductList").on('click', '.clsRemove', function () {
$(this).parents("div.span12:first").remove();
});
});
update to this:
function removeElement(cnt)
{
$('#lnkRemove_'+cnt).closest("div.span12").remove();
}
You don't need to bind click event again instead you can do as suggested or better to delegate the event to the closest static parent (try unobtrusive js):
$("#ProductList").on('click', '[id^="lnkRemove"]', function () {
$(this).closest("div.span12").remove();
});

Update div html and content with ajax

I would yo have a functionality similar to the StackExchange link on the top left of the Stack Overflow site.
As I understand it, after the stack exchange link is clicked, the following things happen:
the hidden div container is shown.
this div is populated with its html and the actual data using ajax (maybe jquery)
I've noticed that the html and data does not appear in the page markup, so I think it is probably fetched using javascript/jquery/ajax.
one note - I'm using asp.net mvc 2 and linq-to-sql.
Please give me examples on how this can be acheived, or maybe links to similar examples,
thanks.
You can achieve this with jQuery and page methods in the code behind.
//Gets the list of requests
function getRequestList() {
// call server-side webmethod using jQuery
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Index.aspx/GetOrdersForApproving",
data: "{ }", // send an empty object for calls with no parameters
dataType: "json",
success: displayRequests,
failure: reportError
});
}
//displays the requests in the ul
function displayRequests(result) {
// ASP.NET encapsulates JSON responses in a property "d"
if (result.hasOwnProperty("d")) { result = result.d; }
// iterate through player list and add info to the markup
var ul = $("#requestsForApproval");
for (i = 0; i < result.length; i++) {
var li = $("<li class='approvalListItem'><div>"
+ "<h3>" + result[i].OrderID + " - " + result[i].Supplier + "</h3>"
+ "</div>"
+ "<div>"
+ result[i].Description
+ "</div>"
+ "<div> "
+ "<table width='100%'>"
+ "<tr>"
+ "<td>"
+ "Quant: " + result[i].Quantity
+ "</td>"
+ "<td>"
+ "Price: " + result[i].UnitPrice
+ "</td>"
+ "<td>"
+ "Total: " + result[i].Value
+ "</td>"
+ "</tr>"
+ "</table>"
+ "</div>"
+ " <div class='approvalButtons' style='display:none'>"
+ "<ul><li class='approveButton'>Approve</li>"
+ "<li class='rejectButton'>Reject</li></ul>"
+ "</div>"
+ "<input type='hidden' class='hiddenID' name='OrderLineID' value='" + result[i].OrderLineID + "'>"
+ "</li>");
ul.append(li);
}
Code Behind:
/// <summary>
/// Gets a list of Request Lines
/// </summary>
/// <returns>List of order lines</returns>
[WebMethod]
public static List<iOrderLine> GetOrdersForApproving()
{
try
{
List<iOrderLine> Lines = new List<iOrderLine>();
foreach (Objects.Database.OrderLine oOrderLine in Objects.Database.OrderLine.GetLinesWaitingFor(StaticStore.CurrentUser.UserID, int.MinValue))
{
Lines.Add(new iOrderLine(oOrderLine));
}
return Lines;
}
catch (Exception)
{
throw;
}

Categories