oldi

Вариации на тему электронного магазина

Виталий Сизов

Введение

Представление товаров

   Раскрывающиеся окна

   Списки

   Таблицы с произвольной структурой

Выбор товаров

   Формы в pop-up-окнах

   Функция выбора товара

Введение

Электронная коммерция с использованием Web-страниц является одним из наиболее бурно развивающихся направлений Всемирной сети. Еще год назад большинство сайтов коммерческих предприятий носили исключительно рекламный характер, а сегодня любая уважающая себя компания предоставляет возможность совершения сделок купли-продажи в интерактивном режиме.

Конечно, существует специфика продажи разных товаров и услуг. Решения, которые подходят для книжного магазина, совершенно неприемлемы для машиностроительной компании, изготавливающей сложные агрегаты в небольшом ассортименте. Поэтому владельцы корпоративных сайтов всегда будут искать новые решения с учетом особенностей собственной продукции и размеров инвестиций в электронную торговлю.

Настоящая статья написана с целью помочь предприятиям, начинающим или только планирующим начать электронную торговлю, не располагающим ни собственными серверами, ни квалифицированным персоналом, который освоил технологию «клиент-сервер» для Internet, создать простые, но эффективные электронные магазины и включиться в мировой процесс электронной коммерции.

Слова об освоении технологии «клиент-сервер» могут показаться парадоксальными, поскольку все существующие реализации электронных магазинов основаны именно на этой технологии, требующей полноценного доступа к программному обеспечению сервера со стороны разработчиков. Интерактивный характер взаимодействия пользователя с Web-страницами электронного магазина означает немедленную реакцию системы на манипуляции человека. Такая реакция достигается с помощью следующего механизма. Интерактивные Web-страницы обычно включают элементы управления и формы ввода, формирующие запросы к серверу. Запрос представляет собой символьную строку, содержащую имя процедуры обработки и передаваемые ей параметры. Имя процедуры задается в форме URL, используемой для перехода к любой Web-странице Сети. Но адресуемые таким образом страницы на сервере не являются обычными HTML-документами. Это специальные программы, призванные анализировать полученные параметры и динамически формировать новые Web-страницы, в которых тем или иным способом отражается реакция на действия пользователя. Сгенерированные Web-страницы отправляются клиенту и открываются в браузерах так же, как и обычные страницы, подготовленные заранее. Серверные программы, принимающие запросы, могут быть написаны на языке Perl и размещаться в специальном каталоге с зарезервированным именем /cgi-bin/ на серверах UNIX. Это могут быть либо страницы с расширением ASP (Active Server Pages) на серверах Microsoft, либо страницы PHP, использующие специальные команды гипертекстового препроцессора. В любом случае требуются знания специального программного обеспечения сервера, наличие права доступа к нужным ресурсам, что автоматически влечет за собой необходимость их оплаты.

Таким образом, начинающий предприниматель в области электронной коммерции, еще не приступив к торговле, вынужден нести затраты, связанные с внедрением серверных технологий. Причем не только материальные, но и методологические, обусловленные необходимостью ограничиваться только теми средствами, которые может предоставить конкретный сервер.

Вместе с тем возможностей чисто «клиентского» программного обеспечения зачастую оказывается достаточно для реализации полноценного электронного магазина. Рассмотрим этот вопрос подробнее. Электронный магазин прежде всего должен иметь некий прейскурант товаров или услуг, предлагаемых для продажи. Это может быть как простой «прайс-лист», так и целая база данных, содержащая подробные описания, включающие графические и мультимедийные элементы. Другим необходимым компонентом электронного магазина является «покупательская корзина» — среда, в которой накапливается информация об отобранных товарах и суммируется общая стоимость покупки. Третий компонент электронного магазина — это процесс фиксации сделки: invoicing, сопровождаемый формированием документов для покупателя и продавца и влекущий за собой юридические последствия — обязательства оплаты и поставки товара. И наконец, последний компонент — автоматическая оплата покупки с использованием средств электронных платежей.

