javascript code does not execute after a loop - javascript

I have the following code (snippet from a larger function):
var res = data.results;
for (var i=0;i<res.length;i++) {
$('<option value="' + res[i] + '">' + res[i] + '</option>').appendTo(sel);
}
if (data.select && data.select!='') { sel.val(data.select); }
For some reason, the
if (data.select && data.select!='') { sel.val(data.select); }
line just wasn't executing, and is appearing greyed out in Firebug suggesting that Firebug somehow knows it is not reachable. If I make a simple change to the code like this:
var res = data.results;
for (var i=0;i<res.length;i++) {
var opt = '<option value="' + res[i] + '">' + res[i] + '</option>';
$(opt).appendTo(sel);
}
if (data.select && data.select!='') { sel.val(data.select); }
the last line runs without issue.
I found similar post on here where the for loop had a <= for the while parameter causing an error and although this is not the case here, when I stepped through the code it was trying to execute the loop one more time than it should have, i.e. if res.length was 4, it was allowing i to increment to 4 and then trying to execute the code in the loop which was therefore ending the code because res[i] was out of range, even though it wasn't placing an error in the console. If I change the code as demonstrated, the loop does not run when i == res.length
So, how did Firebug know that the original code wasn't going to allow execution past the end of the loop, and why is the loop executing one more time than it should?
The entire function is below and is a success callback from a jQuery ajax call which populates a select with the values received from the server:
function GetDeptsOK(data, textStatus, jqXHR) {
var sel = $('#orgpicker').find('select[name="orgpicker-dept"]');
if (sel.length == 0) {
var cell = $('#orgpicker-deptcell');
cell.text('');
$('<select name="orgpicker-dept"></select>').appendTo(cell);
sel = $('#orgpicker').find('select[name="orgpicker-dept"]');
} else {
sel.find('option').remove();
}
$('<option value=""></option>').appendTo(sel);
var res = data.results;
for (var i=0;i<res.length;i++) {
$('<option value="' + res[i] + '">' + res[i] + '</option>').appendTo(sel);
}
if (data.select && data.select!='') { sel.val(data.select); }
}

replace this by
var res = data.results;
for (var i=0;i<res.length;i++) {
if(!res[i])
break;
var opt = '<option value="' + res[i] + '">' + res[i] + '</option>';
$(opt).appendTo(sel);
}
if (data.select && data.select!='') { sel.val(data.select); }

Related

How to recursively build a list using a callback and iterating through the children?

