An Advanced Sortable Table


This is a more advanced table sorting app.

Here is the basic interface:

Here is the app with a 4X4 table created:

with data added:

and sorted by Last Name, First Name and Age:
The first row is a header and is not sorted

The app consists of a html and two js files.
Here is the html:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>Sort</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }
table{position: relative; left: 100px; empty-cells: show; border-collapse: collapse}
td{position: relative; height: 20px; padding: 2px 10px;}
</style>
</head>
<body onload = ‘ini();’>
<table id= “tb1″ contenteditable=”true”>
<tr><td id=”norows” onclick=’document.getElementById(“norows”).innerHTML = “”;’ >Number Rows</td><td id=”nocols” onclick=’document.getElementById(“nocols”).innerHTML = “”;’>Number Columns</td><td><input type=”button” id=”set” name=”set” value=”Create Table” onclick=’setGrid();’ /></td><td><select id=”s1″ name=”s1″ onchange=’spop();’ ><option>Sort1</option><option></option></select></td><td><select id=”s2″ name=”s2″ ><option>Sort2</option><option></option></select></td><td><select id=”s3″ name=”s3″ ><option>Sort3</option><option></option></select></td><td><select id=”s4″ name=”s4″ onchange = ‘ld();’><option>Load</option></select></td><td><input type=”checkbox” id=”ch1″ name=”ch1″ value=” ” />Descend</td><td><input type=”button” id=”b1″ name=”b1″ value=”Sort” onclick=’srtd();’ /></td><td><input type=”button” id=”b3″ name=”b3″ value=”Add Rows” onclick=’addRows();’ /><td><input type=”button” id=”b4″ name=”b4″ value=”Save Data” onclick=’savData();’ /></td><td><input type=”button” id=”b2″ name=”b2″ value=”Save” onclick=’sve();’ /></td></tr>
</table>
<table id= “tb2″ contenteditable=”true”></table>
<textarea id = “d1” style = “position: absolute; top:0; left: 0; width: 100%; height: 100%; visibility: hidden” ondblclick=’document.getElementById(“d1”).style.visibility = “hidden”;document.getElementById(“b5”).style.visibility = “hidden”;’></textarea> ;
<input type=”button” id=”b5″ style = “position: absolute; top:0; left: 0; visibility: hidden” name=”b5″ value=”Close” onclick=’document.getElementById(“d1”).style.visibility = “hidden”;document.getElementById(“b5”).style.visibility = “hidden”;’ />

http://tableSort.js
http://tableSort2.js
<script type=”text/javascript”>
function setGrid() {
rs = document.getElementById(“norows”).innerHTML;
cs = document.getElementById(“nocols”).innerHTML;
for (var j = 1; j <= rs; j ++) {
stri += ‘<tr>’;
for (var i =1; i <= cs; i ++) {
stri += ‘<td id=”td’ + j + i + ‘” style=”border:1px solid black”> </td>’;
}
stri += ‘</tr>’;
}
document.getElementById(“tb2”).innerHTML = stri;
}
</script>
</body></html>


The only script in the html file is setGrid(), which creates the table:
rs = document.getElementById(“norows”).innerHTML;
cs = document.getElementById(“nocols”).innerHTML;
for (var j = 1; j <= rs; j ++) {
stri += ‘<tr>’;
for (var i =1; i <= cs; i ++) {
stri += ‘<td id=”td’ + j + i + ‘” style=”border:1px solid black”> </td>’;
}
stri += ‘</tr>’;
}
document.getElementById(“tb2”).innerHTML = stri;
}
Two loops set up the 2D table.

Most of the script is in the file tableSort.js:

var tmp = “”; v = new Array(); var stri = “”; var rs; var cs; var a; var cnt = 2; var usd = “0,”; var oldURL = “newSort.html”; var no = 0;

function ini() {
document.getElementById(“s4”).innerHTML += ‘<option>sort1</option><option>sort2</option>’;
}

function savData() {
var nm = prompt(“Data Name”, “”) ;
document.getElementById(“d1″).innerHTML = ‘\n\nvar ‘ + nm + ‘ = \” + document.getElementById(“tb2”).innerHTML + ‘\’; \n’;
document.getElementById(“d1”).innerHTML += ‘if (document.getElementById(“s4”).value == “‘ + nm + ‘”) {document.getElementById(“tb2”).innerHTML = ‘ + nm + ‘; rs = ‘ + rs + ‘; cs = ‘ + cs + ‘;}’;

document.getElementById(“d1”).style.visibility = “visible”;
document.getElementById(“b5”).style.visibility = “visible”;
}

function addRows() {
stri = “”;
no = prompt(“How Many Rows”, “1”);
for (var j = 1; j <= no; j ++) {
stri += ‘<tr>’;
for (var i =1; i <= cs; i ++) {
stri += ‘<<d id="td' + (parseInt(rs) + j) + i + '" style="border:1px solid black"> </td>';
}
stri += '</tr>';
}
document.getElementById("tb2").innerHTML += stri;
rs = (parseInt(rs) + parseInt(no)).toString();
}

