Профессиональный веб-дизайн: Введение в современные веб-технологии "3. Создание внешней таблицы. Теперь создайте таблицу с двумя строками. Во второй строке создайте две ячейки - в первую переместите таблицу цифр, а во вторую - таблицу знаков." Как в ячейку <td> поместить таблицу? Таблица же сама состоит из ячеек. Исходя из задания следует, что <td> может быть родителем для <td>, но это противоречит правилам HTML? |
Динамический HTML
В данной работе будет создана страница формирования клиентом заказа из набора продуктов. При выборе клиентом продукта, указании количества единиц и нажатии кнопки "Добавить в корзину" к таблице заказа добавляется пункт. Кнопка "Удалить" позволяет удалять пункты из заказа по одному или группой. При всех манипуляциях подсчитывается число единиц заказанных товаров и их суммарная стоимость.
Часть 1. Создание разметки
Статическая часть страницы будет иметь следующий вид ( рис. 15.1).
Рассмотрим действия по созданию этой формы пошагово.
1. Создание выпадающего списка (элемент select).
В теле нового документа с именем Lab5.htm создайте элемент div, в котором будет размещена вся разметка данного примера.
Первый элемент - select - должен содержать несколько групп ( optgroup ) по несколько пунктов ( option ). Каждый пункт содержит наименование какого-либо продукта, а его атрибут value - цену (произвольное в данном случае число).
В развёрнутом виде список должен выглядеть так:
Назначьте списку атрибут id="lstProducts".
2. Создание остальной разметки
Следом за выпадающим списком создайте два элемента ввода - input type="text" и input type="button" (примеры приведены в лекции). Текстовому элементу ввода назначьте атрибут id="txtQty", а кнопке - onclick="AddToCart()".
Последний элемент разметки - таблица с заголовком (элемент caption), верхним и нижним колонтитулом (элементы thead и tfoot) и телом (элемент tbody). Описания и примеры применения этих элементов также см. в соответствующей лекции. Таблице назначьте атрибут id="tblOrder".
Раздел tbody оставьте пустым (он будет формироваться динамически), а в колонтитулах, как видно на рисунке, следует расположить по одному элементу управления - checkbox и button. Им сразу задайте обработчики события click следующим образом:
<input type="checkbox" onclick="ToggleCheck(this)" /> <input type="button" value="Удалить отмеченные" onclick="RemoveSelected()" />
Часть 2. Создание сценария, манипулирующего таблицей
1. Добавление пунктов заказа
Создайте скрипт в конце тела документа, и сразу определите обработчики, назначенные элементам управления в предыдущем задании - пока пусть это будут функции с пустым телом.
В начало скрипта поместите следующие команды:
var tbl = document.getElementById('tblOrder'); var oList = document.getElementById('lstProducts');
Ссылки на элементы 'tblOrder' (таблица заказа) и lstProducts (список выбора продукта) будут часто использоваться в коде сценария, поэтому целесообразно определить их единожды.
При нажатии кнопки "Добавить в корзину" в таблицу должны добавляться строки, соответствующие пунктам заказа:
Функция-обработчик этой кнопки была названа AddToCart. Изучите её код и добавьте его в скрипт.
/* Добавление пунктов заказа */ function AddToCart() { /* Определяем значение, введённое в текстовое поле */ var qty = document.getElementById('txtQty').value; /* Проверка: распознаётся ли значение как число? Если нет, считаем единицей */ if (parseFloat(qty) != qty) qty = 1; /* Вставляем строку в тело таблицы */ var oRow = tbl.tBodies[0].insertRow(-1); /* В добавленную строку вставляем, во-первых, checkbox */ oRow.insertCell(-1).innerHTML = '<input type="checkbox">'; /* во-вторых, текст, взятый из списка выбора продуктов */ oRow.insertCell(-1).innerHTML = oList.options[oList.selectedIndex].text; /* в-третьих, цена выбранного продукта */ oRow.insertCell(-1).innerHTML = oList.value; /* далее, количество, указанное в текстовом поле */ ... /* затем стоимость пункта заказа */ ... /* и, наконец, кнопку "Удалить" */ ... /* По окончании вставки строки необходимо пересчитать сумму заказа */ //Calculate(); }
Код в тех строках, где оставлено многоточие, напишите самостоятельно, опираясь на комментарии. Вызов функции Calculate() оставьте пока закомментированным - эту функцию мы добавим позже, а сначала следует добиться верной работы AddToCart().
2. Отладка сценария.
Лучший способ проследить ход работы сценария - это выполнить его пошагово в отладчике. IE8 предлагает встроенный отладчик в наборе средств разработчика (Меню Сервис - Средства разработчика). Для некоторых других браузеров также существуют отладчики, доступные как плагины - например, FireBug.
Откройте страницу в IE8 и запустите Средства разработчика. На вкладке Сценарий имеются кнопки для запуска отладки и пошагового выполнения команд сценария. На правой панели можно просмотреть локальные переменные (определённые в текущей функции), а также произвольные выражения, включающие объекты модели документа, их свойства и даже вызовы методов.
Поставьте точку останова на начало интересующей нас функции и запустите отладку. Нажав кнопку "Добавить в корзину" на веб-странице, вы тем самым запустите обработчик события click, и отладчик покажет команду в точке останова. Двигайтесь пошагово, наблюдая за всеми объектами, влияющими на логику обработчика.
Отладка помогает выявить те ошибки в программе, которые появляются вследствие неточного понимания смысла тех или иных свойств и методов объектов модели документа - всегда можно посмотреть, чему в действительности равно то или иное свойство, или что выдаёт тот или иной метод. Также можно выявить ошибки, связанные с логикой программы - запускается ли функция при наступлении события, верно ли сформулированы условный и циклический операторы и, наконец, если возникает исключение, то на какой команде и в каком контексте. Но те ошибки, которые связаны с синтаксисом, отладчик вряд ли поможет найти - во многих случаях браузер просто не поймёт сценария с такими ошибками. Особенно тщательно следует следить за соответствием скобок и тэгов, а также символами-разделителями.
3. Функция Calculate.
Создайте функцию Calculate() и уберите комментарий с её вызова в функции AddToCart.
function Calculate() { /* Счётчики для количества единиц товара и общей стоимости */ var qty = 0, amount = 0; /* Цикл по всем строкам в теле таблицы */ for (var i = 0, n = tbl.tBodies[0].rows.length; i < n; i++) { /* Увеличиваем qty на значение в 3 столбце текущей строки */ qty += parseFloat(tbl.tBodies[0].rows[i].cells[3].innerHTML); /* Увеличиваем amount на значение в 4 столбце текущей строки */ amount += parseFloat(tbl.tBodies[0].rows[i].cells[4].innerHTML); } /* Записываем qty в 3 столбец нижнего колонтитула */ ... /* Записываем amount в 4 столбец нижнего колонтитула */ ... }
Код в тех строках, где оставлено многоточие, напишите самостоятельно, опираясь на комментарии.
4. Функция RemoveProduct(elem).
Кнопка "Удалить", динамически вставляемая в правую ячейку каждой строки заказа, должна выполнять свою работу - следовательно, необходимо назначить ей обработчик (назовём его RemoveProduct ) и написать его код. Этот обработчик имеет существенное отличие от предыдущих, которое заключается в том, что смысл его работы зависит от контекста вызова: удалять нужно именно ту строку, в которой кнопка расположена. Таким образом, обработчик должен принимать параметр, определяющий контекст. Это можно сделать различными путями, и здесь мы предлагаем наиболее типичный. Если назначить кнопке обработчик следующим образом: onclick="RemoveProduct(this)", то функция RemoveProduct получит в качестве параметра ссылку на тот объект, который принял событие (в данном случае - щелчок мыши). Очевидно, обладая ссылкой на элемент в таблице, можно каким-то образом определить номер ( index ) строки таблицы, в которой он находится, и применить метод таблицы deleteRow(index). Отношение между строкой и кнопкой "Удалить" такое: строка является контейнером ячейки, которая является контейнером кнопки. Ссылку на контейнер элемента можно получить при помощи свойства parentNode этого элемента. С учётом этих соображений код рассматриваемой функции примет следующий вид:
function RemoveProduct(elem) { tbl.deleteRow(elem.parentNode.parentNode.rowIndex); Calculate(); }
Вставьте эту функцию в скрипт (и не забудьте правильно описать её вызов в динамическом определении кнопки - см. функцию AddToCart ). Сохраните документ, обновите страницу в браузере и проверьте добавленную функциональность.
5. Функция RemoveSelected.
Код этой функции представлен ниже, и на нём следует остановиться подробнее, поскольку работа по удалению элементов из множества имеет свои тонкости.
function RemoveSelected() { /* находим все элементы input в теле таблицы */ var checks = tbl.tBodies[0].getElementsByTagName('input'); var i = 0; /* начинаем перебор элементов в цикле */ while (i < checks.length) { /* рассматриваем элемент лишь в том случае, если это checkbox и он отмечен */ if (checks[i].type == 'checkbox' && checks[i].checked) /* вызываем функцию, которая удалит строку с пунктом заказа - передаём ей ссылку на checkbox */ RemoveProduct(checks[i]); else /* счётчик увеличиваем лишь в том случае, если удаление не было сделано */ i++; } }
Множество ссылок на все элементы ввода ( input ) внутри тела таблицы ( tbody ) легко получить при помощи метода getElementsByTagName. Этот метод принимает в качестве параметра имя тэга и выдаёт массив ссылок. Важно отметить, что ссылками являются не только элементы массива, но и сам массив - ссылочный, т.е. все изменения в документе автоматически отражаются в этом массиве. Таким образом, удалив строку из таблицы (и тем самым удалив из неё два элемента input - checkbox и button ), мы тем самым уменьшим число элементов в массиве checks на 2. Это следует принимать во внимание при циклической обработке массива.
Номер рассматриваемого в цикле элемента массива обозначен в данном примере переменной i, и отсчёт, как обычно, начинается с нуля. Чтобы правильно сформулировать оператор увеличения этого i, следует задаться вопросом: "Рассмотрев элемент с номером i, элемент с каким номером следует рассматривать далее?" Ответ таков: "Если удаление не было выполнено, то i+1 ; иначе - вновь i (теперь это номер следующего из оставшихся элементов)". Поэтому цикл записан именно таким образом (а не с использованием оператора for ).
6. Функция ToggleCheck.
Последняя функция данного сценария создаёт дополнительное удобство: можно отметить или сбросить сразу все галочки в пунктах заказа, щёлкнув по галочке (элементу checkbox ) в заголовке таблицы. Этому элементу назначена функция-обработчик ToggleCheck, принимающая в качестве параметра this, т.е. ссылку на объект-источник события. Напишите эту функцию самостоятельно - все необходимые для этого приёмы уже рассмотрены.
Окончательный вид работающей страницы показан на рис. 15.2.