Don't know how to output this array

I am using my PHP skills to understand how JS works and have been able to figure out a lot. However, I’m stumped by how to output the results to a form’s text field where an array is involved.

I’ll give you the full code (which has not been finalized yet), but in a nutshell, I’m applying calculations to an array of integers. The user chooses the array he wants to calculate, and the arrays all have a different amount of integers in them. After each calculation I round up and down the numbers and calculate them again. After that, I output the results of each number to a text field. That’s where I encounter the difficulty. Here’s the code. I separated out the troubling line and put ?? with it.

Note that this has not been tested yet. I have no idea whether this first draft will even work. :shifty:


var ans = document.getElementById('ans'); // this is the result that comes from a prior calculation. It's the basis of the new calculations.
var ratio = form.elements.ans.value; // the result field that returned the user's spur and pinion input calculation
var choiceSpurs;
<!-- evaluate the spur gear list choice from drop-down menu, and establish the array of spurs to calculate -->
switch (choiceSpurs) 
    {
    case (choiceSpurs=='sc10_4x4'):
        choiceSpurs = "spurs['58', '60', '62', '93']";
        break;
    case (choiceSpurs=='sc10'):
        choiceSpurs = "spurs['75', '78', '81', '84', '87']";
        break;
    case (choiceSpurs=='sc8'):
        choiceSpurs = "spurs['50', '52', '54']";
        break;
    case (choiceSpurs=='b44'):
        choiceSpurs = "spurs['72', '75', '78', '81', '84']";
        break;
    case (choiceSpurs=='b4t4'):
        choiceSpurs = "spurs['72', '75', '78', '81', '84', '87']";
        break;
    case (choiceSpurs=='gt2'):
        choiceSpurs = "spurs['54', '55', '56']";
        break;
	default: "spurs['72', '75', '78', '81', '84', '87'];
    };

var spurArrayLength = choiceSpurs.length; <!-- get the number of items in the array -->

function alternateRatios () {
var L = spurArrayLength - 1; // get the count of the array; subtract 1 to assume length starts at zero like array does
var i = 0; <!-- start the increment count at zero because it'll also be used to point to the object in the array, which starts counting at zero -->
while (i <= L) { <!-- while the count is still within the range of objects in the choiceSpurs array... -->
var pinion . i = spurs[i] / ratio; <!-- ... divide the array's spur tooth count by the ratio -->
i++; // up the increment and work on the next object in the array
}

<!-- round up and round down each of the results to get the pinion tooth count -->
var i = 0; 
while (i <= L) { 
var pinionUP . i = Math.ceil(pinion . i);
var pinionDOWN . i = Math.floor(pinion . i);
i++; <!-- up the increment -->
}

<!-- divide new spur/pinion combinations to get new ratios -->
var i = 0; // start the increment count
while (i <= L) { 
var ratioUP . i = spurs[i] / pinionUP . i;
var ratioDOWN . i = spurs[i] / pinionDOWN . i;
i++; 
}

<!-- display results to user -->
var i = 0; 
var original = "Original ratio: " . ratio;
var try = " Try: ";

while (i <= L) { 
display . i = spurs[i] . i . "/" . pinionUP . i . " = " . ratioUP . i. ", " . spurs[i] . "/" . pinionDOWN . i . " = " . ratioDOWN . i. ", ";




form.elements.moreRatios.value = ??



i++;
}
} <!-- end of function -->

var buttonNewRatios = document.getElementById('buttonNewsRatios');
buttonNewRatios.onclick = function () {
var form = document.getElementByID('form2');
alternateRatios(form);
};

Let’s start with some basics.

Run your javscript code through the online lint to find the most obvious problems.

Here’s the old code, with suggested fixes from online lint highlighted highlighted.


