reptile7's JavaScript blog
Thursday, June 28, 2007
Honey, I Shrunk the Script
Blog Entry #80

Today's post will conclude our guided tour of the Script Tips #56-59 Script. Having slogged through the deconstruction of the last few entries, I have a couple of bombshells for you:
(1) The custom chords object (see Blog Entry #78) is unnecessary - it is easily folded into the chord selection list of the second td element.
(2) The parser( ) function (see the previous post) is also unnecessary, and can be replaced with a single line of code.

Ciao, chords

When I first looked over the chords object and the chord menu, my intuition told me that these code blocks were redundant, and it subsequently occurred to me that the chords values could alternatively be assigned to value attributes added to the corresponding chord option elements:

<select name="chord" size="7" onchange="showChord( );">
<option value="1;5;14;15;16">A</option>
<option value="1;3;5;14;16">A7</option>
<option value="3;4;5;14;25;30">A9</option> <!-- etc. -->

Then, in the showChord( ) function, let's replace the parser( ) function call and the preceding Text variable statement with:

Value = document.guitar.chord.options[Item].value;
Frets = parser(Value);

Upon making these changes, the entire chords array can be removed.

Pack it in, parser( )

Given the format of the guitar chord property values - a series of numbers delimited with semicolons - we can substitute for the parser( ) function the following command:

Frets = document.guitar.chord.options[Item].value.split(";");

As its name implies, the split( ) method of the String object splits a string to give an array of substrings; the split(x) parameter x sets the original string's break-points but is not part of the resulting array.

At HTML Goodies, the "Arrays" section of the "JavaScript Basics Part 5" tutorial provides a split( ) method demo and points out that the split( ) method can be considered the inverse of the join( ) method of the Array object, which we utilized in the "Method arrays" section of Blog Entry #43.

Lagniappe: don't play that string

If you're a guitarist, you are familiar with chord diagrams with X's above unplayed strings:

Dm chord showing headstock X's for 5th/6th strings

My approach to modifying the Script Tips #56-59 Script to write headstock X's for chords that need them is outlined below.

The chord option elements

For the D minor chord, the 5th (A) and 6th (E) strings are unplayed*, so we want to write X's in place of the zeroth and first radio buttons of the guitar form. In the chord menu, code the Dm option as:

<option value="0X;1X;2;11;15;22">Dm</option>

(*If you play the D minor chord with an open 5th string, code the option as:
<option value="0X;1;2;11;15;22">Dm</option>
- the code to follow works with either option value.)

The radio button grid

Next, put each radio button element in a span element:

var spanIndex = 0, radioIndex = 0, inputIndex = 0;
for (Countx = 1; Countx < 8; Countx++)
for (Count = 1; Count < 7; Count++)
document.write("<span id='span" + spanIndex + "'>");
document.writeln("<input type='radio' name='r" + radioIndex + "' id='input" + inputIndex + "' onclick='this.checked=false;' />");
spanIndex++; radioIndex++; inputIndex++;
document.write("<br />");
if (Countx == 1) document.write("<hr class='nut' />");
else document.write("<hr class='fret' />"); }

The span and input elements are given 'ordinalized' id values (span0, span1, span2, ... input0, input1, input2, etc.) that can be exploited by the checking and unchecking loops, bringing us to...

The checking loop

for (Count = 0; Count < Frets.length; Count++)
if (/X$/.test(Frets[Count])) {
var dontplay = parseInt(Frets[Count],10);
document.getElementById("span" + dontplay).innerHTML = "X";
else document.getElementById("input" + Frets[Count]).checked = true; }

A six-iteration loop generates the display shown by the above image.

(1-2) For the first two iterations:

The 0X and 1X Frets values match the /X$/ regular expression and thus the if condition returns true.

The 0 and 1 characters of 0X and 1X are respectively extracted by the top-level parseInt( ) function and appended to "span" for a document.getElementById( ) command that will assign X to the value of the innerHTML property of the span0 and span1 elements, which would normally hold the 0th and 1st radio buttons in the first table cell.

For the parseInt( ) function to see the 0 of 0X as a decimal number, it is necessary to include a second parseInt( ) parameter specifying a radix of 10 for 0X. Without the 10, the parseInt( ) function perceives 0X as the beginning of a hexadecimal number; as a result,

var dontplay = parseInt("0X");
// is equivalent to
var dontplay = parseInt("");

In this case, the dontplay variable returns the value NaN (Not-a-Number). As you might expect, the browser promptly throws a runtime error when NaN is plugged into the document.getElementById( ) command.

(3-6) For the other four iterations:

The if condition returns false and the else statement kicks in. The 2, 11, 15, and 22 string values are respectively appended to "input" for a document.getElementById( ) command that will assign true to the value of the checked property of the input2, input11, input15, and input22 radio button elements.

The unchecking loop

for (Count = 0; Count < 42; Count++)
if (document.getElementById("span" + Count).innerHTML == "X")
document.getElementById("span" + Count).innerHTML =
"<input type='radio' name='r" + Count + "' id='input" + Count + "' onclick='this.checked=false;' />";

else document.getElementById("input" + Count).checked = false; }

I trust you can take it from here. For the loop's first two iterations, the if condition returns true and the X's in the span0 and span1 elements are converted to radio buttons. The loop's other forty iterations 'erase' (uncheck as necessary) the input2-input41 set of radio buttons.

(Regarding the checking and unchecking loops above, I found that document.getElementById( ) commands allowed a much more straightforward manipulation of the fretboard than did the original document.guitar.elements[ ] commands, which were accordingly 'put out to pasture'.)

And now for something completely different: in the next post, we'll take up the Script Tips #60-64 Script, which sets and reads a JavaScript cookie.


Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)