База данных электронного магазина, в отличие от баз данных иного назначения, имеет одну интересную особенность, которая заключается в том, что все элементы подобной базы данных целесообразно постоянно выставлять на обозрение пользователю, не дожидаясь специального запроса с его стороны. Товар должен находиться на «полках» для всеобщего обозрения, а не пылиться в «кладовках» (на сервере). Поэтому длинные линейные списки товаров с короткими описаниями, которые потенциальный покупатель может продолжительное время просматривать в автономном режиме, предпочтительнее глубокой древовидной структуры, состоящей из множества динамических страниц, формируемых на сервере по уточняющим запросам. Навигация по ветвям такого дерева аналогична постоянным обращениям к продавцу «подпольной» торговой точки с вопросами о наличии того или иного товара. Понятно, что покупатель предпочтет супермаркет с огромными витринами и видимым разнообразием товаров, тем более что «бродить» по такому магазину можно совершенно бесплатно, не занимая телефонную линию или другие каналы связи. А раз так, то базу данных (или значительную ее часть) целесообразно перенести на клиентскую сторону, сведя обращения к продавцу (программам на сервере) до минимума.

Перемещение базы данных на компьютер клиента влечет за собой необходимость переноса туда же и процедур формирования и редактирования покупательской корзины. Вполне естественно заставить «корзину на колесиках» следовать за покупателем в его странствиях вдоль витрин с товарами и разрешить ее произвольное редактирование также в автономном режиме. Кстати, другое решение, основанное на серверных процедурах, потребовало бы гораздо более сложных алгоритмов, предусматривающих ведение базы данных клиентов и их транзакций на сервере. Все это заметно снижает время реакции системы.

Invoicing как заключительный процесс покупки, безусловно, требует обращения к серверу — но только однажды и в самый последний момент. До этого на стороне клиента могут быть выполнены все подготовительные операции, включая печать счета, если такая возможность предусмотрена. Но обращение обращению рознь. При окончательной фиксации факта покупки от продавца не требуется даже квитанции — она уже была сформирована. Единственное, что необходимо, — это уведомить владельца товара о том, что сделан заказ на определенную сумму и покупатель готов заплатить деньги. А использовать для этого серверные технологии вовсе не обязательно. Ведь сообщение направляется только в одну сторону. С подобной задачей легко может справиться и электронная почта. Тем более что покупателю безразлично, как скоро продавец получит его заказ. Важно только обеспечить доставку товаров в срок.

Наконец, последний момент — использование электронных систем оплаты. Здесь уж без серверной поддержки не обойтись. Но только потому, что хочется поскорее проверить платежеспособность клиента и снять необходимую сумму с его счета. А это уже удовольствие для продавца, за которое нужно платить. Если же проявить выдержку и отказать себе в подобном удовольствии, дожидаясь, пока покупатель сам переведет деньги, то вопросы приобретения сервера и освоения непростых технологий можно отложить до того момента, когда прибыль, полученная от электронной торговли, окажется достаточной для новых инвестиций.

В начало В начало

Представление товаров

Любую совокупность товаров или услуг можно представить в виде простой линейной структуры, состоящей из описаний — дескрипторов. В общем случае дескрипторы для различных товаров будут иметь разные структуру и форматы представления информации. Единственное, что объединяет любые товары или услуги, это наличие наименования, цены и единицы измерения. Эти три реквизита являются ключевыми для оформления сделки купли-продажи. Именно они фигурируют в финансовых документах, и рассматривать их следует в качестве неделимого идентификатора товара. Если представить такую базу данных в виде гипертекста, то получится одна страница со списком идентификаторов, служащим оглавлением, и множество страниц подробных описаний, связанных с этим оглавлением с помощью гиперссылок. Таким образом, к трем упомянутым выше обязательным реквизитам добавляется четвертый — адрес домашней страницы товара.

Если товаров много, то оглавление, или «прайс-лист», становится очень длинным. Тогда возникает потребность представления его в виде дерева. Появляются товарные группы, делящие общий список по какому-нибудь признаку. Что касается домашних страниц, то для некоторых товаров их вообще можно не создавать, ограничившись лишь информацией из оглавления.

Ниже рассматриваются проблемы, возникающие при таком представлении товаров в электронном магазине.

В начало В начало

Раскрывающиеся окна

При наличии длинных списков товаров возникает противоречие. С одной стороны, для удобства навигации покупателя во множестве товаров необходимо использовать иерархическое представление в виде товарных групп. С другой стороны, желательно сократить обращение к серверу за новыми страницами, чтобы обеспечить продолжительную работу в автономном режиме. Выход может быть найден путем представления иерархических структур на одной странице. В этом случае можно руководствоваться не логической группировкой товаров, а размерами Web-страницы, открываемой на стороне клиента. При современных скоростях модемов вполне допустимыми можно считать страницы размером от 50 до 200-300 Кбайт. А это позволяет представить многие сотни и даже тысячи наименований товаров на одной странице. Другое дело, что товары, попавшие в конец списка, могут оказаться незамеченными, если пользователю надоест прокручивать страницу.

