Is a JavaScript function not executed always the same way? - javascript

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.

Related

Javascript JQuery append does not work

I am attempting to dynamically add some content to some content that was just dynamically added. I want to put a bunch of OPTION items into a SELECT that I just added. The SELECT is added along with some code from an external .php file (see below). This div appears just fine. However, the contents that I attempt to add inside the SELECT does not appear. My SELECT is simply empty. I get no errors. The console output in the code below checks out and prints what I expect it to.
Here is the Javascript code:
$.get("test-new-product-modal.php", function(data){
$(".modal-body").html(data);
});
$divSelect = $("#product-list");
for(var i = 0; i<(arrayProductTypes.length); i++){
$divOption = $("option", {'value' : i});
$divOption.html(arrayProductTypes[i][0]);
$divSelect.append($divOption);
console.log("Product ID at "+i+" is: "+arrayProductTypes[i][0]);
}
Here is the .php file that I add HTML from:
<div class="container-fluid no-padding">
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>Välj en produkt.</h4>
<select id="product-list" class="form-control">
<!-- <option>DRA</option>
<option>DRB</option> -->
</select>
<div class="divider-line"></div>
</div>
<div class="col-sm-6 col-md-6 col-lg-6">
<p class="product-add-description">Text.</p>
</div>
</div>
Try jQuery add() method
$.get("test-new-product-modal.php", function(data){
$(".modal-body").html(data);
});
$divSelect = $("#product-list");
for(var i = 0; i<(arrayProductTypes.length); i++){
$divOption = $("option", {'value' : i});
$divOption.html(arrayProductTypes[i][0]);
$divSelect.add($divOption);
console.log("Product ID at "+i+" is: "+arrayProductTypes[i][0]);
}
you can find the doc here
Working jsFiddle of something similar to what you are expecting:
It's a lot simpler to add in a HTML element like this:
.append('<option value="' + array[i] + '">' + array[i] + '</option>');
I believe its how you declare your option object try this:
$divOption = $("<option>").attr('value',i);
You are not adding options correctly. If you want to add options like jquery object you can do this
for (var i = 0; i < (arrayProductTypes.length) ; i++) {
$divOption = $('<option></option>').val(i).html(arrayProductTypes[i][0])
$divSelect.append($divOption);
console.log("Product ID at " + i + " is: " + arrayProductTypes[i][0]);
}
This line $divOption = $("option", {'value' : i}); doesn't return the option object instead it is returning an empty array so you need to use
$('<option></option>').val(i).html("somehtml") to return an option.

Call javascript from appended content