I'm unsure on how to build this list (which is a string) and then returning as one complete string.
I've worked past my last issue but I think this one is realy bugging me. buildItem() should iterate through item, and then recursively build a list while getting the totalCost from another callback. I know it works asynchronously...
buildItem(data, function(html){
$('#nestable ol').append(html);
});
Should append the 'final' html string that's created from being appended throughout the file.
function buildItem(item, callback) {
getTotalCost(item, function(totalCost) {
var html = "<li class='dd-item' data-id='" + item.id + "' data-email='" + item.email + "' data-title='" + item.corporateTitle + "' data-name='" + item.firstName + " " + item.lastName + "' id='" + item.id + "'>";
if (item.children && item.children.length > 0) {
html += "<ol class='dd-list'>";
$.each(item.children, function (index, sub) {
buildItem(item, function(subHtml){
html += subHtml;
})
})
html += "</ol>";
}
html += "</li>";
callback(html);
});
}
I know that
buildItem(item, function(subHtml){
html += subHtml;
})
shouldn't work since javascript is asynchronous. I'm just not sure on how to return from a recursive function? If I were to do something like
buildItem(item, function(subHtml){
callback(subHtml);
})
You'll get duplicate values because you'll have the starting value and it's children, but since you're also calling it back you'll get the children outside of the starting value. So it'll look like
1
a
b
c
d
e
a
b
c
d
e
So what's the best way to approach a solution? I was thinking of making another function, hypothetically a buildChild(sub) that returned html, but the same issue with asynchronous is going to come up where the return will be undefined. I've read some of the threads where you can handle asynchronous values with callbacks, but I'm not sure on how to do it with recursion here.
getTotalCost is another callback function that shouldn't mean much, I removed the line by accident but I just need the totalCost from a database.
function getTotalCost(item, callback) {
$.ajax({
dataType: "json",
url: "/retrieveData.do?item=" + item.email,
success: function(data) {
var totalCost = 0;
for (var i = 0; i < data.length; i++) {
totalCost += parseFloat(data[i].cost);
}
callback(totalCost);
}
});
}
You can simplify this with promises and async functions:
async function getTotalCost(item) {
const data = await Promise.resolve($.ajax({
dataType: "json",
url: "/retrieveData.do?item=" + item.email
}));
return data.reduce((acc, next) => acc + next.cost, 0);
}
async function buildItem(item) {
const totalCost = await getTotalCost(item);
let html = `<li class="dd-item" data-id="${item.id}" data-email="${item.email}" data-title="${item.corporateTitle}" data-name="${item.firstName} ${item.lastName}" id="${item.id}">`;
if (item.children && item.children.length > 0) {
html += '<ol class="dd-list">';
for (const childItem of item.children) {
html += await buildItem(childItem);
}
html += "</ol>";
}
html += "</li>";
return html;
}
Unfortunately, async functions aren't supported by all browsers yet, so you'll have to use Babel to transpile your code.
I also added some new ES6 features: arrow functions, const and template literals.
You can mix slow ajax requests with logic and recursion if you execute your code via synchronous executor nsynjs.
Step 1. Write your logic as if it was synchronous, and place it into function:
function process(item) {
function getTotalCost(item) {
var data = jQueryGetJSON(nsynjsCtx, "/retrieveData.do?item=" + item.email).data;
var totalCost = 0;
for (var i = 0; i < data.length; i++) {
totalCost += parseFloat(data[i].cost);
}
return totalCost;
};
function buildItem(item) {
const totalCost = getTotalCost(item);
var html = "<li class='dd-item' data-id='" + item.id + "' data-email='" + item.email + "' data-title='" + item.corporateTitle + "' data-name='" + item.firstName + " " + item.lastName + "' id='" + item.id + "'>";
if (item.children && item.children.length > 0) {
html += '<ol class="dd-list">';
for (var i=0; i<item.children.length; i++)
html += buildItem(item.children[i]);
html += "</ol>";
}
html += "</li>";
return html;
};
return buildItem(item);
};
Step 2: run it via nsynjs:
nsynjs.run(process,{},item,function (itemHTML) {
console.log("all done",itemHTML);
});
Please see more examples here: https://github.com/amaksr/nsynjs/tree/master/examples

javascript/php won't load data for first option in dependent dropdown

I'm having a problem with a script that is part borrowed and part my own, it is javascript and php that populates two dropdown lists, with options in the second being dependent on what the user selects in the first. For some reason, it won't load the options in the second dropdown when the initial option is selected in the first, either on page load or if it is selected manually (if the options were 'a, b, c, d, e...etc', it won't load anything for 'a').
I think it might be something to do with the javascript document ready function, but I'm afraid I know very little about javascript. This is the javascript code:
$(document).ready(function () {
$.getJSON("getOutcome.php", success = function(data)
{
var options = "";
for(var i = 0; i < data.length; i++)
{
options += "<option value ='" + data[i].toLowerCase() + "'>" + data[i] + "</option>";
}
$("#slctOutcome").append(options);
$("#slctProxy").change();
});
$("#slctOutcome").change(function()
{
$.getJSON("getProxies.php?outcome=" + $(this).val(), success = function(data)
{
var options = "";
for(var i = 0; i < data.length; i++)
{
options += "<option value ='" + data[i].toLowerCase() + "'>" + data[i] + "</option>";
}
$("#slctProxy").html("");
$("#slctProxy").append(options);
});
});
});
Change event is only fired, when selection is changed - not when filled ;-)
Try adding $("#slctOutcome").trigger("change"); at pre-last line
Have fun :-)
Try the below. Second function wasn't being called in first function.
$(document).ready(function () {
$.getJSON("getOutcome.php", success = function(data)
{
var options = "";
for(var i = 0; i < data.length; i++)
{
options += "<option value ='" + data[i].toLowerCase() + "'>" + data[i] + "</option>";
}
$("#slctOutcome").append(options);
$("#slctOutcome").change(); //<-Here
});
$("#slctOutcome").change(function()
{
$.getJSON("getProxies.php?outcome=" + $(this).val(), success = function(data)
{
var options = "";
for(var i = 0; i < data.length; i++)
{
options += "<option value ='" + data[i].toLowerCase() + "'>" + data[i] + "</option>";
}
$("#slctProxy").html("");
$("#slctProxy").append(options);
});
});
});

