Веб-школа
Интернет-технологии

РЕКЛАМА

Разное

Иерархическое (многоуровневое)
меню навигации на JavaScript
(часть 5)

< Часть 4

Листинг 2. Файл kcmmenu.js


var nmb=0;
var divs = null;
var spans = null;
var zindex=100;
//////////////////////////////
//Функции создания объектов //
//////////////////////////////
function SafeString(str){
//функция SafeString заменяет символы > и < строки str на &gt и &lt,
//чтобы браузер не интерпретировал их как часть тэгов
 r = str.replace(/>/ig, "&gt");
 r = r.replace(/</ig, "&lt");
 return r;
}
function MenuItem(title,anchor,alt){
  this.objname="item";
  this.owner=null;
  this.name=SafeString(title);
  this.idname="n"+(nmb++);
  this.a=anchor;
  this.alt=SafeString(alt);
}
function MenuBox(name){
  this.objname="box";
  this.owner=null;
  this.name=SafeString(name);
  this.idname="n"+(nmb++);
  this.alt="Goto submenu";
  this.count = 0;
  this.item = new Object;
  this.addItem = addMenuItem;
  this.getItem = getMenuItem;
}
function addMenuItem(item){
  item.owner=this;
  this.item[this.count++]=item;
}

function getMenuItem(name){
  for (var i=0; i<this.count; i++){
   p = this.item[i];
   if (p.idname==name) return p;
   if (p.objname == "box"){
      p2 = p.getItem(name);
      if (p2!=null) return p2;
  }}
return null;
}
function MenuBar(){
  this.objname="bar";
  this.owner=null;
  this.count = 0;
  this.name = "MMB";
  this.item = new Object;
  this.addMenu = addSubMenu;
  this.getSubMenu = getSubMenu;
  this.getMenuItem = getMenuItem;
}
function addSubMenu(menu){
  menu.owner=this;
  this.item[this.count++]=menu;
}

function getSubMenu(name){
 for (var i=0; i<this.count; i++){
  p = this.item[i];
  if (p.idname==name) return p;
  if (p.objname == "box"){
    p2 = p.getItem(name);
    if (p2!=null)
       if (p2.objname=="box") return p2;
  }}
return null;
}
//////////////////////////////////////////////////
//Функции создания меню (записи в html-документ //
//////////////////////////////////////////////////
function ConstructMenuBox(mb){
  if (mb.objname!="box"){alert("Error: mb.objname!=\"box\". It's "+
     mb.objname); return;}
  zindex++;
  if (mb.owner==null) alert("Warrning! "+mb.name + " don't have the owner");
  var strsm="<DIV class=submenu id=submenu name="+
  mb.idname+" style='z-index:"+zindex+
  "; position:absolute; width:1000px; visibility:hidden;'>";
   for (var j=0; j<mb.count; j++){
    var p = mb.item[j];
    if (p.objname == "box") ConstructMenuBox(p);
    var ttl = p.idname;
    var label = p.name;
    var alt = p.alt;
    strsm+="<span class=nonact id=menupunkt name="+
       ttl+" title=\"" +alt+"\"> "+label+" </span><br>";
  }
  strsm += "</DIV>";
  document.writeln(strsm);
  zindex--;
}
function ConstructMenu(menu)
{
  str = "<DIV class=menu id=MainMenuBar>";
  for (var i=0; i<menu.count; i++){
   var m=menu.item[i];
   str+="<span class=menu id=submenu name='"+m.idname+
   "'> "+m.name+" </span>";
   if ((i+1)<menu.count) str+="<span class=menudelim>|</span>";
   ConstructMenuBox(m);
 }
 str += "</DIV>";
 document.writeln(str);
 InitMenu(menu);
}

function InitMenu(menu)
{
  divs = document.all.tags("DIV");
  if (divs==null){alert("Collection \"DIV\" don't aviable");return;}
  spans = document.all.tags("SPAN");
  if (spans==null){alert("Collection \"SPAN\" don't aviable"); return;}
  for (var k=0; k<divs.length; k++)
  {
    var m=null;
    var curdiv=null;
    if ((m=menu.getSubMenu(divs[k].name))==null) continue;
    else {
       curdiv=divs[k];
       maxw=0;
       for (var j=0; j<m.count; j++){
          var p = m.item[j];  var curspan = null;
          ttl=p.idname
          for (var l=0; l<spans.length; l++)
            if (spans[l].name==ttl){curspan=spans[l]; break;}
          if (curspan==null){alert("object:"+ttl+" = null"); continue;}
          w = curspan.offsetWidth;
          if (w>maxw) maxw=w;
       }
       curdiv.style.width=maxw;
    }}
}

 
Реклама

В Листинге 2 определены (в смысле, представлены все необходимые функции для создания) три объекта, определяющие содержание иерархического меню. Наиболее простой из них объект MenuItem, который представляет собой конечный пункт меню; он определяет реакцию браузера на щелчок мышки. Этот объект имеет шесть полей и не имеет методов. Его конструктору передаются три аргумента (title, anchor, alt), инициализирующих три из шести полей; остальные поля заполняются по умолчанию. Смысл полей таков:

name – имя пункта меню, которое будет отображаться на экране. Инициализируется параметром title конструктора. Для предохранения имени от "блуждающих" символов > и < оно обрабатывается функцией SafeString, заменяющей эти символы на "безопасные" синонимы (named entities) &gt и &lt.

a – ссылка (URL), на которую необходимо перейти, или код на JavaScript, который надо выполнить. В последнем случае строка предваряется строкой "javascript:" или "jscript:". Инициализируется параметром anchor контруктора.

alt – строка подсказки (tip), появляющаяся после остановки курсора мышки на пункте меню, соответствующая параметру title практически любого тэга HTML. Может быть пустой строкой (""). Инициализируется параметром alt контруктора.

objname – имя объекта (вернее тип). Т.к. пунктом меню может быть не только объект MenuItem, но и MenuBox, определяющий подменю, необходима информация для индентификации. Самый простой способ хранить тип объекта в строке с одним и тем же именем (объект MenuBox тоже имеет поле objname). Для объекта MenuItem эта строка равна "item".

idname – уникальное имя объекта, равное "nxx" (xx – число). Уникальность достигается присваением числа, равному инкременту (увеличенному на 1) глобальной переменной nmb, т.о. что каждый пункт меню отличается от предыдущего как минимум на единицу.

owner – объект-владелец пунктом меню, заполняется при добавлении пункта в состав подменю (см. ниже). Для определения (резервирования) поля присваивается нулевая ссылка (null).

Следующим объектом, определенным в файле kcmmenu.js, является MenuBox. Этот объект представляет собой контейнер для объектов MenuItem и объектов подменю MenuBox. Его контруктор – function MenuBox(name), где name – имя подменю. Поля и методы объекта MenuBox таковы.

name – имя пункта меню, которое будет отбражаться на экране. Инициализируется параметром name контруктора. Для предохранения имени от "блуждающих" символов > и < обрабатывается функцией SafeString (см. выше).

alt – строка подсказки (tip) (см. MenuItem.alt). По умолчанию задана как "Goto submenu"; при необходимости можно изменить.

objname – имя объекта (см. MenuItem.objname); для объекта MenuBox равно "box".

idname – уникальное имя объекта (см. MenuItem.idname).

owner – объект-владелец пунктом меню (см. MenuItem.owner).

item – массив объектов (MenuItem и MenuBox), для которых данный объект является контейнером. Инициализация этого поля рассмотрена выше

count – текуший размер массива item; для нового объекта равен 0.

addItem – метод (функция) добавления пункта меню (или подменю) в контейнер. Методу присваивается ссылка на функцию addMenuItem(item) (где item – объект типа MenuItem или MenuBox). Функция addMenuItem присваивает полю owner объекта item указатель на объект контейнер this (напомним, что в this передается ссылка на объект, чей метод используется, т.е. ссылка на самого себя). Т.о. контейнер "говорит" своим элементам, что он является их владельцем. После этого item добаляется в массив объектов MenuBox.item, увеличивая при этом размер массива count.

getItem – метод (функция) получения пункта меню (или подменю) по имени. Методу присваивается ссылка на функцию getMenuItem(name) (где name – имя пункта меню, объект которого (MenuItem или MenuBox) необходимо получить). Функция getMenuItem просматривает поля idname всех своих элементов. Если это поле не равно имени запрошенного элемента, а тип элемента равен MenuBox (objname=="box"), то вызывается функция getItem этого элемента для просмотра в нем. Т.о. просматривается вся ветка подчиненных объектов. Если объект с заданным именем не найден, возвращается пустая ссылка (null).
 

Реклама

Каждому сайту - механизм организации собственной ленты новостей бесплатно! Подробнее >>

Наконец, последним объектом, определенным в файле kcmmenu.js, является MenuBar. Этот объект представляет собой контейнер для объектов MenuBox нулевого уровня, всегда отображаемых в полосе меню экрана. Его контруктор – function MenuBar() не имеет аргументов. Поля и методы объекта MenuBar таковы.

name – имя меню; по умолчанию присваивается "MMB" (Main Menu Bar).

objname – имя объекта (см. MenuItem.objname); для объекта MenuBar равно "bar".

owner – равен null, т.е. не имеет владельца.

item – массив объектов MenuBox, для которых данный объект является контейнером. Инициализация этого поля рассмотрена выше

count – текущий размер массива item; для нового объекта равен 0.

addMenu – метод аналогичен MenuBox.addItem и отличается только именем.

getSubMenu – метод (функция) получения ссылки на подменю по имени. Метод аналогичен MenuBox.getItem и отличается лишь тем, что проверяет тип возвращаемого значения (ссылка возвращается только в том случае, если objname=="box").

getMenuItem – метод полностью аналогичен MenuBox.getItem.