Чтобы этого не случилось, существует хорошо известный прием, применяемый в текстовых редакторах при работе с длинными документами. Этот прием заключается в представлении текста в виде структуры — outline, позволяющей раскрывать или скрывать ветви дерева единственным щелчком мыши.

Аналогичная структура может быть реализована и в HTML-документе. Разработчики, использующие Microsoft FrontPage, наверняка знакомы с элементами JavaScript dynamicoutline, который может быть автоматически добавлен к любым спискам <UL> или <OL>, что превращает обычную страницу в DHTML-страницу.

Более удобная структура для представления на одной странице большого количества разнородной информации может быть также заимствована у Microsoft — из новейшей технологии Digital Dashboard. Суть этого способа заключается в том, что на странице создается большое количество динамических окон — nuggets, раскрывающихся и свертывающихся при нажатии кнопки. В отличие от настоящих окон Windows, окна nuggets не перекрываются и не перетаскиваются. При открытии таких окон происходит смещение всей информации, расположенной ниже окна, а сама HTML-страница удлиняется. Обычно в момент загрузки все окна закрыты, что позволяет быстро ознакомиться со всеми разделами Web-страницы. Если товарные группы заключить в nuggets, то ни одна из них не останется незамеченной, пользователь сможет быстро перейти к интересующему его разделу и раскрыть детальный список товаров этой группы. И все это в автономном режиме.

Технически окна nuggets состоят из двух частей: заголовка с названием окна и кнопкой minimize / maximize и тела, содержащего основную информацию.

Код заголовка показан ниже. Важно отметить, что название окна часто служит гиперссылкой на домашнюю страницу, содержащую описание. В нашем случае это может быть описание товарной группы:

<table cellpadding="0" border="0" cellspacing="0"  
 class="nuggetHeader">  
  <tr>  
    <td nowrap width="100%" valign="center"   
     class="nuggetTitle" >  
      <a href="gruppa.htm" class="nuggetTitleText">  
       Название товарной группы</a>  
    </td>  
    <td nowrap class="nuggetButtonWrapper">  
      <img id="Toggle" src="images/blank.gif"  
       title="Click to maximize / minimize"  
       onClick="document.all.nContent.style.display  
       =document.all.nContent.style.display  
       =='none'?'':'none';  
       if(document.images)this.src  
       =document.all.nContent.style.display  
       =='none'?imgMax.src:imgMin.src;"  
       WIDTH="17" HEIGHT="17">  
    </td>  
  </tr>  
</table>

Тело nugget представляет собой произвольный код, чаще всего таблицу, заключенную в именованный block-level-контейнер <DIV></DIV>:

<div valign="top" id="nContent">  
  <table width="100%" border="0" cellpadding="0"  
   cellspacing="0" class="nuggetBody">  
    <tr>  
      <td>  
        Произвольная информация  
      </td>  
    </tr>  
  </table>  
</div>

Окна nuggets можно располагать на странице в несколько колонок. На работоспособность конструкции это не повлияет. Однако в случае длинных страниц этого делать не следует, поскольку браузеры по-разному отображают таблицы. Некоторые из них не выводят на экран ничего до тех пор, пока во входном потоке не встретится закрывающий таблицу тэг </TABLE>. Поэтому если изрядное количество nuggets поместить внутри глобальной таблицы, то пользователь до конца загрузки всей страницы будет любоваться пустым экраном.

Окна nuggets обычно обеспечивают только два уровня иерархии на странице. Чаще всего этого бывает достаточно. При необходимости введения более высоких уровней можно использовать отдельные страницы, упоминавшийся выше JavaScript dynamicoutline или комбинированные решения.

В начало В начало

Списки