var ans = document.getElementById('ans'); // this is the result that comes from a prior calculation. It's the basis of the new calculations.
var ratio = form.elements.ans.value; // the result field that returned the user's spur and pinion input calculation
var choiceSpurs;
<!-- evaluate the spur gear list choice from drop-down menu, and establish the array of spurs to calculate -->
switch (choiceSpurs) 
    {
    case (choiceSpurs=='sc10_4x4'):
        choiceSpurs = "spurs['58', '60', '62', '93']";
        break;
    case (choiceSpurs=='sc10'):
        choiceSpurs = "spurs['75', '78', '81', '84', '87']";
        break;
    case (choiceSpurs=='sc8'):
        choiceSpurs = "spurs['50', '52', '54']";
        break;
    case (choiceSpurs=='b44'):
        choiceSpurs = "spurs['72', '75', '78', '81', '84']";
        break;
    case (choiceSpurs=='b4t4'):
        choiceSpurs = "spurs['72', '75', '78', '81', '84', '87']";
        break;
    case (choiceSpurs=='gt2'):
        choiceSpurs = "spurs['54', '55', '56']";
        break;
	default: "spurs['72', '75', '78', '81', '84', '87'];
        [color="red"][B]SyntaxError: unterminated string literal[/B][/color]
    };
    [color="red"][B]lint warning: missing break statement for last case in switch[/B][/color]
    [color="red"][B]lint warning: empty statement or extra semicolon[/B][/color]

var spurArrayLength = choiceSpurs.length; <!-- get the number of items in the array -->

function alternateRatios () {
var L = spurArrayLength - 1; // get the count of the array; subtract 1 to assume length starts at zero like array does
var i = 0; <!-- start the increment count at zero because it'll also be used to point to the object in the array, which starts counting at zero -->
while (i <= L) { <!-- while the count is still within the range of objects in the choiceSpurs array... -->
var pinion . i = spurs[i] / ratio; <!-- ... divide the array's spur tooth count by the ratio -->
[color="red"][B]SyntaxError: missing ; before statement[/B][/color]

i++; // up the increment and work on the next object in the array
}

<!-- round up and round down each of the results to get the pinion tooth count -->
var i = 0; 
[color="red"][B]warning: redeclaration of var i[/B][/color]
while (i <= L) { 
var pinionUP . i = Math.ceil(pinion . i);
[color="red"][B]SyntaxError: missing ; before statement[/B][/color]
[/B][/color]
var pinionDOWN . i = Math.floor(pinion . i);
[color="red"][B]SyntaxError: missing ; before statement[/B][/color]
[/B][/color]
i++; <!-- up the increment -->
}

<!-- divide new spur/pinion combinations to get new ratios -->
var i = 0; // start the increment count
[color="red"][B]warning: redeclaration of var i[/B][/color]
while (i <= L) { 
var ratioUP . i = spurs[i] / pinionUP . i;
[color="red"][B]SyntaxError: missing ; before statement[/B][/color]
var ratioDOWN . i = spurs[i] / pinionDOWN . i;
[color="red"][B]SyntaxError: missing ; before statement[/B][/color]
i++; 
}

<!-- display results to user -->
var i = 0; 
[color="red"][B]warning: redeclaration of var i[/B][/color]
var original = "Original ratio: " . ratio;
[color="red"][B]SyntaxError: missing ; before statement[/B][/color]
var try = " Try: ";
[color="red"][B]SyntaxError: missing variable name[/B][/color]
while (i <= L) { 
display . i = spurs[i] . i . "/" . pinionUP . i . " = " . ratioUP . i. ", " . spurs[i] . "/" . pinionDOWN . i . " = " . ratioDOWN . i. ", ";
[color="red"][B]SyntaxError: missing name after . operator[/B][/color]




form.elements.moreRatios.value = ??



i++;
}
} <!-- end of function -->

var buttonNewRatios = document.getElementById('buttonNewsRatios');
buttonNewRatios.onclick = function () {
var form = document.getElementByID('form2');
alternateRatios(form);
};

Some of those errors don’t make sense, due to the checker not knowing what you’re tying to attempt there.

The “missing ; before statement” and “missing name after . operator” errors are because the fullstop does not perform concatenation in javascript. The plus symbol performs that duty instead, or because you should be using an array index instead.

You cannot access array items using the PHP concatenator.
pinion . i

You need to ensure that the array exists first, and use the array index notation instead:
var pinion = ;

pinion[i]

The “redeclaration of var i” warnings don’t just mean that you remove the var declaration. When you declare them in the middle of a function JavaScript automatically hoists them to the top of the function anyway, so put them at the top to help remove any confusion that you might have.

Javascript also has something called a for loop, which helps you to easily loop over arrays. So instead of this:

var L = spurArrayLength - 1; // get the count of the array; subtract 1 to assume length starts at zero like array does
var i = 0; <!-- start the increment count at zero because it'll also be used to point to the object in the array, which starts counting at zero -->
while (i <= L) { <!-- while the count is still within the range of objects in the choiceSpurs array... -->
var pinion . i = spurs[i] / ratio; <!-- ... divide the array's spur tooth count by the ratio -->
i++; // up the increment and work on the next object in the array
}

You would instead do this:


var i;
...
for (i = 0; i < spurArrayLength, i += 1) {
    pinion[i] = spurs[i] / ratio;
}

The “missing variable name” error is because “try” is a reserved name. It’s used for try/catch blocks of code.

Do not use HTML comments within your script. They will break as soon as you put your code in an external script file. Use single-line or multi-line comments instead with // and //

And try to avoid restating in your comments what the code is doing. Instead, limit your comments to explain why.

Fixing those problems though don’t mean that your code is going to work. It does help to weed out the most obvious problems though.

So, you can also use jslint to find other problems (there are a lot, so I won’t list them here) and finally you can turn on “The Good Parts” at jslint so that you can make your code all clean and shiny.

Instead of looping many times over the same array, I combined them all in to just one loop, which helped to remove the need for vast amounts of array storage.
I also adjusted a few variable names so that they more closely follow standard javascript code conventions.
Here’s what the code looks like after all of that:


function gearSpurs(ratio) {
    var spurs = [];

    // evaluate the spur gear choice, and establish the array of spurs to calculate
    switch (ratio) {
    case 'sc10_4x4':
        spurs = ['58', '60', '62', '93'];
        break;
    case 'sc10':
        spurs = ['75', '78', '81', '84', '87'];
        break;
    case 'sc8':
        spurs = ['50', '52', '54'];
        break;
    case 'b44':
        spurs = ['72', '75', '78', '81', '84'];
        break;
    case 'b4t4':
        spurs = ['72', '75', '78', '81', '84', '87'];
        break;
    case 'gt2':
        spurs = ['54', '55', '56'];
        break;
    default:
        spurs = ['72', '75', '78', '81', '84', '87'];
        break;
    }
    return spurs;
}

function alternateRatios(spurs, ratio) {
    var pinion = 0,
        pinionUp = 0,
        pinionDown = 0,
        ratioUp = 0,
        ratioDown = 0,
        i,
        spursLen = spurs.length,
        alternates = '';

    for (i = 0; i < spursLen; i += 1) {
        pinion = spurs[i] / ratio;

        // get the pinion tooth count
        pinionUp = Math.ceil(pinion);
        pinionDown = Math.floor(pinion);

        // get new ratios
        ratioUp = spurs[i] / pinionUp;
        ratioDown = spurs[i] / pinionDown;

        alternates += spurs[i] + i + '/' + pinionUp + ' = ' + ratioUp + ', ' + spurs[i] + '/' + pinionDown + ' = ' + ratioDown + ', ' + "\
';
    }
    return alternates;
}

var buttonNewRatios = document.getElementById('buttonNewsRatios');
buttonNewRatios.onclick = function () {
    var form = document.getElementById('form2'),
        ratio = form.elements.ans.value, // the result field that returned the user's spur and pinion input calculation
        spurs = gearSpurs(ratio),
        alternates = alternateRatios(spurs, ratio);

    form.elements.moreRatios.value = 'Original ratio: ' + ratio + "\
" + 
        'Try: ' + alternates;
};

I don’t know how your form is structured so there’s no way to test of the above code works or not, but it’s a lot closer to working now than it was before.

Wow! I come home from a long weekend to find a fantastic present on my doorstep! You’re right, it doesn’t work as written – I’ll figure out why. But it is 1:10 the size of the alternate I figured on using (a series of if statements with the entire code written over and over with the values filled in) before I saw your version. Thank you so much for this gift!

I’m still trying to find out why no values display. All the buttons and text fields are correctly linked in the code. The code checks out perfectly in JSHint. Not yet finished.

The code was created with no testing involved, due to having no access to anything that can work in the web browser.

Do you have a test page of a previously working version that you can link to, or provide here? From that working older code progress can then be made.

Unfortunately, my working code was replaced later by unworking code when I tried to “improve” on it. However, here is the HTML and Javascript that goes right above the code you gave me:

	<div class="pageitem"><div class="textbox">