I have a problem calling ajax from appended content.
Let's show you my example (An example calling ajax request from appended content, and other call ajax request from none appended content):
https://jsfiddle.net/r7f3zo92/
$(document).ready(function() {
new AjaxUpload("#change", {
action: 'verifImg',
name: 'uploadfile',
onSubmit: function(file, ext) {
if (!(ext && /^(png|jpeg|jpg|bmp|gif)$/.test(ext))) {
return false;
}
},
onComplete: function(file, response) {
$("#imagechange").html(response);
}
});
$(document).on('click', '#test', function(ev) {
$('body').find('#show').remove();
var modal =
'<div class="modal fade" id="show" tabindex="-1" role="dialog" aria-labelledby="show">' +
'<div class="modal-dialog" role="document">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>' +
'<h4 class="modal-title" id="myModalLabel">Test</strong></h4>' +
'</div>' +
'<div class="modal-body">' +
'<div class="row">' +
'<div class="form-group">' +
'Change Image' +
'<br/>' +
'<div id="imagechange"></div>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'</div>';
$('body').append(modal);
$('#show').modal({
show: true
});
return false;
});
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="http://www.phpclasses.org/browse/download/1/file/51812/name/ajaxupload.3.5.js"></script>
<strong>Example with appended content:</strong>
<br/>
An example to test
<br/>
<br/>
<strong>Direct example:</strong>
<br/>
<div class="form-group">
Change Image
<br/>
<div id="imagechange"></div>
</div>
In JSFIDDLE it's work fine, but locally no ! strange !
Can you find the problem related to this example ? Thank's
From what I gathered from the ajaxupload.js source code, it seems like it expects one element (or id of element) available on load. On the onload event, it binds a whole lot of stuffs to it, using mousemove, etc. instead of simple click events. it was done for a reason (handle differences in browsers), but it means that:
ajaxupload is only bound to one html element, which must be available on load
it is not possible (or highly complicated) to simulate a click event on the #change button from code, since we don't know exactly what type of event and parameters it requires.
So, in my opinion, you have the following choices:
create a hidden input that you bind to a second AjaxUpload, then move it into your form every time it shows
change the library you use
To be clearer upon the first choice, here is a working jsfiddle. The idea is:
on load, you create a button #choice2 which is hidden
everytime you create your modal, you move (not clone) your button inside the modal
upon modal close, you put back the button inside the body and hide it
your button is ready for the next modal call.
Of course, this approach is a dirty fix and works only when you are sure only one component at a time needs the hidden button.

list.js not working on dynamically created elements created dynamically using $.getJSON

I am trying to make a sortable list using list.js on codepen. The following is the code I have so far:
HTML:
<h1>FCC News Viewer</h1>
<div id="users">
<form id="searchbox">
<input type="form" class="search" id="magnifying_glass"></input>
</form>
<div id="sort-by">
<button class="sort" data-sort="date">Sort by date</button>
<button class="sort" data-sort="rank">Sort by rank</button>
<button class="sort" data-sort="author">Sort by author</button>
</div>
<ul class="list">
</ul>
</div>
Javascript:
$('document').ready(function(){
$.getJSON("http://www.freecodecamp.com/news/hot", function(json) {
for (i=0; i<Object.keys(json).length; i++) {
$('ul').append("<li class='article'><img src="+json[i].author.picture+"\><a href="+json[i].link+" class='headline'>"+json[i].headline+"</a><br><a href='http://www.freecodecamp.com/"+json[i].author.username+" class='author'>"+json[i].author.username+"</a><p class='rank_text'>"+"♥"+"<span='rank'>"+json[i].rank+"</span></p><p class='date'></p></li>")
if (i == Object.keys(json).length-1) {
var options = {
valueNames: [ 'date', 'rank', 'author' ]
};
var userList = new List('users', options);
}
}
})
})
I have both the jquery and list.js libraries loaded. When I press any of the sort buttons, three elements of the 100 I created move and not even according to the sorting category. Is there something more to creating a sortable list that I am missing?
I noticed an issue with the opening and closing quotes (double and single), also date should be included in the options, and even though I did not find it explicitly written in the documentation, it seems that the sorting class should be attached to an immediate child of the li item - this is what fixed rank sorting for me. This is how I have adjusted your codepen js:
$('document').ready(function(){
$.getJSON("http://www.freecodecamp.com/news/hot", function(json) {
for (i=0; i<Object.keys(json).length; i++) {
$('ul').append(
"<li class='article'>" +
"<img src='" + json[i].author.picture + "' /'>" +
"<a href='" + json[i].link + "' class='headline'>" +
json[i].headline +
"</a><br>" +
"<a href='http://www.freecodecamp.com/" + json[i].author.username +
"' class='author'>" + json[i].author.username + "</a>" +
"<p class='rank'>♥<span='rank-span'>"
+ json[i].rank + "</span></p><p></p></li>");
if (i == Object.keys(json).length-1) {
var options = {
valueNames: [ 'rank', 'author' ]
};
var userList = new List('users', options);
}
}
})
})
I would strongly suggest not mixing in any HTML code in the Javascript file. What you have is considered obtrusive. I would redefine the problem after you have factored out the HTML that is embedded in the Javascript, this can lead to some very messy and hard to understand code. The separation of presentation and behavior is very important.
Please see:
https://en.wikipedia.org/wiki/Unobtrusive_JavaScript
Here is the section of code concerning this:
$('ul').append(
"<li class='article'><img src="+json[i].author.picture+"\>
<a href="+json[i].link+" class='headline'>"+json[i].headline+"</a>
<br><a href='http://www.freecodecamp.com/"+json[i].author.username+" class='author'>"+json[i].author.username+"</a>
<p class='rank_text'>"+"♥"+"<span='rank'>"+json[i].rank+"</span></p><p class='date'></p></li>")

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:');

Correct way to implement bootstrap popover with ajax request

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?

Categories