Особенностью приложений типа электронного магазина является то, что все они предполагают использование базы данных. При этом неважно, где физически расположена база данных и как именно происходит обращение к ней. Важно другое. Операции купли-продажи должны многократно обращаться к объектам, представляющим товар — сначала при отборе товаров в покупательскую корзину, затем при редактировании корзины или формировании счета. Поэтому в электронном магазине желательно предусмотреть явное создание коллекции объектов «товары». Конечно, в Internet Explorer можно попытаться извлекать информацию непосредственно из элементов тела документа, используя, например, свойства .innerText, тогда как в Netscape Navigator это невозможно принципиально. Хорошо, если коллекция товаров допускает простое представление в виде структурированных записей. Тогда описание такой структуры можно использовать и для определения коллекции объектов в программах. Например, во многих случаях запись о товаре, кроме упоминавшихся выше полей: наименование, единица измерения, цена и адрес домашней страницы, может быть дополнена несколькими типичными реквизитами: номер по каталогу, примечание и фирма-производитель. Если на примечание не накладывать ограничений, а напротив — разрешить использовать в нем любые коды HTML, то подобная структура записи может оказаться вполне универсальной.

Чтобы описать класс и создать коллекцию новых объектов, в HTML-документе используется массив. Например, следующий код JavaScript создает коллекцию объектов goods (товары) со свойствами .len — количество элементов в коллекции и .maxlen — максимальное количество элементов:

var MAX_ITEM = 1  
   
function MakeArray(n)  
{  
  for (var i = 1; i <= n; i++) {  
    this[i] = 0  
  }  
  this.maxlen = n  
  this.len = 0  
  return this  
}  
   
var goods = MakeArray(MAX_ITEM)

В приведенном примере предполагается, что свойство .maxlen не будет использоваться и коллекция goods не будет иметь ограничений на количество членов. Если это не так — при вызове функции MakeArray необходимо поменять значение константы MAX_ITEM.

Чтобы иметь возможность задавать значения свойствам отдельных объектов коллекции, используется следующая вспомогательная функция:

function Item(code, name, home, unit, price, comment)  
{  
  this.code = code       // код товара  
  this.name = name       // наименование  
  this.home = home       // адрес домашней страницы  
  this.unit = unit       // единица измерения  
  this.price = price     // цена  
  this.comment = comment // примечание  
}  

Теперь можно создать функцию — метод, добавляющий объекты в коллекцию:

function Add(code, name, home, unit, price, comment)  
{  
  goods.len++  
  goods[goods.len] = new Item(code,  
    name, home, unit, price, comment)  
}  

После этих несложных приготовлений огромную базу данных можно загрузить в браузер с помощью простой последовательности вызовов метода Add:

Add(код товара,  
  "наименование товара",  
  "IP-адрес домашней страницы товара",  
  "единица измерения",  
  цена товара,  
  "комментарий, содержащий тэги HTML")  

Показанная выше команда является оператором JavaScript. Начиная с четвертой версии Internet Explorer и в любой версии Netscape Navigator коды JavaScript можно размещать во внешних текстовых файлах, с расширением .JS. Это очень удобно для нашей задачи. Всю базу данных можно представить в виде достаточно компактного текстового файла и подгружать к Web-странице актуальную версию с помощью следующего кода:

<script TYPE="text/javascript"   
 LANGUAGE="JavaScript" src="price_list.js">  
</script>  

Поскольку избыточность текстового файла по сравнению с HTML-документом аналогичного содержания значительно меньше, то появляется возможность вместить в файл приемлемых размеров еще больше информации.

Наличие предварительно загруженной базы данных дает много преимуществ. Для извлечения объекта из коллекции товаров при операции покупки достаточно просто указать его индекс и скопировать значения свойств новому объекту в коллекции «покупательская корзина». В базе данных можно даже реализовать любые алгоритмы поиска и сортировки. Однако у такого решения есть и оборотная сторона.

База данных хоть и загружена в память, но пока еще не представлена пользователю ни в виде nuggets, ни в виде обычной таблицы. Но разрабатывать обычный HTML-документ только для отображения списка товаров просто нелепо. Единственный выход в подобной ситуации — это генерировать тело Web-страницы динамически, с использованием функций браузера write() или writeln().