function spop() {
if (document.getElementById("s1").innerHTML == "Sort1″) {
for (var i = 1; i <= cs; i ++) {
document.getElementById("s1").innerHTML += '’ + document.getElementById(“td1″ + i).innerHTML + ‘ ‘ + i + ”;
document.getElementById(“s2″).innerHTML += ” + document.getElementById(“td1″ + i).innerHTML + ‘ ‘ + i + ”;
document.getElementById(“s3″).innerHTML += ” + document.getElementById(“td1″ + i).innerHTML + ‘ ‘ + i + ”;
}
}
}

function srtd() {
usd = “0,”
cnt = 2;
a = document.getElementById(“s1″).value.lastIndexOf(” “);
v[1] = document.getElementById(“s1”).value.substr(a + 1);
usd += v[1] + “,”;
a = document.getElementById(“s2″).value.lastIndexOf(” “);
v[2] = document.getElementById(“s2”).value.substr(a + 1);
usd += v[2] + “,”;
a = document.getElementById(“s3″).value.lastIndexOf(” “);
v[3] = document.getElementById(“s3”).value.substr(a + 1);
usd += v[3] + “,”;
for (var j = 1; j <= 2; j ++) {
for (var i = 1; i <= cs; i ++) {
if (v[1] != i.toString() && v[2] != i.toString() && v[3] != i.toString() && usd.indexOf(i.toString()) == -1) {
cnt ++;
if (cnt <= 5) {
usd += i.toString() + “,”;
}
}
}
v = usd.split(“,”);
}
if (! document.getElementById(“ch1”).checked) srt();
if (document.getElementById(“ch1”).checked) srt2();
}

function srt() {
for (var j = 2; j <= rs – 1; j ++) {
for (var i= j + 1; i <= rs; i ++) {
if (document.getElementById(“td” + i +v[1]).innerHTML < document.getElementById(“td” + j + v[1]).innerHTML) {
for (var k = 1; k <= cs; k ++) {
tmp = document.getElementById(“td” + j + v[k]).innerHTML;
document.getElementById(“td” + j + v[k]).innerHTML = document.getElementById(“td” + i + v[k]).innerHTML;
document.getElementById(“td” + i + v[k]).innerHTML = tmp;
}
}
}
}

for (var j = 2; j <= rs – 1; j ++) {
for (var i= j + 1; i <= rs; i ++) {
if (document.getElementById(“td” + i + v[1]).innerHTML == document.getElementById(“td” + j + v[1]).innerHTML && document.getElementById(“td” + i + v[2]).innerHTML < document.getElementById(“td” + j + v[2]).innerHTML) {
for (var k = 1; k <= cs; k ++) {
tmp = document.getElementById(“td” + j + v[k]).innerHTML;
document.getElementById(“td” + j + v[k]).innerHTML = document.getElementById(“td” + i + v[k]).innerHTML;
document.getElementById(“td” + i + v[k]).innerHTML = tmp;
}
}
}
}

for (var j = 2; j <= rs – 1; j ++) {
for (var i= j + 1; i <= rs; i ++) {
if (document.getElementById(“td” + i + v[1]).innerHTML == document.getElementById(“td” + j + v[1]).innerHTML && document.getElementById(“td” + i + v[2]).innerHTML == document.getElementById(“td” + j + v[2]).innerHTML && document.getElementById(“td” + i + v[3]).innerHTML < document.getElementById(“td” + j + v[3]).innerHTML) {
for (var k = 1; k <= cs; k ++) {
tmp = document.getElementById(“td” + j + v[k]).innerHTML;
document.getElementById(“td” + j + v[k]).innerHTML = document.getElementById(“td” + i + v[k]).innerHTML;
document.getElementById(“td” + i + v[k]).innerHTML = tmp;
}
}
}
}
}

function srt2() {
for (var j = 2; j <= rs – 1; j ++) {
for (var i= j + 1; i <= rs; i ++) {

if (document.getElementById(“td” + i + v[1]).innerHTML > document.getElementById(“td” + j + v[1]).innerHTML) {
for (var k = 1; k <= cs; k ++) {
tmp = document.getElementById(“td” + j + v[k]).innerHTML;
document.getElementById(“td” + j + v[k]).innerHTML = document.getElementById(“td” + i + v[k]).innerHTML;
document.getElementById(“td” + i + v[k]).innerHTML = tmp;
}
}
}
}

for (var j = 2; j <= rs – 1; j ++) {
for (var i= j + 1; i <= rs; i ++) {
if (document.getElementById(“td” + i + v[1]).innerHTML == document.getElementById(“td” + j + v[1]).innerHTML && document.getElementById(“td” + i + v[2]).innerHTML > document.getElementById(“td” + j + v[2]).innerHTML) {
for (var k = 1; k <= cs; k ++) {
tmp = document.getElementById("td" + j + v[k]).innerHTML;
document.getElementById("td" + j + v[k]).innerHTML = document.getElementById("td" + i + v[k]).innerHTML;
document.getElementById("td" + i + v[k]).innerHTML = tmp;
}
}
}
}

for (var j = 2; j <= rs – 1; j ++) {
for (var i= j + 1; i document.getElementById(“td” + j + v[3]).innerHTML) {
for (var k = 1; k <= cs; k ++) {
tmp = document.getElementById(“td” + j + v[k]).innerHTML;
document.getElementById(“td” + j + v[k]).innerHTML = document.getElementById(“td” + i + v[k]).innerHTML;
document.getElementById(“td” + i + v[k]).innerHTML = tmp;
}
}
}
}
}