<table border="0" cellpadding="5" cellspacing="0" width="100%">
<tr>
<td class="alignright">
<form id="formGearRatio">Spur Gear <input class="display3" type="number" size="14" value="" name="a"> 
</td></tr>
<tr>
<td class="alignright">
Pinion Gear <input class="display3" type="number" size="14" value="" name="b"> 
</td></tr>
<tr>
<td class="alignright">
<input id="divide" class="buttPress2" type="button" value="Ratio =">  
<input class="display2" size="4" type="text" value="" name="ans">:1  
</td></tr></form></table>
</center></p>
	</div></div> 


	<div class="pageitem"><div class="textbox">
<p>Choose the range of spurs for alternate gearing approximating your ratio.<br>
<center>
<table border="0" cellpadding="5" cellspacing="0" width="100%">
<tr>
<td class="alignright">
<form id="form2">
<select class="displayLength" name="ratio" size="6">
<option value=sc10_4x4>SC10 4x4: 58, 60, 62, 93</option> 
<option value=sc10>SC10: 75, 78, 81, 84, 87</option> 
<option value=sc8>SC8: 50, 52, 54</option> 
<option value=b44>B44: 72, 75, 78, 81, 84</option> 
<option value=b4t4>B4, T4: 72, 75, 78, 81, 84, 87</option> 
<option value=gt2>GT2: 54, 55, 56</option> 
</select>
</td></tr>
<tr>
<td class="alignright">
<input id="buttonNewRatios" class="buttPress2" type="button" value="Alternates"><br><br>
<input class="displayLength" size="7" type="text" value="" name="moreRatios">  
</td></tr></form></table>

	</div></div> 

</div> <!-- contents -->


<script language="JavaScript">

function sanitizeNumber() {
    // restrict to digits only
    this.value = /\\d+/.exec(this.value);
    // size check
    if (this.value < 1) {
        this.value = "not valid";
	}
    if (this.value > 200) {
        this.value = "not valid";
	}
}
 
function a_div_b(form) {
var a = form.elements.a.value,
    b = form.elements.b.value,
    c = a / b;
    form.elements.ans.value = c;
}
 
var form = document.getElementById('formGearRatio');
form.elements.a.onchange = sanitizeNumber;
form.elements.b.onchange = sanitizeNumber;

divide.onclick = function () {
    a_div_b(form);
}

The first error that I’m getting is “Cannot set property ‘onclick’ of null” which is due to the divide.onclick part.

Use this instead:


document.getElementById('divide').onclick = ...

The other error comes from this original code:


var buttonNewRatios = document.getElementById('buttonNewsRatios');

There is no ‘buttonNewsRatios’ identifier. There is instead a ‘buttonNewRatios’ identifier.

So the following will work with that:


var buttonNewRatios = document.getElementById('buttonNewRatios');

Also, it wasn’t known that there are multiple forms, so the “ans” field needs to come from a different form:


var ratioForm = document.getElementById('formGearRatio'),
    form = document.getElementById('form2'),
    ratio = ratioForm.elements.ans.value, // the result field that returned the user's spur and pinion input calculation
    ...

Finally, the alternates can be quite a long list, so I presumed that a textarea was used to display the info:


<textarea name="moreRatios" cols="65" rows="5"></textarea>

With which you’ll need to put the
parts in double quotes instead of single quotes:


alternates += ... + ', ' + "\
";
...
form.elements.moreRatios.value = 'Original ratio: ' + ratio + "\
" + ...

Putting all the JS through JSHint, I’m told of a missing semi-colon on line 30. You pointed this out as a problem early on (on a different line), but I had no clue as to how JS uses semi-colons, so I could not see it. Not the same as PHP!

That missing semicolon is from this set of code:


divide.onclick = function () {
    a_div_b(form);
}

The end of the assignment statement is where the semicolon is put.


divide.onclick = function () {
    a_div_b(form);
};

The javascript interpretor will do that automatically anyway thanks to automatic semicolon insertion, but it’s best to provide them where they’re required so that automatic mistakes don’t have an opportunity to occur.

OK, the JSHint is flawless! However, still no readout. I’ll double-checked the JS:

function sanitizeNumber() {
    // restrict to digits only
    this.value = /\\d+/.exec(this.value);
    // size check
    if (this.value < 1) {
        this.value = "not valid";
	}
    if (this.value > 200) {
        this.value = "not valid";
	}
}
 