function DisplayList()  
{  
  for (i = 1; i <= goods.len; i++) {  
    document.writeln("<tr>")  
    if (goods[i].home != "") {  
      document.writeln("<td><a target=\"_blank\"  
       href=\"" + goods[i].home + "\">"   
       + goods[i].name + "</a></td>")  
    } else {  
      document.writeln("<td>" + goods[i].name + "</td>")  
    }  
    document.writeln("<td>" + goods[i].unit + "</td>")  
    document.writeln("<td align=\"right\">"   
     + goods[i].price + "</td>")  
    document.writeln("<td><a href=\"javascript:void(0)\"  
     onClick=\"BasketInsert(" + i +")\">Buy Now!</a></td>")  
    document.writeln("</tr>")  
    if (goods[i].comment != "") {  
      document.writeln("<tr>")  
      document.writeln("<td colspan=\"4\">"   
       + goods[i].comment + "</td>")  
      document.writeln("</tr>")  
    }  
  }  
} 

Приведенный пример не является законченной функцией. Здесь опущены команды вывода заголовков таблиц и завершающих тэгов. Сделано это намеренно, поскольку в действительности в процессе обработки коллекции товаров должны будут обрабатываться специальные записи, разделяющие товарные группы. В этом случае необходимо формировать заголовки nuggets и тэги <DIV></DIV>, начинать и заканчивать внутренние таблицы, присваивать уникальные имена для элементов и делать многое другое. Поэтому реальная функция будет гораздо сложнее.

Однако на приведенном примере можно проанализировать, как осуществляется обработка типичных ситуаций, связанных непосредственно с записями товаров.

Прежде всего необходимо отметить, что свойство .code — код товара — вообще не отображается. Это внутренняя информация, и покупателю она ни к чему. Наименование и адрес домашней страницы объединяются в единое целое. Если адрес домашней страницы присутствует, то формируется элемент <A>, в противном случае наименование не будет оформляться как гиперссылка.

Примечание выводится в отдельную строку таблицы, занимая четыре объединенных ячейки (параметр colspan). Таким образом, если запись о товаре не содержит примечания, то она занимает одну строку регулярного списка. Если же примечание предусмотрено, то оно выводится ниже основной строки и может занимать столько места, сколько потребуется для воспроизведения своего HTML-кода. В свою очередь, HTML-код примечания может содержать любые inline- и block-level-элементы, например таблицы с картинками.

Самое важное, что в процессе формирования динамической страницы к каждой строке списка товаров добавляется элемент управления, с помощью которого будет осуществляться покупка. В приведенном примере это элемент <A> со словами «Buy Now!», но можно использовать и элемент <IMG> с изображением привычной «корзины на колесиках». Щелчок мышью по этому элементу вызывает обращение к функции BasketInsert, к проектированию которой мы приступим позже. В качестве параметра этой функции передается индекс объекта в коллекции goods, однозначно определяющий все сведения, необходимые для того, чтобы поместить выбранный товар в покупательскую корзину.

В начало В начало

Таблицы с произвольной структурой

Несмотря на всю простоту и компактность передаваемых по сети файлов, динамические страницы со списками можно использовать не всегда. Выигрыш, полученный за счет коротких файлов, во многом теряется из-за медлительности браузеров, интерпретирующих функции write/writeln. Как ни парадоксально, этим особенно грешит Netscape Navigator, считающийся «быстрым» браузером. А ведь пользователю не объяснишь, что он давно уже мог бы отключится от Internet, несмотря на то что процесс отображения длинной страницы на экране не закончен.

Иногда специфика продукции или дизайн страницы не допускает представления товаров в виде списка структурированных записей. В этом случае можно пожертвовать скоростью передачи файлов и простотой программирования за счет более выразительного и привлекательного внешнего вида страниц электронного магазина. Более того, если вместо динамических использовать обычные Web-страницы, подготовленные заранее, то это не вызовет у пользователя недоумения по поводу непривычного процесса медленного «прорисовывания» документа с помощью функций write/writeln.

В статических страницах можно применить те же приемы иерархического представления данных, что и в DHTML со списками. Например, использовать окна nuggets для выделения товарных групп. В этом случае каждое тело nugget может быть представлено совершенно произвольной таблицей.

Однако за такую «свободу» дизайна придется расплачиваться. В варианте со списками исходные данные хранились в коллекции хорошо определенных объектов, допускающих простое обращение. При произвольном представлении данных такую коллекцию создавать бессмысленно. Но ведь в электронном магазине товары нужно не только демонстрировать, но и продавать. Выходом в этой ситуации является усложнение функции, вызываемой при выборе товара. Вспомним, что в варианте со списками функция BasketInsert оперировала с индексом объекта в коллекции. При добавлении нового объекта в корзину его свойства можно было скопировать у объекта goods[i]. Теперь коллекции goods не существует. А раз так, то функция BasketInsert обязана будет сама проинициализировать все свойства нового объекта. Очевидно, что данные, необходимые для инициализации, функция может получить только через параметры вызова. Например, знакомый элемент<A> «Buy Now!» должен будет выглядеть следующим образом:

<a href="javascript:void(0)"  
 onClick="BasketInsert(  
  'код товара',  
  'наименование',  
  'IP-адрес домашней страницы',  
  'единица измерения',  
  'цена')">  
Buy Now!  
</a>  

Несмотря на то что примечание в случае покупки не требуется, совершенно очевидно, что значительную часть информации приходится дублировать. Ведь, скорее всего, и наименование товара, и его цена уже присутствуют где-либо на странице.

Поскольку подобное дублирование для произвольных страниц неизбежно, напрашивается любопытный вывод. Как в случае DHTML со списками, так и в случае произвольных статических страниц формирование компонентов электронного магазина должно осуществляться программным путем. При этом исходная база данных ведется вне страниц электронного магазина, например в каком-нибудь классическом приложении Microsoft Office. И для нее обязательно должны быть разработаны специальные программы — утилиты, преобразующие базу данных — источник для того вида электронного магазина, к которому относится выбранный вариант. Для DHTML со списками — это текстовый файл с командами JavaScript, а для статических страниц — фрагменты HTML-кода, вставляемые в документ, — прототип страницы по методу гипертекстового препроцессора (так же как это делается на страницах PHP). Если этот процесс не автоматизировать, то необходимость дублирования информации в параметрах вызова функции покупки превратит создание Web-страницы в пытку.

В начало В начало

Выбор товаров

После того как «прайс-лист» тем или иным способом сформирован, можно перейти к проектированию процедур выбора товара на странице. Здесь важно понять, что между щелчком мыши у понравившегося товара и перемещением его в покупательскую корзину существует промежуточный этап. Этот этап связан с необходимостью ввода дополнительной информации о количестве экземпляров товара, нужных покупателю. Конечно, существуют приложения, когда количество задавать не требуется, например при покупке услуг. В этом частном случае процедуры выбора можно опустить.

Для ввода дополнительной информации о количестве товаров не избежать использования форм.

В большинстве популярных электронных магазинов процесс выбора товаров осуществляется следующим образом. После щелчка мыши у выбранного товара формируется отдельная страница с формой ввода для единственного объекта. Естественно, что для реализации такого способа получения информации требуется участие сервера. Кроме того, формирование отдельной динамической страницы для ввода количества уводит пользователя со страницы с множеством товаров и психологически способствует прекращению процесса наполнения корзины. А это нежелательно. Не спасает даже открытие отдельного окна браузера — все равно внимание пользователя переключается.

Еще хуже обстоит дело, когда форма для ввода количества реализуется непосредственно в «прайс-листе». Например, в электронном магазине уважаемой фирмы «1С» все множество товаров (а это несколько сот наименований) разбито на страницы — формы ввода, не более чем с десятком позиций, снабженных окошечками для ввода количества. Понятно, что подобная группировка вызвана не здравым смыслом, а ограничениями на разумное количество полей в форме.

Очевидно, что лучшее, если не идеальное, решение заключается в формировании совсем маленького окна, всплывающего на самой странице «прайс-листа» в позиции, где находится элемент управления «Buy Now!», точно так, как появляется контекстное меню в большинстве приложений. Это окно должно служить формой ввода для количества и содержать минимум элементов: само поле ввода и две кнопки — OK и Cancel. По ходу дела такая форма может появляться то здесь, то там, ничуть не мешая покупателю путешествовать по обширному списку товаров. У подобного решения есть еще одно замечательное свойство — возможность устанавливать значение количества по умолчанию, в зависимости от вида товара. Никакая «групповая» форма этого позволить не в состоянии.

В начало В начало

Формы в pop-up-окнах

Создать pop-up-окно на странице несложно. Для этого необходим объект класса layer, который независимо от браузера создается для именованного Generic block-level-контейнера <DIV></DIV>, со стилем, где присутствует параметр position. Например, форму для ввода количества можно следующим образом задать с помощью стиля:

#divFormQ {  
  visibility: hidden;  
  position: absolute;  
  left: 0;  
  top: 0;  
  width: 128;  
  layer-background-color: steelblue;  
  background-color: steelblue;  
  border: 2px groove lightsteelblue;  
  padding: 0px;  
}

и кода, расположенного где-нибудь в теле документа:

<div id="divFormQ">  
   
<form name="FormQ">  
  <table border="0" cellpadding="3" cellspacing="0"  
   width="128">  
    <tr>  
      <td width="50%" align="right">Quantity</td>  
      <td width="50%">  
        <input type="text" name="Q"  
         size="2" value="1">  
      </td>  
    </tr>  
    <tr>  
      <td width="50%" align="center">  
        <a href="javascript:QuantityOK()">OK</a>  
      </td>  
      <td width="50%" align="center">  
        <a href="javascript:HideIt(oFormQ)">Cancel</a>  
      </td>  
    </tr>  
  </table>  
</form>  
</div>  

Поскольку в стиле задано свойство visibility = hidden, то при первоначальной загрузке страницы форма будет невидимой.

Для отображения невидимого объекта используется функция:

function ShowIt(o, iX, iY)  
{  
  if (ie) {  
    if (o.style.visibility != "visible") {  
      o.style.xpos = iX  
      o.style.left = iX  
      o.style.ypos = iY  
      o.style.top = iY  
      if (o.filters.revealTrans != null) {  
        o.filters.revealTrans.apply()  
      }  
      o.style.visibility = "visible"  
      if (o.filters.revealTrans != null) {  
        o.filters.revealTrans.play()  
      }  
    } else {  
      HideIt(o)  
    }  
  }  
  if (n) {  
    if (o.visibility != "show") {  
      o.xpos = iX  
      o.left = iX  
      o.ypos = iY  
      o.top = iY  
      o.visibility = "show"  
    } else {  
      HideIt(o)  
    }  
  }  
}  

Для вызова этой функции необходимо кроме самого объекта задать позицию его вывода на экране в виде абсолютных координат левого верхнего угла. Для придания всплывающему окну особой выразительности предусмотрены манипуляции со свойствами .revealTrans, которые обеспечат нужный эффект в Internet Explorer при условии правильной инициализации .filters.

Скрыть объект можно с помощью обратной функции:

function HideIt(o)  
{  
  if (ie) {  
    if (o.filters.revealTrans != null) {  
      o.filters.revealTrans.stop()  
    }  
    o.style.visibility = "hidden"  
  }  
  if (n) {  
    o.visibility = "hide"  
  }  
}  

Одних только функций, показывающих и скрывающих форму, для решения поставленной задачи недостаточно. Требуется обеспечить появление формы в нужной позиции. А это не такая простая задача, поскольку в разных браузерах для этого используются и разные объектные модели. Для задания координат отображения формы используется следующая функция:

function ShowItWithEvent(o, iOffsetX, iOffsetY)  
{  
  if (ie) {  
    if (event != null) {  
      giMousePosX = event.clientX +  
        document.body.scrollLeft  
      giMousePosY = event.clientY +  
        document.body.scrollTop  
      iNewX = giMousePosX + iOffsetX  
      iNewY = giMousePosY + iOffsetY  
    } else {  
      ShowIt(o, iOffsetX, iOffsetY)  
      return  
    }  
  }  
  if (n) {  
    iNewX = giMousePosX + iOffsetX  
    iNewY = giMousePosY + iOffsetY  
  }  
  ShowIt(o, iNewX, iNewY)  
}  

В этой функции для работы варианта для Netscape Navigator требуются две глобальные переменные: giMousePosX и giMousePosY. Значения этих переменных изменяются в обработчике события MOUSEMOVE. Целиком эта довольно громоздкая конструкция выглядит так:

var giMousePosX = 0  
var giMousePosY = 0  
var sUserAgent = navigator.appName + " "   
    + navigator.appVersion  
var sAgentInfo = sUserAgent.substring(0, 12)  
   
if (sAgentInfo >= "Netscape 4.0") {  
  window.captureEvents(Event.MOUSEMOVE)  
  window.onmousemove = MouseHandler  
}  
   
function MouseHandler(e)  
{  
  giMousePosX = e.pageX  
  giMousePosY = e.pageY  
}  