function sve() {
var sav = ‘<html><head><title>Sort</title><style>body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }table{position: relative; left: 100px; empty-cells: show; border-collapse: collapse}td{position: relative; height: 20px; padding: 2px 10px;}</style>&lt/head><body><table id= “tb2”>’ +document.getElementById(“tb2″).innerHTML + ‘</table><script>window,print();<\/script>&lt&/body&gt”</html>’;
var textToSaveAsBlob = new Blob([sav], {type:”text/plain”});
var textToSaveAsURL = window.URL.createObjectURL(textToSaveAsBlob);
fileNameToSaveAs = prompt(“FileName to save as”,oldURL);
oldURL = fileNameToSaveAs;
var downloadLink = document.createElement(“a”);
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = “Download File”;
downloadLink.href = textToSaveAsURL;
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = “none”;
document.body.appendChild(downloadLink);
downloadLink.click();
}

function destroyClickedElement(event) {
document.body.removeChild(event.target);
}


The sorting options are set by spop():
function spop() {
if (document.getElementById(“s1”).innerHTML == “Sort1”) {
for (var i = 1; i <= cs; i ++) {
document.getElementById("s1").innerHTML += '’ + document.getElementById(“td1″ + i).innerHTML + ‘ ‘ + i + ”;
document.getElementById(“s2″).innerHTML += ” + document.getElementById(“td1″ + i).innerHTML + ‘ ‘ + i + ”;
document.getElementById(“s3″).innerHTML += ” + document.getElementById(“td1″ + i).innerHTML + ‘ ‘ + i + ”;
}
}
}

Clicking Sort calls up srtd() which creates an array v to be used in the sorting:
function srtd() {
usd = “0,”
cnt = 2;
a = document.getElementById(“s1″).value.lastIndexOf(” “);
v[1] = document.getElementById(“s1”).value.substr(a + 1);
usd += v[1] + “,”;
a = document.getElementById(“s2″).value.lastIndexOf(” “);
v[2] = document.getElementById(“s2”).value.substr(a + 1);
usd += v[2] + “,”;
a = document.getElementById(“s3″).value.lastIndexOf(” “);
v[3] = document.getElementById(“s3”).value.substr(a + 1);
usd += v[3] + “,”;
for (var j = 1; j <= 2; j ++) {
for (var i = 1; i <= cs; i ++) {
if (v[1] != i.toString() && v[2] != i.toString() && v[3] != i.toString() && usd.indexOf(i.toString()) == -1) {
cnt ++;
if (cnt <= 5) {
usd += i.toString() + “,”;
}
}
}
v = usd.split(“,”);
}
if (! document.getElementById(“ch1”).checked) srt();
if (document.getElementById(“ch1”).checked) srt2();
}

srt sorts ascendingly while sort3 descends.

