Strange for-loop behavior in Javascript - javascript

I have next problem: arr has only two elements. Next loop tries to execute his body 3 times:
var selectHTML = "";
for (var i = 0; i< arr.length; i++) {
selectHTML += '<option value="' + arr[i].id + '">' + arr[i].name + '</option>';
}
Next loop tries to execute his body only 2 times as I expect:
var selectHTML = "";
for (var i = 0; i< arr.length; i++) {
alert(i);
selectHTML += '<option value="' + arr[i].id + '">' + arr[i].name + '</option>';
}
Why (tested in Firefox 14.0.1)?
Updated: sorry for the semicolon after counter increment, it's a typo. But the code still doesn't work event without it.
Updated: Ok, this code was simplified. Whole code itself:
var selectHTML = "";
timeSheet.steps = [ { name:"Leave as is", id:-1}, { name:"Approved", id:2} ];
for (var counter = 0; counter < timeSheet.steps.length; counter++) {
selectHTML += '<option value="' + timeSheet.steps[counter].id + '">' + timeSheet.steps[counter].name + '</option>';
}
In Firebug I can see that timeSheet.steps.length equals 2. By the way, instead of placing "alert(i)" I've added a comment and body executes 2 times. Magic...

You have an extra semicolon in the loop
for (var i = 0; i< arr.length; i++;) {
Try it without:
for (var i = 0; i< arr.length; i++) {
This works fine for me (Tested in Firefox 14.0.1):
var arr = [{id:1, name: 'test2'}, {id:2, name: 'test2'}];
var selectHTML = "";
for (var i = 0; i< arr.length; i++) {
selectHTML += '<option value="' + arr[i].id + '">' + arr[i].name + '</option>';
}
console.log(selectHTML);
var selectHTML = "";
for (var i = 0; i< arr.length; i++) {
selectHTML += '<option value="' + arr[i].id + '">' + arr[i].name + '</option>';
}
console.log(selectHTML);
Returns
<option value="1">test2</option><option value="2">test2</option>
<option value="1">test2</option><option value="2">test2</option>

Please elaborate what you meant by the body running three times. If your array contains only 2 elements, when i == 2, arr[i] is undefined and hence accessing id and name will throw an error.
If this is what is happening, then at some point in the looping, the length of the array is being modified with/without an element being added.
Issues that fixed by adding an alert are usually timing issues. See this section on Alerts
Alert boxes (and the related confirm and prompt-boxes) have some
strange properties.
They are synchronous in the sense that the script that initiates the
dialog is suspended until the dialog is closed. The script waits for
the alert()-function to return before it continues.
The tricky part is that some browsers allow events to be dispatched
while the dialog is visible and waiting for some user action. This
means that while one script is suspended, waiting for the alert
function to return, another function might be executed as part of a
different event dispatch.
User interface events like mouseup and click will not fire during the
alert, as the alert is modal and captures all user input, but
non-user-initiated events like page load, timeout handlers, and
asynchronous XMLHttpRequest return handlers, might fire.
You have not shown code that modifies the arr variable. Chances are you have a ajax call some where that is modifying the arr. In the first code sample there is no alert, so maybe the entire for loop gets over before the ajax success handler fires. In the second sample, the alert prevents the for loop from executing till you discard the alert. In the time it took for you to discard it, the ajax success handler must have fired.
Please share all relevant code that modifies the arr variable.

Related

Can bind be useful here? Duplication of code

I am new to javascript and thats my pen: pen
so i have faced such problem as duplication of code. how can I avoid it? I have tried this new fuction instead of old ones but its not working:
//populatePosition($("#positionElement"));
populate1($("#positionElement"), positionList, 'position');
function populate1(dd, list, elem) {
var temp="";
for (var i = 0; i < list.length; i++) {
temp += "<option value='" + list[i]["id"] + "'>" + list[i][''+elem] + "</option>\n";
//console.log(temp);
}
dd.append(temp);
}
I guess bind can be used here. is it possible?

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!).

Calling multiple functions to generate textboxes on same page

I am calling two separate functions to generate dynamic textboxes one of the function works fine whereas other doesn't work though the code for generating textboxes is same except the variables names and label names. Could anyone please let me know what I am doing wrong and how can i figure out this ?
this is the function which is not working.
var C = 3;
var matrixArray = ["question", "mrank"];
$("#addMatrix").click(function () {
for(var j = 0; j < matrixArray.length; j++){
createMatrixInput(MatrixArray[j]);
}
C++;
});
function createMatrixInput(l){
var tb_Div = $('#TextBoxes');
var mstr = '<div class="control-group">';
mstr += '<label class="control-label">' + l + " " + C + '</label>';
mstr += '<div class="controls">';
mstr += '<input type="text" id="' + l + '_' + C + '" name="'+ l +'_' + C + '" />';
mstr += '</div>';
mstr += '</div>';
tb_Div.append(mstr);
};
this is my jsfiddle with complete code.
http://jsfiddle.net/qqqyC/2/
There are 2 problems. The button id is addmatrix and the array is matrixArray, not MatrixArray. The method should look like:
$("#addmatrix").click(function () {
for(var j = 0; j < matrixArray.length; j++){
createMatrixInput(matrixArray[j]);
C++;
}
});
I've spotted a error in your JSFiddle see the id of your button "matrix button" it is addmatrix and you are binding the onClick event to addMatrix and javascript event binding via ID is case sensitive, so the event will not be bind.
Maybe this will solve your whole problem, because it was preventing to execute the click event.

javascript code does not execute after a loop

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); }

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