Sunday, May 20, 2007
I've Got Blisters on my Fingers!
Blog Entry #76
Today's post begins a look at one of the most interesting scripts at the HTML Goodies site: the guitar chord chart script that spans Script Tips #56, #57, #58, and #59. In brief, the Script Tips #56-59 Script presents a menu of guitar chords and, when the user selects a chord, the corresponding finger positions are displayed on a simulated fretboard to the left of the menu.
The Script Tips #56-59 Script can be located here in HTML Goodies' /legacy/beyond/javascript/stips/ subdirectory (the "Here's the Code" links in Script Tips #56-59 all lead to scriptless pages) and is reproduced in the div below:
<table border="2"> <td bgcolor="ffffff" cellpadding="0" cellspacing="0" align="center"> <form name="guitar"> <script language="javascript"> <!-- var chords = new Object( ); chords["A"] = "1;5;14;15;16"; chords["A7"] = "1;3;5;14;16"; chords["A9"] = "3;4;5;14;25;30"; chords["A13"] = "1;3;20;22;23"; chords["Am"] = "1;5;10;14;15"; chords["Am6"] = "0;1;10;14;15;17"; chords["Am7"] = "30;32;33;34"; chords["Bb"] = "7;20;21;22"; chords["B"] = "13;26;27;28"; chords["B7"] = "4;8;13;15;17"; chords["B9"] = "8;13;15;16"; chords["Bm"] = "13;22;26;27"; chords["Bm6"] = "2;4;9;13;17"; chords["Bm7"] = "2;4;13;15;17"; chords["C"] = "3;5;10;14;19"; chords["C6"] = "5;10;14;15;19"; chords["C7"] = "5;10;14;19;21"; chords["Cmaj7"] = "3;4;5;14;19"; chords["C9"] = "5;14;19;21;22"; chords["Csus4"] = "3;10;19;20"; chords["C7sus4"] = "4;19;20;21"; chords["Cdim7"] = "8;10;12;15"; chords["Cm"] = "19;28;32;33"; chords["Cm7"] = "19;21;28;32"; chords["D"] = "2;15;17;22"; chords["D6"] = "1;2;4;15;17"; chords["D7"] = "2;10;15;17"; chords["Dmaj7"] = "1;2;15;16;17"; chords["D9"] = "26;31;33;34;35"; chords["Dsus4"] = "1;2;15;22;23"; chords["D7sus4"] = "1;2;10;15;23"; chords["Ddim7"] = "2;4;9;11"; chords["Dm"] = "2;11;15;22"; chords["Dm7"] = "1;2;10;11;15"; chords["Eb9"] = "20;25;27;28;29"; chords["E"] = "0;4;5;9;13;14"; chords["E7"] = "0;2;4;5;9;13"; chords["E9"] = "0;2;9;13;17;22"; chords["E13"] = "0;2;5;9;13;16"; chords["Em"] = "0;3;4;5;13;14"; chords["Em6"] = "0;3;5;13;14;16"; chords["Em7"] = "0;3;5;13;14;22"; chords["F"] = "10;11;15;20"; chords["F5"] = "6;19"; chords["F6"] = "1;2;6;10;15"; chords["Fmaj7"] = "1;5;6;10;14;15"; chords["Fm"] = "6;9;10;11;19;20"; chords["Fm7"] = "6;8;9;10"; chords["F#"] = "12;16;17;21;25;26"; chords["F#7"] = "5;12;14;16;21"; chords["F#9"] = "7;9;12;14;16"; chords["F#m"] = "12;15;16;17;25;26"; chords["F#m7"] = "5;15;16;26"; chords["G"] = "2;3;4;13;18;23"; chords["G5"] = "24;37"; chords["G6"] = "14;18;22;27"; chords["G7"] = "2;3;4;11;13;18"; chords["Gmaj7"] = "18;22;26;27"; chords["G9"] = "4;15;18;20"; chords["G6/9"] = "14;15;22;23"; chords["Gsus4"] = "2;3;10;18;23"; chords["G7sus4"] = "3;10;18;20;23"; chords["Gdim7"] = "3;7;14;16;18"; chords["Gm"] = "2;3;7;18;22"; chords["Gm7"] = "18;20;21;22"; for (Countx = 1; Countx < 8; Countx++) { var Count, Countx; for (Count = 1; Count < 7; Count++) { document.write ("<input type='radio' onClick=''>"); } document.write ("<br>"); if (Countx == 1) document.write ("<img src='bb.gif' width='150' height='3' align='absmiddle'>"); else document.write ("<img src='bb.gif' width='150' height='1' align='absmiddle'>"); document.writeln("<br>"); } function showChord( ) { var Item, Ret, Count, Skip; for (Count = 0; Count < 42; Count++) { document.guitar[Count].checked = false; } Item = document.guitar.chord.selectedIndex; if (Item != -1) { Text = document.guitar.chord.options[Item].text; Frets = parser(chords[Text]); for (Count = 1; Count <= Frets[0]; Count++) { document.guitar[parseInt(Frets[Count])].checked = true; } } } function parser(InString) { var Sep = ";", NumSeps = 1, Count, Start, ParseMark, parse; for (Count = 1; Count < InString.length; Count++) { if (InString.charAt(Count) == Sep) NumSeps++; } parse = new Array( ); var Start = 0, Count = 1, ParseMark = 0, LoopCtrl = 1; while (LoopCtrl == 1) { ParseMark = InString.indexOf(Sep,ParseMark); TestMark = ParseMark; if ((TestMark == 0) || (TestMark == -1)) { parse[Count] = InString.substring(Start,InString.length); LoopCtrl = 0; break; } parse[Count] = InString.substring(Start,ParseMark); Start = ParseMark + 1, ParseMark = Start, Count++; } parse[0] = Count; return (parse); } // --> </script> </td> <td align="center" bgcolor="000000"> <select name="chord" size="7" onchange="showChord( );"> <option>A <option>A7 <option>A9 <option>A13 <option>Am <option>Am6 <option>Am7 <option>Bb <option>B <option>B7 <option>B9 <option>Bm <option>Bm6 <option>Bm7 <option>C <option>C6 <option>C7 <option>Cmaj7 <option>C9 <option>Csus4 <option>C7sus4 <option>Cdim7 <option>Cm <option>Cm7 <option>D <option>D6 <option>D7 <option>Dmaj7 <option>D9 <option>Dsus4 <option>D7sus4 <option>Ddim7 <option>Dm <option>Dm7 <option>Eb9 <option>E <option>E7 <option>E9 <option>E13 <option>Em <option>Em6 <option>Em7 <option>F <option>F5 <option>F6 <option>Fmaj7 <option>Fm <option>Fm7 <option>F# <option>F#7 <option>F#9 <option>F#m <option>F#m7 <option>G <option>G5 <option>G6 <option>G7 <option>Gmaj7 <option>G9 <option>G6/9 <option>Gsus4 <option>G7sus4 <option>Gdim7 <option>Gm <option>Gm7 </select> </form> </td> </tr> </table>
Joe's attempted script demo in Script Tips #56-59 does not work for two reasons:
(1) More importantly, the script's [ and ] characters have been unnecessarily and fatally escaped to [ and ], respectively.
(2) Less importantly, the path to the bb.gif image that composes the fretboard's frets is incorrect; the src URL should be /images/bb.gif (and not simply bb.gif).
That leaves it to us to craft our own demo:
The script's chord menu comprises no fewer than sixty-five chords, and should thus prove a helpful learning tool for both beginning and experienced guitarists.
(A13? D7sus4? As I recall, it was knowing these sorts of chords that got Glen Matlock kicked out of the Sex Pistols. But I digress.)
Before we put the Script Tips #56-59 Script under the microscope, I should mention that Joe himself is a guitarist, and the Hobbies tab on his homepage at the Web site of Southeastern Louisiana University, where he teaches, sports
(a) links to photos of his guitars and
(b) a collection of mp3s of his performances thereupon for your listening pleasure.
Table structure
The Script Tips #56-59 Script's display is housed in a one-row, two-cell table. Before we address the contents of the individual table cells, a few comments on table structure are in order.
Sharp-eyed observers will note that the tr element's end-tag is present but its start-tag is missing; however, the HTML 4.01 Specification states that the tr element's start-tag is required and its end-tag is optional. (According to the HTML 4.01 Index of Elements, there are no HTML elements whose start-tags are optional and whose end-tags are required.) Of course, if you would like the script to be XHTML-compliant, then the <tr> and </tr> tags should both be present.
The first table cell element has been given cellspacing="0" and cellpadding="0" attributes; however, cellspacing and cellpadding are attributes of the table element, and not of the td element. Moreover, both cellspacing and cellpadding have default specifications of #IMPLIED, which in practice means, "The browser has its own preferred way of dealing with these attributes, so you don't have to supply them if you don't want to." (#IMPLIED is an SGML keyword that is murkily defined by the W3C here; a better #IMPLIED description appears in the "Attributes" section in Chapter 2 of the Text Encoding Initiative's "A Gentle Introduction to SGML".) When I removed the cellspacing="0" and cellpadding="0" attributes from the first cell's start-tag and put them in the <table border="2"> tag, and then reran the script, I observed no difference vis-à-vis the script's display.
Both table cell elements are equipped with an align attribute and a bgcolor attribute; these attributes can be replaced by the following style block:
td { text-align: center; }
td#cell0 { background-color: white; } /* for a <td id="cell0"> cell */
td#cell1 { background-color: black; } /* for a <td id="cell1"> cell */
FYI: the HTML 4.01 Index of Attributes shows that the align attribute is not deprecated for table element descendants but is deprecated for all other elements that can take an align attribute, whereas the bgcolor attribute is deprecated for all elements that can take a bgcolor attribute.
(While we're on the topic of style: fretboards are typically not white, and you might prefer a bisque (#ffe4c4) or blanchedalmond (#ffebcd) background color for the first cell.)
One more point before moving on: the script's td elements and guitar form element are improperly nested:
<td id="cell0">
<form name="guitar">
<!-- First cell content -->
</td>
<td id="cell1">
<!-- Second cell content -->
</form>
</td>
To rectify this violation of well-formedness, we can and should make the guitar form the parent of the table element:
<form name="guitar">
<table border="2">
<!-- Table body -->
</table>
</form>
Alternatively and less preferably, we could end the guitar form in the first table cell and start a second form in the second table cell (this would require slight modification of two statements in the script's script element):
<td id="cell0">
<form name="guitar">
<!-- First form content -->
</form>
</td>
<td id="cell1">
<form name="guitar2">
<!-- Second form content -->
</form>
</td>
The second cell
The first table cell is far more complex than the second table cell, so let's start with the second cell. The second cell comprises a selection list of sixty-five options, one for each guitar chord. The select element has a size="7" attribute and thus displays seven consecutive options at a time; it doesn't have a multiple attribute, however, and consequently the user can only choose one option at a time.
The option elements all lack </option> tags, which are not required by HTML but, per the discussion above, must be added for XHTML compliance.
We'll take up the first table cell in the next entry.
reptile7
Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)