Getting rid of undefined in select options

I have code which reads an uploaded text file and sorts it as options for different <select>. Since i am using for loop and setting it less than file.length. All the options from text file are read correctly but once the text is done, the rest of the options show up as undefined. I wanted to know how to get rid of these undefined? If confusing, please comment below for further explanation.
I've updated your code. Check this Fiddle. It Worked for me for .txt file with content Arif, John, Sonya, SuperGirl, The Flash, Batman, Suparman
i added if statement to for loop, and combined your 3 loops into one
for (var i = 0; i < results.length; i=i+3) {
$('#name').append('<option val="1">' + results[i] + '</option>');
$('#name2').append('<option val="1">' + results[i] + '</option>');
if(typeof results[i+1] !== 'undefined') {
$('#address').append('<option val="1">' + results[i+1] + '</option>');
$('#address2').append('<option val="1">' + results[i+1] + '</option>');
}
if(typeof results[i+2] !== 'undefined') {
$('#size').append('<option val="1">' + results[i+2] + '</option>');
$('#size2').append('<option val="1">' + results[i+2] + '</option>');
}
}
You should do
for (var i = 0; i < results.length; i=i+3) {
or you should get value from file
$('#name').append('<option value="1">' + file[i] + '</option>');

How to fetch the values of a dynamic select list in a global variable?

I'm trying to get the values of my dynamically filled select list in a global variable. This is how I get and fill the select list:
My dropdown.js script:
$(document).ready(function () {
$("#slctTable").change(function()
{
$.getJSON("dropdown_code/get_fields.php?table=" + $(this).val(), success = function(data)
{
var options = "";
for(var i = 0; i < data.length; i++)
{
options += "<option value='" + data[i] + "'>" + data[i] + "</option>";
}
$("#slctField").html("");
$("#slctField").append(options);
$("#slctField").change();
});
});
});
So after this I tryed this code in my main.js scgript to get the values of the select lists:
$('#slctField > option').each(function(){
console.log(this.value); // Use this.value to get the value of the option
});
var options = [];
$('#slctField > option').each(function(){
options.push(this.value);
});
console.log(options);
But when I run my scripts this the result I get back:
But when I copy and paste the code in firebug and run it. I get the result i want.So I think the select lists aren't filled yet when i try to get the values. But I'm stuck on this for a long time and I don't know what to do at the moment.
Because getJSON is asynchronous, to solve your problem you can trigger a custom event when the select is completed (at the end of getJSON success).
In my example I used this slctFieldFilled new event.
This is a different approach. Another possible solution can be based on callbacks: at the end of an asynchronous function execute the callback function, like the getJSON does.
My snippet:
$(function () {
$.getJSON('https://api.github.com/users', success = function (data) {
var options = '';
for (var i = 0; i < data.length; i++) {
options += "<option value='" + data[i].id + "'>" + data[i].id + "</option>";
}
$("#slctTable").append(options);
$("#slctTable").change();
});
$("#slctTable").on('change', function(e) {
var par1 = $(this).val();
$.getJSON("https://api.github.com/users", success = function(data) {
var options = "";
for(var i = 0; i < data.length; i++) {
options += "<option value='" + data[i].id + "'>" + data[i].id + "</option>";
}
$("#slctField").html("");
$("#slctField").append(options);
$("#slctField").change();
//
// Now, the slctField is filled, so trigger your custom event
//
$('#slctField').trigger('slctFieldFilled', options);
});
});
$("#slctField").change(function() {
var par1 = $(slctTable).val();
var par2 = $(slctField).val();
$.getJSON("https://api.github.com/users", success = function(data) {
var options = "";
for(var i = 0; i < data.length; i++) {
options += "<option value='" + data[i].id + "'>" + data[i].id + "</option>";
}
$("#slctAttribute").html("");
$("#slctAttribute").append(options);
$("#slctAttribute").change();
});
});
// listen on custom event...
$('#slctField').on('slctFieldFilled', function(e, optionVariable) {
var options = [];
$(optionVariable).each(function(index, element){
options.push(this.value);
});
$('#log').text(options);
});
});
<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
<select id="slctTable"></select>
<select id="slctField"></select>
<select id="slctAttribute"></select>
<p id="log"></p>
You're very correct! Your GET is asynchronous and will likely complete long after your main.js code has finished executing. You'll want to make sure your modifications to the global variable is tied to your callbacks in some way so its guaranteed to run afterwards.
var options = [];
$("#slctField").change(function()
{
$.getJSON("dropdown_code/get_attributes.php?table=" + $(slctTable).val() ,"field=" + $(slctField).val() , success = function(data)
{
...
//Option 1: Append the values inside your callback.
//Use window.options because you have another local variable options(window.XX calls any global XX)
for(var i = 0; i < data.length; i++)
{
...
window.options.push(data[i]);
}
//Option 2: Basically the same thing as 1, call a function that does the same thing at the end of your callback
populateOptions();
});
});
function populateOptions(){
$('#slctField > option').each(function(){
options.push(this.value);
});
}
There's plenty of other ways to do it as well as long as you guarantee it executes after your GET. If you have any questions, post a comment. Be careful about the scope of options since you have multiple variables named options(or consider different names so that you can't be confused later on!).