function srt() {
for (var j = 2; j <= rs – 1; j ++) {
for (var i= j + 1; i <= rs; i ++) {
if (document.getElementById(“td” + i +v[1]).innerHTML < document.getElementById("td" + j + v[1]).innerHTML) {
for (var k = 1; k <= cs; k ++) {
tmp = document.getElementById("td" + j + v[k]).innerHTML;
document.getElementById("td" + j + v[k]).innerHTML = document.getElementById("td" + i + v[k]).innerHTML;
document.getElementById("td" + i + v[k]).innerHTML = tmp;
}
}
}
}

After the first sort a condition is set for the second sort:
if (document.getElementById("td" + i + v[1]).innerHTML == document.getElementById("td" + j + v[1]).innerHTML && document.getElementById("td" + i + v[2]).innerHTML < document.getElementById("td" + j + v[2]).innerHTML) {

Similarly a conditio n for the third sort is set.

“Moving” a Table Column


This post shows a method to move table columns as if they were spreadsheet columns.

There are two select boxes, one to pick the column to be moved and the other to pick the point of placement.

The second image shows the table after moving the third column to after the first column:

Here is the code:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>HTML Editor</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }
#s1{position: relative; left 0; top: 0}
#s2{position: relative; left 200; top: 0}
td {padding: 0 10px}
</style>
</head>
<body>
<select id=”s1″ name=”s1″ onchange=’insrt();’><option >Insert after</option><option >td11</option><option >td12</option><option >td13</option><option >td14</option></select> <select id=”s2″ name=”s2″ onchange=’ct();’><option >Cut</option><option >td11</option><option >td12</option><option >td13</option><option >td14</option></select>
<table id= “tb1″ style=”position: relative; left: 10px; top: 50px;” >
<tr id = “tr1″><td id=”td11″>cell11</td><td id=”td12″>cell12</td><td id=”td13″>cell13</td><td id=”td14″>cell14</td><td id=”td15”>cell15</td></tr>
<tr id = “tr2″><td id=”td21″>cell21</td><td id=”td22″>cell22</td><td id=”td23″>cell23</td><td id=”td24″>cell24</td><td id=”td25”>cell25</td></tr>
<tr id = “tr3″><td id=”td31″>cell31</td><td id=”td32″>cell32</td><td id=”td33″>cell33</td><td id=”td34″>cell34</td><td id=”td35”>cell35</td></tr>
<tr id = “tr4″><td id=”td41″>cell41</td><td id=”td42″>cell42</td><td id=”td43″>cell43</td><td id=”td44″>cell44</td><td id=”td45”>cell45</td></tr>
<tr id = “tr5″><td id=”td51″>cell51</td><td id=”td52″>cell52</td><td id=”td53″>cell53</td><td id=”td54″>cell54</td><td id=”td55”>cell55</td></tr>
</table>

<table id= “tb2″ style=”position: relative; left: 10px; top: 70px; visibility: hidden” contenteditable = “true” >
<tr><td id = “a1”></td></tr>
<tr><td id = “a2”></td></tr>
<tr><td id = “a3”></td></tr>
<tr><td id = “a4”></td></tr>
<tr><td id = “a5″></td></tr>
</table>

<script type=”text/javascript”>
var cls = 5; var cut = false;

function ct() {
cut = true;
var no = document.getElementById(“s2”).selectedIndex;
if (no > 0) {
for (var i = 1; i <= 5; i ++) {
document.getElementById(“a” + i.toString()).innerHTML = document.getElementById(“td” + i.toString() + no.toString() ).innerHTML;
document.getElementById(“td” + i.toString() + no.toString() ).innerHTML = “”;
document.getElementById(“td” + i.toString() + no.toString() ).style.width = “1px”;
document.getElementById(“td” + i.toString() + no.toString() ).style.padding = “0”;
document.getElementById(“td” + i.toString() + no.toString() ).style.backgroundColor = “#dddddd”;
}
}
document.getElementById(“s2”).selectedIndex = 0;
}

function insrt() {
var no = document.getElementById(“s1”).selectedIndex;
var no2 = no + 1;
if (no > 0) {
for (var i = 1; i <= 5; i ++) {
document.getElementById(“tr” + i).innerHTML += ‘<td id=”td’ + i.toString() + (cls + 1).toString() + ‘”>test</td>’;
}
cls ++;
for (var i = 1; i <= 5; i ++) {
for (var j = cls; j > no; j –) {
document.getElementById(“td” + i.toString() + j.toString() ).innerHTML = document.getElementById(“td” + i.toString() + (j-1).toString() ).innerHTML;
}
document.getElementById(“td” + i.toString() + no2.toString() ).innerHTML = document.getElementById(“a” + i.toString()).innerHTML;
}
}
document.getElementById(“s1”).selectedIndex = 0;
cut = false;
}
</script>
</body></html>


Clicking the Cut box calls the function ct():
function ct() {
cut = true;
var no = document.getElementById(“s2”).selectedIndex;
if (no > 0) {
for (var i = 1; i <= 5; i ++) {
document.getElementById(“a” + i.toString()).innerHTML = document.getElementById(“td” + i.toString() + no.toString() ).innerHTML;
document.getElementById(“td” + i.toString() + no.toString() ).innerHTML = “”;
document.getElementById(“td” + i.toString() + no.toString() ).style.width = “1px”;
document.getElementById(“td” + i.toString() + no.toString() ).style.padding = “0”;
document.getElementById(“td” + i.toString() + no.toString() ).style.backgroundColor = “#dddddd”;
}
}
document.getElementById(“s2”).selectedIndex = 0;
cut = false;
}

This function uses a second method for making selections. Each option has an index, beginning at 0. Rather than the usual comparing to the value, the choice index can be obtained:
var no = document.getElementById(“s2”).selectedIndex;

This can be more convenient if the action involves an index as in the following:
document.getElementById(“td” + i.toString() + no.toString() ).innerHTML = “”;
document.getElementById(“td” + i.toString() + no.toString() ).style.width = “1px”;
document.getElementById(“td” + i.toString() + no.toString() ).style.padding = “0”;

The column can not actually be cut, but can be made to appear so by removing any text, reducing its width to 1px and padding to 0.

There is a hidden table similar in size to the visible one, whose cells are given the values of the “cut” cells. This table will be used to pass the values to be inserted:
document.getElementById(“a” + i.toString()).innerHTML = document.getElementById(“td” + i.toString() + no.toString() ).innerHTML;

Choosing from the Insert After box calls insrt():
function insrt() {
var no = document.getElementById(“s1”).selectedIndex;
var no2 = no + 1;
if (no > 0) {
for (var i = 1; i <= 5; i ++) {
document.getElementById(“tr” + i).innerHTML += ‘<td id=”td’ + i.toString() + (cls + 1).toString() + ‘”>test</td>’;
}
cls ++;
for (var i = 1; i <= 5; i ++) {
for (var j = cls; j > no; j –) {
document.getElementById(“td” + i.toString() + j.toString() ).innerHTML = document.getElementById(“td” + i.toString() + (j-1).toString() ).innerHTML;
}
document.getElementById(“td” + i.toString() + no2.toString() ).innerHTML = document.getElementById(“a” + i.toString()).innerHTML;
}
}
document.getElementById(“s1”).selectedIndex = 0;
cut = false;
}

First, a new column is added:
for (var i = 1; i <= 5; i ++) {
document.getElementById(“tr” + i).innerHTML += ‘<td id=”td’ + i.toString() + (cls + 1).toString() + ‘”>test</td>’;
}

Each subsequent cell is given the value of the previous one and the saved values are added at the correct position:
for (var i = 1; i <= 5; i ++) {
for (var j = cls; j > no; j –) {
document.getElementById(“td” + i.toString() + j.toString() ).innerHTML = document.getElementById(“td” + i.toString() + (j-1).toString() ).innerHTML;
}
document.getElementById(“td” + i.toString() + no2.toString() ).innerHTML = document.getElementById(“a” + i.toString()).innerHTML;
}

A Javascript Karaoke Creator And Player

This is a template for a future Karaoke creator and player.
It allows the interactive timing of the lyrics. This model has no sound, but the actual creator would allow the synchronization of the lyrics to the music.

Here is how it appears:

Here is the code:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>HTML Editor</title>
<style>
body {margin-left:0;margin-right:0;font:normal 800 24px Arial; background: #f5fffa}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }
#t1{position: relative; width: 700px}
</style>
</head>
<body>
<div id=”frame” style=”width: 100%; height: 100%”></div>
<script type=”text/javascript”>
var si; var cnt = 0; var cnt2 = 1; var cntt = 0; var start; var stop; var scnt = 0; var makesyl = false; dur = new Array();
var durstr = “”;
document.getElementById(“frame”).innerHTML += ‘<center><input type=”button” id=”b1″ name=”b1″ value=”test” onclick=”tst();” /> <input type=”button” id=”b2″ name=”b2″ value=”Create” onclick=”crt();” /> <input type=”button” id=”b3″ name=”b3″ value=”Syllable” onclick=”ns();” /> <input type=”text” id=”t1″ name=”t1″ value=”” /></center>’;

function ns() {
scnt ++;
document.getElementById(“frame”).innerHTML += ‘<span id = “sp’ + scnt + ‘” style=”position: relative; color: red; ” contenteditable = “true” onclick=”del(scnt);”>–</span>’;
document.getElementById(“sp” + scnt.toString()).focus();
del(scnt);
}

function del(nd) {
document.getElementById(“sp” + nd.toString()).innerHTML = “”;
}

function crt() {
cntt ++;
if (cntt == 1) start = new Date().getTime();
if (cntt > 1) {
stop = new Date().getTime();
durstr += stop – start + “,”;
document.getElementById(“t1”).value = durstr;
start = stop;
}
}

function tst() {
durstr = durstr.substr(0, durstr.length – 1);
dur = durstr.split(“,”);
document.getElementById(“t1”).value = dur;
document.getElementById(“sp1”).style.color = “blue”;
setTimeout(“chng()”,dur[0]);
}

function chng() {
cnt ++;
cnt2 ++;
document.getElementById(“sp” + cnt.toString()).style.color = “red”;
document.getElementById(“sp” + cnt2.toString()).style.color = “blue”;
if (cnt2 < dur.length + 1) {
setTimeout(“chng()”, dur[cnt]);
}
if (cnt2 == dur.length + 1) document.getElementById(“sp” + cnt2.toString()).style.color = “red”;
}
</script>
</body></html>


There are three buttons and a text input.

The button Syllable calls the function ns():
function ns() {
scnt ++;
document.getElementById(“frame”).innerHTML += ‘<span id = “sp’ + scnt + ‘” style=”position: relative; color: red; ” contenteditable = “true” onclick=”del(scnt);”>–</span>’;
document.getElementById(“sp” + scnt.toString()).focus();
del(scnt);
It creates the lyrics with each note beat incorporated into a span.
}

The button Create inserts the timing:
function crt() {
cntt ++;
if (cntt == 1) start = new Date().getTime();
if (cntt > 1) {
stop = new Date().getTime();
durstr += stop – start + “,”;
document.getElementById(“t1”).value = durstr;
start = stop;
}
}

The first click starts a timer:
if (cntt == 1) start = new Date().getTime();
Each subsequent click creates an interval and inserts it in the text input:
if (cntt > 1) {
stop = new Date().getTime();
durstr += stop – start + “,”;
document.getElementById(“t1”).value = durstr;
start = stop;
The intervals can be fine tuned in the box.

Test calls the function tst():
function tst() {
durstr = durstr.substr(0, durstr.length – 1);
dur = durstr.split(“,”);
document.getElementById(“t1”).value = dur;
document.getElementById(“sp1”).style.color = “blue”;
setTimeout(“chng()”,dur[0]);
}

which creates a timing array, puts the value in the text input and changes the color of the first beat:
durstr = durstr.substr(0, durstr.length – 1);
dur = durstr.split(“,”);
document.getElementById(“t1”).value = dur;
document.getElementById(“sp1”).style.color = “blue”;
It the calls a timer:
setTimeout(“chng()”,dur[0]);

function chng() {
cnt ++;
cnt2 ++;
document.getElementById(“sp” + cnt.toString()).style.color = “red”;
document.getElementById(“sp” + cnt2.toString()).style.color = “blue”;
if (cnt2 < dur.length + 1) {
setTimeout(“chng()”, dur[cnt]);
}
if (cnt2 == dur.length + 1) document.getElementById(“sp” + cnt2.toString()).style.color = “red”;
}

This function changes the colors,turning the previous blue red and the red blue.
It then calls itself again:
setTimeout(“chng()”, dur[cnt]);

A HTML Music Score Creator


This app creates musical scores which can be saved as web pages and printed, including to pdf.

Here is a simple example(much more can be done):

Here is the interface:

This is not comprehensive, as only quarter, eighth and sixteenth notes can be drawn, but they can be beamed. Other symbols can be included if the images are downloaded.

Staffs can be added as needed,

Here is the code:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>Score Maker</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }
#svg1, #grd{position: absolute; left: 0; top: 0; width: 1000px; height: 1300px;}
#svg1{ border: 1px solid gray; cursor: crosshair; }
</style>
</head>
<body>

<svg id = “grd” xmlns=”http://www.w3.org/2000/svg”&gt;
<defs>
<pattern id=”smallGrid” width=”8″ height=”8″ patternUnits=”userSpaceOnUse”>
<path d=”M 8 0 L 0 0 0 8″ fill=”none” stroke=”gray” stroke-width=”0.5″/>
</pattern>
<pattern id=”grid” width=”80″ height=”80″ patternUnits=”userSpaceOnUse”>
<rect width=”80″ height=”80″ fill=”url(#smallGrid)”/>
<path d=”M 80 0 L 0 0 0 80″ fill=”none” stroke=”gray” stroke-width=”1″/>
</pattern>
</defs>
<rect width=”100%” height=”100%” fill=”url(#grid)” />
</svg>

<svg xmlns=”http://www.w3.org/2000/svg&#8221; version=”1.1″ id = “svg1” >
<line x1 = “20” y1 = “80” x2 = “980” y2 = “80” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “100” x2 = “980” y2 = “100” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “120” x2 = “980” y2 = “120” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “140” x2 = “980” y2 = “140” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “160” x2 = “980” y2 = “160” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “80” x2 = “20” y2 = “160” style = “stroke-width: 2; stroke: black” />
<line x1 = “980” y1 = “80” x2 = “980” y2 = “160” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “200” x2 = “980” y2 = “200” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “220” x2 = “980” y2 = “220” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “240” x2 = “980” y2 = “240” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “260” x2 = “980” y2 = “260” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “280” x2 = “980” y2 = “280” style = “stroke-width: 2; stroke: black” />
<line x1 = “20” y1 = “200” x2 = “20” y2 = “280” style = “stroke-width: 2; stroke: black” />
<line x1 = “980” y1 = “200” x2 = “980” y2 = “280” style = “stroke-width: 2; stroke: black” />

</svg>

<table id= “tb1” style = “position: absolute; left: 1001px” >
<tr>
<td id = “td1” onclick = ‘vals(-8, -34, “images/qu.gif”, 20, 40);’><img src= “images/qu.gif” width=”18px” height=”36px” /></td>
<td id = “td1” onclick = ‘vals(-8, -34, “images/u8.gif”, 20, 40);’><img src= “images/u8.gif” width=”20px” height=”40px” /></td>
<td id = “td1” onclick = ‘vals(-8, -32, “images/u16.gif”, 21, 42);’><img src= “images/u16.gif” width=”20px” height=”40px” /></td>
<td id = “td1” onclick = ‘vals(-10, -6, “images/qd.gif”, 20, 40);’><img src= “images/qd.gif” width=”15px” height=”30px” /></td>
<td id = “td1” onclick = ‘vals(-10, -6, “images/d8.gif”, 20, 40);’><img src= “images/d8.gif” width=”15px” height=”34px” /></td>
<td id = “td1” onclick = ‘vals(-10, -6, “images/d16.gif”, 20, 40);’><img src= “images/d16.gif” width=”15px” height=”34px” /></td>
<td id = “td1” onclick = ‘vals(-14, -20, “images/sharp.gif”,24, 24);’><img src= “images/sharp.gif” width=”12px” height=”24px” /></td>
<td id = “td1” onclick = ‘vals(-8, -20, “images/flat.gif”,12, 24);’><img src= “images/flat.gif” width=”12px” height=”24px” /></td>
<td id = “td1” onclick = ‘vals(-8, -20, “images/nat.gif”,12, 24);’><img src= “images/nat.gif” width=”12px” height=”24px” /></td>
<td id = “td1” onclick = ‘vals(-8, -20, “images/forte.gif”,24, 48);’><img src= “images/forte.gif” width=”20px” height=”32px” /></td>
<td id = “td2” onclick = ‘vals(-8, -2, “images/lbracket.gif”, 15, 90);’><img src= “images/lbracket.gif” width=”15px” height=”60px” /></td>
<td id = “td2” onclick = ‘vals(-14, -42, “images/treble.gif”, 30, 80);’><img src= “images/treble.gif” width=”15px” height=”40px” /></td>
<td id = “td2” onclick = ‘vals(-15, -30, “images/bass.gif”, 30, 60);’><img src= “images/bass.gif” width=”18px” height=”36px” /></td>
<td id = “sel”><select id=”s1″ name=”s1″ onchange=’miscel();’>
<option >Misc</option>
<option >Staff</option>
<option >Measure</option>
<option >Text</option>
<option >Beam 8</option>
<option >Beam 16</option>
</select></td>
<td><input type=”button” id=”b1″ name=”b1″ value=”Save” onclick=’sve();’/>
</tr>
</table>

<script type=”text/javascript”>
var posX;var posY; var offx; var offy; var source; var wdth; var hgt; var ymax = 120; var posX2; var cnt = 0; var posY2; var cnt2 = 0; var fs; var v1; var oldURL = “score1.html”;
var IE = document.all?true:false;
if (!IE) document.captureEvents(Event.MOUSEMOVE);
document.getElementById(“svg1”).onclick = getMouseXY;

function getMouseXY(e) {
if (document.getElementById(“s1”).value != “Beam 8” && document.getElementById(“s1”).value != “Beam 16”) {
if (IE) {
posX = event.clientX + document.body.scrollLeft;
posY = event.clientY + document.body.scrollTop;
}
else {
posX = e.pageX + offx;
posY = e.pageY + offy ;
}
}
if (document.getElementById(“s1”).value == “Misc”) document.getElementById(“svg1”).innerHTML += ‘<image x= “‘ + posX + ‘” y= “‘ + posY + ‘ ” width=”‘ + wdth + ‘” height=”‘ + hgt + ‘” xlink:href = “‘ + source + ‘” />’;
if (document.getElementById(“s1”).value == “Measure”) document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + posX + ‘” y1 = “‘ + posY + ‘” x2 = “‘+ posX + ‘” y2 = “‘ + (posY + 80) + ‘” style = “stroke-width: 2; stroke: black” />’;

if (document.getElementById(“s1”).value == “Text”) document.getElementById(“svg1″).innerHTML += ‘<text x=”‘ + posX + ‘” y=”‘ + posY + ‘” style=”fill: black; font-size:’ + fs + ‘ ; font-family:Georgia ; font-style:italic ; font-weight:800 “>’ + source + ‘</text>’;

if (document.getElementById(“s1”).value == “Beam 8” || document.getElementById(“s1”).value == “Beam 16”) {
cnt ++;
if (cnt == 1) {posX = e.pageX + offx; posY = e.pageY + offy;}
if (cnt == 2) {
posX2 = e.pageX + offx;
posY2 = e.pageY + offy;
document.getElementById(“svg1″).innerHTML += ‘<line stroke-width=”3″ x1=”‘ + posX + ‘” y1=”‘ + posY + ‘” x2=”‘ + posX2 + ‘” y2=”‘ + posY2 + ‘” stroke-linecap=”round” stroke=”black” />’;
if (document.getElementById(“s1”).value == “Beam 16”) document.getElementById(“svg1″).innerHTML += ‘<line stroke-width=”3″ x1=”‘ + posX + ‘” y1=”‘ + (posY + 5) + ‘” x2=”‘ + posX2 + ‘” y2=”‘ + (posY2 + 5) + ‘” stroke-linecap=”round” stroke=”black” />’;
cnt = 0;
}
}
if (cnt == 0) document.getElementById(“s1”).value = “Misc”;
}

function vals(xoff, yoff, srce, wd, ht) {
offx = xoff;
offy = yoff;
source = srce;
wdth = wd;
hgt = ht;
}

function sve() {
var sav = ‘<html><head><title>Score Maker</title><style><body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { } #svg1{position: relative; width: 1000px; height: 1300px;}</style></head> <body><center><svg xmlns=”http://www.w3.org/2000/svg&#8221; version=”1.1″ id = “svg1” >’ + document.getElementById(“svg1″).innerHTML + ‘</svg></center> </body></html>’;
var textToSaveAsBlob = new Blob([sav], {type:”text/plain”});
var textToSaveAsURL = window.URL.createObjectURL(textToSaveAsBlob);
fileNameToSaveAs = prompt(“FileName to save as”,oldURL);
oldURL = fileNameToSaveAs;
var downloadLink = document.createElement(“a”);
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = “Download File”;
downloadLink.href = textToSaveAsURL;
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = “none”;
document.body.appendChild(downloadLink);
downloadLink.click();
}

function destroyClickedElement(event) {
document.body.removeChild(event.target);
window.print();
}

function miscel() {
if (document.getElementById(“s1”).value == “Staff”) {
cnt2 ++;
if (cnt2 == 1) ymax += 160;
if (cnt2 == 2) {ymax += 120; cnt2 = 0; }
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “20” y1 = “‘ + (ymax + 80) +
‘” x2 = “980” y2 = “‘ + (ymax + 80) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 100) +
‘” x2 = “980” y2 = “‘ + (ymax + 100) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 120) +
‘” x2 = “980” y2 = “‘ + (ymax + 120) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 140) +
‘” x2 = “980” y2 = “‘ + (ymax + 140) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 160) +
‘” x2 = “980” y2 = “‘ + (ymax + 160) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “980” y1 = “‘ + (ymax + 80) +
‘” x2 = “980” y2 = “‘ + (ymax + 160) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 80) +
‘” x2 = “20” y2 = “‘ + (ymax + 160) + ‘” style = “stroke-width: 2; stroke: black” />’;
document.getElementById(“s1”).value = “Misc”;
}
if (document.getElementById(“s1”).value == “Measure” || document.getElementById(“s1”).value == “Text” || document.getElementById(“s1”).value == “Beam 8” || document.getElementById(“s1”).value == “Beam 16”) {offx = 0; offy = 0;}
if (document.getElementById(“s1”).value == “Text”) {v1 = prompt(“Enter Text, Font Size”, “”); fs = v1.substr(v1.length – 2); source = v1.substr(0,v1.length – 3);}
}