function a_div_b(form) {
var a = form.elements.a.value,
    b = form.elements.b.value,
    c = a / b;
    form.elements.ans.value = c;
}

var form = document.getElementById('formGearRatio');
form.elements.a.onchange = sanitizeNumber;
form.elements.b.onchange = sanitizeNumber;

document.getElementById('divide').onclick = function () {
    a_div_b(form);
};
    
    function gearSpurs(ratio) {
        var spurs = [];
        
        // evaluate the spur gear choice, and establish the array of spurs to calculate
        switch (ratio) {
            case 'sc10_4x4':
            spurs = ['58', '60', '62', '93'];
            break;
            case 'sc10':
            spurs = ['75', '78', '81', '84', '87'];
            break;
            case 'sc8':
            spurs = ['50', '52', '54'];
            break;
            case 'b44':
            spurs = ['72', '75', '78', '81', '84'];
            break;
            case 'b4t4':
            spurs = ['72', '75', '78', '81', '84', '87'];
            break;
            case 'gt2':
            spurs = ['54', '55', '56'];
            break;
            default:
            spurs = ['72', '75', '78', '81', '84', '87'];
            break;
        }
        return spurs;
    }
    
    function alternateRatios(spurs, ratio) {
        var pinion = 0,
        pinionUp = 0,
        pinionDown = 0,
        ratioUp = 0,
        ratioDown = 0,
        i,
        spursLen = spurs.length,
        alternates = '';
        
        for (i = 0; i < spursLen; i += 1) {
            pinion = spurs[i] / ratio;
            
            // get the pinion tooth count
            pinionUp = Math.ceil(pinion);
            pinionDown = Math.floor(pinion);
            
            // get new ratios
            ratioUp = spurs[i] / pinionUp;
            ratioDown = spurs[i] / pinionDown;
            
            alternates += spurs[i] + i + '/' + pinionUp + ' = ' + ratioUp + ', ' + spurs[i] + '/' + pinionDown + ' = ' + ratioDown + ", \
";
        }
        return alternates;
    }
    
    var buttonNewRatios = document.getElementById('buttonNewRatios');
    buttonNewRatios.onclick = function () {
        var ratioForm = document.getElementByID('formGearRatio'),
        form = document.getElementByID('form2'),
        ratio = ratioForm.elements.ans.value, // the result field that returned the user's spur and pinion input calculation
        spurs = gearSpurs(ratio),
        alternates = alternateRatios(spurs, ratio);
        
        form.elements.moreRatios.value = 'Original ratio: ' + ratio + "\
" + 
        'Try: ' + alternates;
    };

Time for me to knock off work!

getElementByID needs to be getElementById

I see you like jshint. You may want to also look at jslint with “the good parts” turned on if you want to get really nitty gritty with the code.

The code works for me in Google Chrome when I don’t enter the gears and just type in my own ratio number.

Let’s see how it goes in Firefox. That seems to be okay too, although Firebug does provide this warning:

[indent]Element referenced by ID/NAME in the global scope. Use W3C standard document.getElementById() instead.
Line 144[/indent]

That divide.onclick should instead use getElementById

How about in Internet Explorer? Ahh, that errors saying that divide is not defined.
Now you know of what should be looked in to.

I meant JSHint, not JSLint (you had warned me about it!) I made the ID / Id change and still nothing is showing in the text field.

JSLint tells me, with the good parts on, that ratioForm is unused, and document is not defined. The rest is nit-picking about indenting code.

Is that an input field or a textarea field that you are using?
The results can be quite long, so a text area seems to be the better choice there.

I do have a working demo of it here, but I’d like to see what your current code looks like so that you can learn from it. Do you have a test page that you can link to?

We seem to be calling the same form two different vars:

var form = document.getElementById(‘formGearRatio’);

and:
var ratioForm = document.getElementByID(‘formGearRatio’),

I am using a textarea, sorry for the mixup in terms. I’m creating this in Xcode on the Mac for the iPhone, so it’s not online. I’m glad you have a working version! I thought I had carefully implemented your changes. I’ll double-check.

Those are used in different places, so they won’t affect each other.

Then provide some way for us to see what you are doing. You can attach it to your post as a file, if you must.

Here is the code. It’s actually an HTML file, but the upload said it was invalid. So I’m uploading as a zip: 0511.zip.

You still have a getElementByID in there, which needs to instead be getElementById