Два дополнительных параметра в вызове функции ShowItWithEvent — iOffsetX и iOffsetY — позволяют «подкорректировать» позицию отображения объекта так, чтобы его появление на экране не закрывало важные элементы. Поскольку эти параметры могут принимать как положительные, так и отрицательные значения, «контекстная» форма может смещаться в любую сторону относительно позиции, в которой произошло событие Click.

Внимательный читатель, возможно, заметил, что для вызова функции BasketInsert в <A> элементе «Buy Now!» используется событие onClick, тогда как для параметра href применена «заглушка» void(0). Казалось бы, проще использовать привычное обращение href=”javascript:BasketInsert(…)”. Однако в этом случае в Internet Explorer объект event, необходимый для позиционирования формы ввода, не создается.

Наконец, последняя функция, имеющая непосредственное отношение к вводу количества товара, — проверка допустимого значения:

function checkNum(str)  
{  
  for (var i = 0; i < str.length; i++) {  
    var ch = str.substring(i, i+1)  
    if ((ch < "0" || ch > "9") && ch != ".") {  
      alert("invalid entry!")  
      return false  
    }  
  }  
  return true  
}  

В приведенном примере допустимыми значениями считаются цифры и десятичная точка, что позволяет вводить дробные значения количества товара, например единицы веса.

В начало В начало

Функция выбора товара

После того как вспомогательные вопросы решены, можно переходить к созданию самой функции выбора товара BasketInsert. Вспомним, что эта функция может быть реализована в двух вариантах: для DHTML со списками и для обычных статических страниц.

В варианте со списками функции BasketInsert передается единственный параметр — индекс товара в коллекции goods. Сама по себе функция примитивна. Ее назначение — присвоить значение по умолчанию для количества товара и отобразить форму ввода:

function BasketInsert(i)  
{  
  select_item = i  
  oFormQ.document.FormQ.Q.value = 1  
  ShowItWithEvent(oFormQ,0,5)  
}  

В приведенном примере полю «количество» по умолчанию присваивается единица. Если это не так, то для объектов коллекции goods необходимо предусмотреть дополнительное свойство и вместо единицы присваивать значение этого свойства, например goods[i].defaultQ.

Функция BasketInsert оказалась практически вырожденной, поскольку в момент ее вызова неизвестно, как поведет себя пользователь. Он может нажать на кнопку Cancel, ввести нулевое количество, и тогда операцию нужно прекратить.

Непосредственную работу по добавлению объекта в покупательскую корзину можно выполнять только в случае, если пользователь нажмет кнопку OK в форме ввода. Но это означает, что полезное действие будет выполнять функция обработки события. В нашем случае это функция QuantityOK(), которая не имеет параметров. Поэтому единственная возможность связать все в единое целое — это использование глобальной переменной select_item. Глобальная переменная инициализируется в функции BasketInsert, а используется много позже, в обработчике события QuantityOK():

function QuantityOK()  
{  
  HideIt(oFormQ)  
  str = oFormQ.document.FormQ.Q.value  
  if (checkNum(str)) {  
    q = eval(str)  
    i = select_item  
    if (Math.round(q) > 0) {   
      BasketAdd(goods[i].code,  
       goods[i].name,  
       goods[i].home,  
       goods[i].unit,  
       goods[i].price,  
       q)  
    }  
  }  
}  

Для варианта со статическими страницами потребуется гораздо больше глобальных переменных, а одноименные функции будут выглядеть следующим образом:

var select_code = 0  
var select_name = ""  
var select_home = ""  
var select_unit = ""  
var select_price = 0  
   
function BasketInsert(code, name, home, unit, price)  
{  
  select_code = code  
  select_name = name  
  select_home = home  
  select_unit = unit  
  select_price = price  
  oFormQ.document.FormQ.Q.value = 1  
  ShowItWithEvent (oFormQ,0,5)  
}  
   
function QuantityOK()  
{  
  HideIt(oFormQ)  
  str = oFormQ.document.FormQ.Q.value  
  if (checkNum(str)) {  
    q = eval(str)  
    if (Math.round(q) > 0) {   
      BasketAdd(select_code,  
       select_name,  
       select_home,  
       select_unit,  
       select_price,  
       q)  
    }  
  }  
}  

Кстати, и в этом случае можно задавать различные значения количества по умолчанию. Для этого в вызове BasketInsert нужно предусмотреть еще один параметр.

 

(Окончание следует)

КомпьютерПресс 2'2001