</script>
</body></html>


The notes are drawn on a SVG element.

The “notes” are actually table cells containing an image:
<td id = “td1” onclick = ‘vals(-8, -34, “images/qu.gif”, 20, 40);’><img src= “images/qu.gif” width=”18px” height=”36px” /></td>

Clicking one calls vals(), which sets the variables for displaying:
function vals(xoff, yoff, srce, wd, ht) {
offx = xoff;
offy = yoff;
source = srce;
wdth = wd;
hgt = ht;
}

The procedure is done with a mouse listener, which inserts the note at the point of clicking:
if (document.getElementById(“s1”).value == “Misc”) document.getElementById(“svg1”).innerHTML += ‘<image x= “‘ + posX + ‘” y= “‘ + posY + ‘ ” width=”‘ + wdth + ‘” height=”‘ + hgt + ‘” xlink:href = “‘ + source + ‘” />’;

Mouse listeners have been discussed before.

Additionally, measures, text and quarter and half notes beams can be drawn:

if (document.getElementById(“s1”).value == “Measure” || document.getElementById(“s1”).value == “Text” || document.getElementById(“s1”).value == “Beam 8” || document.getElementById(“s1”).value == “Beam 16”) {offx = 0; offy = 0;}
if (document.getElementById(“s1”).value == “Text”) {v1 = prompt(“Enter Text, Font Size”, “”); fs = v1.substr(v1.length – 2); source = v1.substr(0,v1.length – 3);}
}