Jquery help needed- infinite loop?

i have a problem with this code:
var par = [];
$('a[name]').each(function() {
if (($(this).attr('name')).indexOf("searchword") == -1) {
par.push($(this).attr('name'));
$('.content').empty();
for (var i = 0; i < par.length; i++) {
$(".content").append('<a id="par" href="#' + par[i] + '">' + par[i] + '</a><br />');
}
}
});
It causes ie and firefox to popup the warning window "Stop running this script". But it happens only when there is a very very large amount of data on page. Any ideas how to fix it?
Your code should look like this:
var par = [];
$('a[name]').each(function() {
if (($(this).attr('name')).indexOf("searchword") == -1) {
par.push($(this).attr('name'));
}
});
$('.content').empty();
for (var i = 0; i < par.length; i++) {
$(".content").append('<a id="par" href="#' + par[i] + '">' + par[i] + '</a><br />');
}
There is no reason for the second loop to be inside the first - that will just cause a lot of unneeded work.
You can make this code a bit simpler by removing the par array and the second loop, and just creating the content inside the first loop:
$('.content').empty();
$('a[name]').each(function() {
var name = $(this).attr('name');
if (name.indexOf("searchword") == -1) {
$(".content").append('<a id="par" href="#' + name + '">' + name + '</a><br />');
}
});
Browsers run all javascript (and most page interaction) on a single thread. When you run a long loop like this with no interruptions, the UI is totally frozen. You should try to make your algorithm have to do less, but in case that's not possible you can use this trick where you do a bit of work, then pause and give the browser control of the UI thread for a bit, then do more work.
var $targets = $('a[name]');
var current = 0;
var i = 0;
function doSomeWork() {
if (i == $targets.length) return;
var $t = $targets[i];
if (($t.attr('name')).indexOf("searchword") == -1) {
par.push($t.attr('name'));
$('.content').empty();
for (var i = 0; i < par.length; i++) {
$(".content").append('<a id="par" href="#' + par[i] + '">' + par[i] + '</a><br />');
}
}
i++;
window.setTimeout(arguments.callee, 0);
}
This does one iteration of your loop in a function before yielding. It might be a good idea to do more than just one in a function call, but you can experiment with that. An article on this idea: http://www.julienlecomte.net/blog/2007/10/28/

Categories