Построенный, используя эти объекты (см. примечание о файле kcmmenu.js), глобальный объект menu не доступен браузеру, отображаемому информацию. Он находится в памяти (образно) машины сценариев (Scripting Engine), которая связывает html-код с событиями от html-тэгов. Для связи меню с html-кодом необходимо определить html-тэги, содержание визуальное представление меню на экране. Для этого нам необходимо по ходу отображения документа браузером изменить его, чтобы включить меню в отображаемую информацию. Машина сценариев имеет несколько возможностей менять содержание документа. Одной из них является функция write (в нотации JavaScript) объекта document глобального объекта window. Выполнение этой функции приводит к записи в документ в месте ее применения строки (или строк), передаваемой ей в качестве аргумента. Для "вписывания" меню в html-страницу используется функция ConstructMenu(menu) (и ряд вспомогательный функций), которой передается ссылка на объект меню. Вообще, по большому счету, передача функции ссылки на menu не обязательная, т.к. menu – глобальный объект; здесь это сделано в целях универсальности и наглядности.

ConstructMenu создает строковое представление тэга DIV (т.е. html-код) с классом menu и id=MainMenuBar (класс определяет форматирование объекта и описан, в данном случае, в файле menu.css). Этот DIV ассоциируется в объектом menu (типа MenuBar). В этот DIV вставляются контейнеры текста SPAN, отвечающие за подменю нулевого уровня. Строковое представление этих SPAN определяется полем name этих подменю, параметр name – полем idname (уникальным!), class=menu, id=submenu. Разделяются пункты полосы меню вертикальной чертой, "обернутой" в тэг SPAN с классом menudelim. Для каждого пункта, представляющего собой указатель на подменю, вызывается функция построения подменю ConstructMenuBox(submenu). При построении (записи в строку html-кода) всего меню, оно вписывается в документ функцией document.writeln. В конце вызывается функция инициализации меню.

ConstructMenuBox делает тоже самое, что и ConstructMenu, только для подменю. Для этого после проверок на корректность вызова функции она увеличивает глобальную переменную zindex на 1, изначально заданную равной параметру z-index стиля div.menu. Это значение потом присваевается параметру z-index стиля тэга DIV, отвечающего за подменю. Таким образом достигается правильный z-порядок следования подменю; подменю высшего уровня (т.е. находящиеся дальше от корневых подменю нулевого уровня) будут всегда находится сверху подменю низшего уровня. DIV подменю записывается с парамтрами: class = submenu, id = submenu, name = idname подменю. Параметры стиля определяют z-index (см. выше), перемещаемость элемента по экрану (position:absolute), невидимость (visibility:hidden, т.к. первоначально подменю не видны) и ширину (width:1000px). Ширина задается заведомо большой, она впоследствии настраивается на ширину тэгов SPAN, входящих в меню. При отсутствии ширины в описании стиля, это свойство перестает быть доступным из JavaScript. Затем в этот DIV вставляются контейнеры текста SPAN, отвечающие за пункты меню. Строковое представление этих SPAN определяется полем name этих пунктов, параметр name – полем idname (уникальным!), параметр title – полем alt, class=nonact (первоначально неактивный), id=menupunkt. Разделяются пункты подменю разрывом строки (<br>). Если пункт меню является подменю высшего уровня (для этого проверяется его поле objname на соответсвие строке "box"), то для него рекурсивно вызывается ConstructMenuBox с параметром равным ссылке на объект этого пункта-подменю. В конце это подменю вписывается в html-документ (напомним, что оно невидимо изначально). Глобальная переменная zindex уменьшается на 1, возвращая прежнее значение; это заставляет подменю одних уровней иметь одинаковый приоритет при прорисовке на экране. Таким (вобщем-то нехитрым) образом один вызов функции ConstructMenuBox из ConstructMenu для подменю нулевого уровня за счет рекурсии строит всю ветку.

Функция InitMenu (напомним, что она вызывается из ConstructMenu) создает два глобальных массива всех тэгов меню DIV и SPAN (вернее, их описаний), которые используются при обработке событий от элементов меню. Эти массивы содержат не все эти тэги DIV и SPAN документа, а только тэги меню, потому что документ фактически еще не составлен (не подготовлен для просмотра и статистики). Таким образом, между прочим, отсекаются все ненужные нам тэги остальной части документа, не отвечающие за меню. Затем для всех тегов DIV ищется их соответствие во всех объектах подменю главного меню. Для этого методом menu.getSubMenu(DIV.name) ищется подменю с полем idname=DIV.name. Такое соответствие должно быть обязательно, ибо все тэги DIV в массиве строились на основании соответствующих подменю. В противном случае этот тэг пропускается. Если соответствие найдено, просматриваются все пункты меню, входящие в данное подменю. Для этого простым перебором в массиве ищется SPAN с параметром name равным полю idname данного пункта. Максимальная ширина всех просмотренных пунктов подменю назначается шириной данного подменю. Такая процедура исключает согласование ширины пункта меню и ширины подменю, вернее ширины тэга SPAN пункта меню и ширины тэга DIV подменю. Если же Вы решили их согласовать, например, параметром width стилей, то эту процедуру можно убрать. [> далее >]

© 2000, Сергей Кузнецов
 

Реклама

ВАКАНСИИ (Компьютеры, Интернет). Подробнее >>

ПОИСК по сайту: 
© iD, 2000

Hosted by uCoz