New staffs can also be added:
if (document.getElementById(“s1”).value == “Staff”) {
cnt2 ++;
if (cnt2 == 1) ymax += 160;
if (cnt2 == 2) {ymax += 120; cnt2 = 0; }
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “20” y1 = “‘ + (ymax + 80) +
‘” x2 = “980” y2 = “‘ + (ymax + 80) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 100) +
‘” x2 = “980” y2 = “‘ + (ymax + 100) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 120) +
‘” x2 = “980” y2 = “‘ + (ymax + 120) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 140) +
‘” x2 = “980” y2 = “‘ + (ymax + 140) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 160) +
‘” x2 = “980” y2 = “‘ + (ymax + 160) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “980” y1 = “‘ + (ymax + 80) +
‘” x2 = “980” y2 = “‘ + (ymax + 160) + ‘” style = “stroke-width: 2; stroke: black” /> <line x1 = “20” y1 = “‘ + (ymax + 80) +
‘” x2 = “20” y2 = “‘ + (ymax + 160) + ‘” style = “stroke-width: 2; stroke: black” />’;

There are two types of staffs, one, for example to complete a treble staff with a base staff and the other to start a completely new one.

That is why one has a y increment of 120 px and the other 160.

To make placement easier, a grid is drawn, but that procedure has been discussed before

After completion the sheet can be saved to either a HTML page or a PDF, but that has also been discussed before.