Программирование html-таблиц рассматривается на примере создания игры Пятнашки. Для этой игры нужна 4×4-таблица.
Приводимый далее код позволяет:
Также поддерживаются разные виды курсоров мыши (pointer, crosshair и default, [1]) и иной необходимый для игры функционал.
Приводимая ниже функция mkTbl создает n×n-таблицу с квадратными ячейками с n строками и с n столбцами. Функция принимает два параметра: размер ячейки и размер таблицы. Кроме того, в функции определяется обработчик sayRC события click ячейки таблицы, выводящий сообщение о координатах ячейки таблицы. Обработчик принимает ссылку на ячейку таблицы, принявшую мышиный удар.
В коде также присутствуют две глобальные переменные tbl и mT (соответственно ссылка на таблицу и размер таблицы), используемые в функциях создаваемой программы.
tbl = "";
mT = 0;
function mkTbl(tdSz, n) {
mT = n;
td = "<td style = 'background-color:#f0f0f0; width:" + tdSz + "; height:" + tdSz + "'";
td += " onclick = 'sayRC(this)'></td>";
tHdr = "<table id = 'tbl' style = 'background-color:#ccccaa; cursor:pointer'; border = 1px";
document.write(tHdr);
for (i = 0; i < mT; i++) {
document.write("<tr>");
for (j = 0; j < mT; j++) {
document.write(td);
}
document.write("</tr>");
}
document.write("</table>");
tbl = document.getElementById("tbl");
}
function sayRC(cll) {
// Номер столбца текущей ячейки
c = cll.cellIndex;
r = gtRw(cll, c);
alert("Ячейка " + r + ":" + c);
}
// Возвращает номер строки, которой расположена ячейка cll
function gtRw(cll, c) {
for (i = 0; i < mT; i++) {
rw = tbl.rows[i];
if (rw.cells[c] == cll) return i;
}
}
Замечание. В дальнейшем для придания курсору мыши надлежащего вида будет употреблен следующий код:
tbl.style.cursor = 'pointer' // Или иной вид, например crosshair
Запишем приведенный код в файл tryTbl.js и построим html-таблицу, открыв в обозревателе файл tst.html, имеющий следующее наполнение:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv = "Content-Type" content = "text/html; charset=windows-1251">
<title>Таблица на JavaScript</title>
<script type = "text/javascript" src = "tryTbl.js"></script>
</head>
<body style = "background-color: rgb(240, 250, 250)">
<script type = "text/javascript">
mkTbl('50px', 5);
</script>
</body>
</html>
Оба файла (tryTbl.js и tst.html) находятся в одной папке.
Получаемый после открытия обозревателем файла tst.html результат можно наблюдать на рис. 1.
Рис. 1. Таблица с курсором pointer
После удара мышью по ячейке таблицы появится следующее сообщение (рис. 2):
Рис. 2. Выбрана третья ячейка во второй строке таблицы (cellIndex = 2, rowIndex = 1)
В игре Пятнашки все, кроме одной, ячейки таблицы изначально случайным образом заполняются цифрами от 1 до n×n - 1, где n - размер заполняемой таблицы.
Это действие можно выполнить при помощи следующего кода.
function nGmFftngs() {
// mT - размер заполняемой таблицы
fL = mT * mT;
// Массив генерируемых целых чисел
arrF = new Array(fL);
// Массив флажков генерируемых целых чисел
// Элемент arrF2[k] равен false, если число k уже имеется в массиве arrF
arrF2 = new Array(fL);
for (k = 0; k < fL; k ++) {
arrF[k] = 0;
arrF2[k] = true;
}
kL = kL2 = 0;
// Цикл заполнения массива arrF
// Число итераций не более 10000
while (kL < fL && kL2++ < 10000) {
// Получаем целое случайное число из диапазона [1, fL]
k = Math.floor(Math.random() * (fL + 1));
if (k > 0 && arrF2[k - 1]) {
arrF2[k - 1] = false;
arrF[kL] = k;
kL++;
}
}
kL = 0;
// Цикл заполнения таблицы числами из массива arrF
for (i = 0; i < mT; i++) {
rw = tbl.rows[i];
for (j = 0; j < mT; j++) {
k = arrF[kL++];
rw.cells[j].innerHTML = (k > fL - 1) ? "" : k;
}
}
}
Замечание. Взамен innerHTML можно употребить innerText
rw.cells[j].innerText = (k > fL - 1) ? "" : k;
Поместим этот код в уже имеющийся файл tryTbl.js. Пробный вызов функции обеспечит исполнение обозревателем модифицированного файла tst.html:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv = "Content-Type" content = "text/html; charset=windows-1251">
<title>Таблица на JavaScript</title>
<style type = "text/css">
td {
text-indent: 0;
text-align: center;
font-weight: bold;
font-size: 27px;
color: black;
}
</style>
<script type = "text/javascript" src = "tryTbl.js"></script>
</head>
<body style = "background-color: rgb(240, 250, 250)">
<script type = "text/javascript">
mkTbl('50px', 5);
nGmFftngs();
</script>
</body>
</html>
По сравнению с первоначальным вариантом в текущем варианте файла tst.html добавлены определение стиля текста ячейки таблицы и вызов функции nGmFftngs, заполняющей таблицу. Результат приведен на рис. 3.
Рис. 3. Случайное заполнение 5×5-таблицы
Цифра, соседствующая с пустой ячейкой по горизонтали или вертикали, может быть перемещена в эту ячейку. Так, в ситуации, приведенной на рис. 3, в пустую ячейку можно переместить цифру 3, 11, 14 или 15.
В приводимой программе курсор мыши, если он расположен над цифрой соседом, имеет вид pointer, в противном случае курсор отображается как crosshair. Такое поведение обеспечивает обработчик mkCrsr события mouseover, возникающего при позиционировании мыши на ячейке таблицы.
Этот обработчик задается в модифицированной функции mkTbl создания таблицы. В этой функции строка кода
td += " onclick = 'sayRC(this)'></td>";
заменяется на следующую строку:
td += " onmouseover = 'mkCrsr(this)' onclick = 'sayRC(this)'></td>";
Сам же обработчик задается следующими функциями (обе функции помещены в файл tryTbl.js):
function mkCrsr(cll) {
c = cll.cellIndex;
r = gtRw(cll, c);
if (r == -1)
mvd = false;
else
for (kL = 0; kL < 4; kL++) {
switch (kL) {
case 0 : mvd = chkC2(r - 1, c, cll); break;
case 1 : mvd = chkC2(r, c - 1, cll); break;
case 2 : mvd = chkC2(r + 1, c, cll); break;
case 3 : mvd = chkC2(r, c + 1, cll); break;
}
if (mvd) break;
}
tbl.style.cursor = (mvd) ? 'pointer' : 'crosshair';
}
// Возвращает true, если ячейка r2:c2 является пустой
function chkC2(r2, c2, cll) {
if (r2 == -1 || c2 == -1 || r2 == mT || c2 == mT) return false;
cll2 = tbl.rows[r2].cells[c2];
if (cll2.innerHTML == "") return true;
}
Обе формы курсора можно наблюдать на рис. 4.
Рис. 4. Над цифрой 3 курсор имеет вид pointer, а над цифрой 2 - crosshair
Замечание. Курсор, если он расположен вне таблицы, имеет вид default.
Проверка обработчика выполняется в результате открытия файла tst.html.
Цифра, соседствующая по горизонтали или вертикали с пустой ячейкой, занимает эту ячейку при помощи обработчика mvFg события click, возникающего после удара мышью по цифре. Освобождаемая ячейка становится пустой (рис. 5).
Рис. 5. Перемещение цифры 7 обработчиком mvFg события click
Этот обработчик задается в модифицированной функции mkTbl создания таблицы. В этой функции строка кода
td += " onclick = 'sayRC(this)'></td>";
заменяется на следующую строку:
td += " onmouseover = 'mkCrsr(this)' onclick = 'mvFg(this)'></td>";
Обработчик реализуется следующими функциями (обе функции помещены в файл tryTbl.js):
function mvFg(cll) {
c = cll.cellIndex;
r = gtRw(cll, c);
if (r == -1) return;
fL = mT * mT;
for (kL = 0; kL < 4; kL++) {
switch (kL) {
case 0 : mvd = chkC(r - 1, c, cll); break;
case 1 : mvd = chkC(r, c - 1, cll); break;
case 2 : mvd = chkC(r + 1, c, cll); break;
case 3 : mvd = chkC(r, c + 1, cll); break;
}
}
}
function chkC(r2, c2, cll) {
if (r2 == -1 || c2 == -1 || r2 == mT || c2 == mT) return false;
cll2 = tbl.rows[r2].cells[c2];
if (cll2.innerHTML == "") {
cll2.innerHTML = cll.innerHTML;
cll.innerHTML = "";
tbl.style.cursor = 'crosshair';
return true;
}
}
Проверка обработчика выполняется в результате открытия файла tst.html.
В итоговый JavaScript-код добавлены строки, обеспечивающие:
Итоговый JavaScript-код нужно поместить в файл fftngs.js.
// Глобальные переменные
tbl = "";
mT = 0;
dn = false;
tmS = 0;
nFftngs = 0;
//
function mkTbl(tdSz, n) {
mT = n;
td = "<td style = 'background-color:#f0f0f0; width:" + tdSz + "; height:" + tdSz + "'";
td += " onmouseover = 'mkCrsr(this)' onclick = 'mvFg(this)'></td>";
tHdr = "<table id = 'tbl' style = 'background-color:#ccccaa; cursor:pointer'; border = 1px";
document.write(tHdr);
for (i = 0; i < mT; i++) {
document.write("<tr>");
for (j = 0; j < mT; j++) {
document.write(td);
}
document.write("</tr>");
}
document.write("</table>");
}
function sayRC(cll) {
// Номер столбца текущей ячейки
c = cll.cellIndex;
r = gtRw(cll, c);
alert("Ячейка " + r + ":" + c);
}
// Возвращает номер строки, которой расположена ячейка cll
function gtRw(cll, c) {
for (i = 0; i < mT; i++) {
rw = tbl.rows[i];
if (rw.cells[c] == cll) return i;
}
}
function nGmFftngs() {
dn = false;
nFftngs = 0;
dt = new Date();
tmS = dt.getTime();
// mT - размер заполняемой таблицы
fL = mT * mT;
// Массив генерируемых целых чисел
arrF = new Array(fL);
// Массив флажков генерируемых целых чисел
// Элемент arrF2[k] равен false, если число k уже имеется в массиве arrF
arrF2 = new Array(fL);
for (k = 0; k < fL; k ++) {
arrF[k] = 0;
arrF2[k] = true;
}
kL = kL2 = 0;
// Цикл заполнения массива arrF
// Число итераций не более 10000
while (kL < fL && kL2++ < 10000) {
// Получаем целое случайное число из диапазона [1, fL]
k = Math.floor(Math.random() * (fL + 1));
if (k > 0 && arrF2[k - 1]) {
arrF2[k - 1] = false;
arrF[kL] = k;
kL++;
}
}
kL = 0;
// Цикл заполнения таблицы числами из массива arrF
for (i = 0; i < mT; i++) {
rw = tbl.rows[i];
for (j = 0; j < mT; j++) {
k = arrF[kL++];
rw.cells[j].innerHTML = (k > fL - 1) ? "" : k;
}
}
}
function mkCrsr(cll) {
if (dn) mvd = false;
else {
c = cll.cellIndex;
r = gtRw(cll, c);
if (r == -1)
mvd = false;
else
for (kL = 0; kL < 4; kL++) {
switch (kL) {
case 0 : mvd = chkC2(r - 1, c, cll); break;
case 1 : mvd = chkC2(r, c - 1, cll); break;
case 2 : mvd = chkC2(r + 1, c, cll); break;
case 3 : mvd = chkC2(r, c + 1, cll); break;
}
if (mvd) break;
}
}
tbl.style.cursor = (mvd) ? 'pointer' : 'crosshair';
}
// Возвращает true, если ячейка r2:c2 является пустой
function chkC2(r2, c2, cll) {
if (r2 == -1 || c2 == -1 || r2 == mT || c2 == mT) return false;
cll2 = tbl.rows[r2].cells[c2];
if (cll2.innerHTML == "") return true;
}
function mvFg(cll) {
if (dn) return;
c = cll.cellIndex;
r = gtRw(cll, c);
if (r == -1) return;
fL = mT * mT;
for (kL = 0; kL < 4; kL++) {
switch (kL) {
case 0 : mvd = chkC(r - 1, c, cll); break;
case 1 : mvd = chkC(r, c - 1, cll); break;
case 2 : mvd = chkC(r + 1, c, cll); break;
case 3 : mvd = chkC(r, c + 1, cll); break;
}
// Проверяем, завершена игра или нет
if (mvd) {
// Переменная dn будет равна true, если игра завершена
dn = true;
k = 0;
for (i = 0; i < mT; i++) {
rw = tbl.rows[i];
for (j = 0; j < mT; j++) {
k++;
if (k < fL && rw.cells[j].innerHTML != k) {
dn = false;
break;
}
}
if (!dn) break;
}
if (dn) sRslt();
break;
}
}
}
function chkC(r2, c2, cll) {
if (r2 == -1 || c2 == -1 || r2 == mT || c2 == mT) return false;
cll2 = tbl.rows[r2].cells[c2];
if (cll2.innerHTML == "") {
nFftngs++;
cll2.innerHTML = cll.innerHTML;
cll.innerHTML = "";
tbl.style.cursor = 'crosshair';
return true;
}
}
// Выстраивает кости в надлежащем порядке
function bldFftngs() {
if (dn) return;
k = 0;
for (i = 0; i < mT; i++) {
rw = tbl.rows[i];
for (j = 0; j < mT; j++) rw.cells[j].innerHTML = (++k > 15) ? "" : k;
}
dn = true;
sRslt();
}
// Показывает потраченные время и число ходов
function sRslt() {
dt = new Date();
tm = Math.round(0.001 * (dt.getTime() - tmS));
alert("Игра завершена. Число ходов " + nFftngs + ". Затрачено времени " + tm + " с.");
}
Переход к игре осуществляется в результате открытия файла fftngs.html, воспроизводящего описание игры (рис. 6), ее инициализацию и завершение.
Рис. 6. В обозревателе открыт файл fftngs.html
Файл fftngs.html содержит следующий код:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv = "Content-Type" content = "text/html; charset=windows-1251">
<title>Пятнашки</title>
<style type = "text/css">
td {
text-indent: 0;
text-align: center;
font-weight: bold;
font-size: 27px;
color: black;
}
.dv {
width: 400px;
height: 300px;
position: relative;
top: 0;
left: 0;
background-color: white;
float: left;
margin-left: 6px;
margin-right: 6px;
}
p {
margin-top: 6px;
margin-bottom: 6px;
font-size: 12pt;
}
.btn {
font-size: 14px;
font-weight: 400;
font-family: "Times New Roman", serif;
width: 80px;
height: 25px;
margin-right: 4px;
}
</style>
<script type = "text/javascript" src = "fftngs.js"></script>
</head>
<body>
<div class = "dv">
<p><b>Пятнашки</b></p>
<p>Расположите числа в порядке возрастания,</p>
<p>поместив первые 4 в первой строке таблицы,</p>
<p>следующие 4 - во второй строке таблицы и так далее</p>
<p>Для перемещение числа используйте мышиный удар.</p>
<br>
<p>
<input type = "button" class = "btn" value = "Новая" onclick = "nGmFftngs()" title = "Начни сначала">
<input type = "button" class = "btn" value = "Завершить" onclick = "bldFftngs()" title = "Получить решение">
</p>
</div>
<script type = "text/javascript">
mkTbl("50px", 4);
tbl = document.getElementById("tbl");
nGmFftngs();
</script>
</body>
</html>
После завершения игры функция sRslt обеспечит приведенное на рис. 7 сообщение.
Рис. 7. Игра окончена
Замечание. Игру Пятнашки можно опробовать на странице сайта 100byte.ru.
Html-таблица является достаточно сложным объектом. В частности, ее ячейка (HTMLElement td) имеет следующие события, методы и свойства (их детальное описание можно найти в [2]):