Список работ

Создание раскрывающегося HTML-списка

Содержание

Постановка задачи

В работе решается задача создания раскрывающегося HTML-списка, иерархия которого описана в текстовом поле HTML-документа (рис. 1).

Заданное по шаблону описание раскрывающегося списка

Рис. 1. Пример описания раскрывающегося списка

Описание создается по приведенному ниже шаблону.
По этому описанию формируется HTML-код, воспроизводящий раскрывающийся список.
В частности, набранному в левом текстовом поле рис. 1 описанию списка отвечает следующий раскрывающийся список:

 + Хранилище СН

Конфигурация Гарантия

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

Шаблон описания раскрывающегося списка

Раскрывающийся список имеет древовидную структуру. Каждый элемент такого списка ассоциируется с ветвью иерархического дерева. Каждая ветвь характеризуется уровнем и позицией на текущем уровне. Так, в вышеприведенном примере, список содержит 2 уровня. На уровне 1 имеется шесть ветвей, а на уровне 2 - только одна ветвь.
Каждый элемент раскрывающегося списка предваряется номером, обрамленным символами #, например:

#1.2.3#

Далее следует элемент списка, например:

#1.2.3#Загрузка серийных номеров

Номер ветви формируется из номера родительской ветви и номера позиции в пределах текущего уровня ветви. В качестве разделителя используется точка. Например, номер

1.2.3

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

Теги раскрывающегося HTML-списка

Набор тегов, необходимых для генерации раскрывающегося HTML-списка, возьмем из вышеприведенного примера. Без обрамляющей рамки этот пример воспроизводится следующим HTML-кодом:

<!DOCTYPE HTML>
<html>
 <head>
  <script>
   function toggle(node, idVl) {
    dvS = document.getElementById("d" + idVl).style
    sp = document.getElementById("s" + idVl);
    // Раскрывает ветвь списка
    if (dvS.display == "none") {
     sp.innerHTML = "&nbsp;–&nbsp;";
     dvS.display = "inline";
    }
    // Скрываем ветвь списка
    else {
     sp.innerHTML = "&nbsp;+&nbsp;";
     dvS.display = "none";
    }
   }
  </script>
  <style>
   span {
     border-style:dotted;
     border-width:1px;
   }
   .p {
    cursor:pointer;
   }
  </style>
 </head>
 <body>
  <p class = "p" onClick = "toggle(this, 1)"><span id = "s1">&nbsp;+&nbsp;</span>Хранилище СН</p>
  <div id = "d1" style = "display:none">
   <p style = "margin-left:30px">Конфигурация хранилища СН</p>
   <p class = "p" style = "margin-left:10px" onClick = "toggle(this, 2)"><span id = "s2">&nbsp;+&nbsp;</span>Заполнение хранилища СН</p>
   <div id = "d2" style = "display:none">
    <p style = "margin-left:60px">Обработки загрузки данных</p>
    <p style = "margin-left:60px">Загрузка номенклатуры</p>
    <p style = "margin-left:60px">Загрузка серийных номеров</p>
   </div>
   <p style = "margin-left:30px">Общий модуль СН</p>
  </div>
 <p style = "margin-left:20px">Конфигурация Гарантия</p>
 </body>
</html>

Для генерации раскрывающегося HTML-списка нам понадобятся теги, расположенные между <body> и </body>.
Идея построения кода проста: соответствующая часть дерева (подсписок) скрывается или раскрывается за счет изменения свойства display блока div с этим подсписком. При этом, если подсписок скрыт, то родительскому элементу этого подсписка предшествует знак "плюс", или знак "минус" – в противном случае.
Знаки воспроизводятся тегом span с границей из точек (border-style:dotted).
Одновременно изменяемые span и div-объекты снабжены идентификаторами с одинаковой числовой частью, например s2 и d2.
Смена знака, а также изменение видимости div-блока выполняется click-обработчиком toggle, вызываемым при ударе мышью по абзацу, содержащему span-блок и предшествующему div-блоку. Это обработчик принимает в качестве параметра числовую часть идентификаторов span и div-объектов, получает ссылки на эти объекты и изменяет надлежащим образом их свойства.
Из примера видно, что при генерации раскрывающегося HTML-списка потребуются следующие теги:

<p class = "p" style = "margin-left:#px" onClick = "toggle(this, N)">...</p>
<span id = "sN">&nbsp;+&nbsp;</span>
<div id = "dN" style = "display:none">...</div>
<p style = "margin-left:#px">...</p>

Символы # и N, присутствующие в тегах, заменяются по ходу генерации соответствующими значениями. Также в заголовок результирующего HTML-документа следует скопировать имеющиеся в примере строки между <head> и </head>.

Оформление раскрывающегося HTML-списка

Формируемый HTML-код обеспечивает отображение свернутого списка. Выделим в генерируемом коде три вида строк:

Запишем указанные виды строк, используя выше выделенные теги.

p-строка:

<p class = "p" style = "margin-left:#px" onClick = "toggle(this, N)"><span id = "sN">&nbsp;+&nbsp;</span></p><div id = "dN" style = "display:none">

0-строка:

<p style = "margin-left:#px">Элемент списка</p>

d-строка:

<p style = "margin-left:#px">Элемент списка</p></div>...</div>

В приведенном оформлении с целью упрощения генерирующего кода открывающий тег div располагаются в одной строке с сопутствующим тегом span (см. оформление p-строки), а закрывающий тег div – в конце строки с последним абзацем div-блока (см. оформление d-строки). То есть при генерации кода для отображения приведенного в примере списка вместо строк

 <p style = "margin-left:30px">Общий модуль СН</p>
</div>

будет создана следующая одна строка:

<p style = "margin-left:30px">Общий модуль СН</p></div>

Схема формирования раскрывающегося HTML-списка

В приводимая ниже схеме предполагается, что в описании списка (см. рис. 1), по которому список формируется, элементы списка разделены символом конца строки (\n) и не содержат этот символ:

  1. Вычислить NL - число уровней в списке.
  2. L = 1 (L – номер текущего уровня списка).
  3. Найти в описании списка номера nS и nE начальной и конечной строк текущего уровня списка.
  4. Если nS = nE, то перейти к п. 9.
  5. Сформировать массив arrP размера nE - nS + 1, информирующий о p-строках, в котором arrP[k] = 1, если строка с номером nS + k является p-строкой. В противном случае arrP[k] = 0.
  6. Сформировать массив arrL размера nE - nS + 1, в котором arrL[k] содержит длину заданного шаблоном номера строки k (номер берется без обрамляющих символов).
  7. Сформировать массив arrD размера nE - nS + 1, информирующий о d-строках, в котором arrD[k] содержит число закрывающих тегов div, располагаемых в конце строки nS + k.
  8. Сформировать, используя созданные массивы, HTML-код для текущего уровня списка.
  9. L = L + 1.
  10. Если L <= NL, то перейти к п. 3, иначе Останов.

Реализация отдельных шагов схемы формирования раскрывающегося HTML-списка

Вычисление числа уровней в списке

Число уровней найдем после расщепления исходного описания списка в массив и анализа номера последнего элемента этого массива.

   // Возвращает число уровней в списке
   function findNL(arrT) {
    s = arrT[arrT.length - 1];
    return parseInt(s.substring(1, 2));
   }

Вызов функции findN:

   t = document.getElementById("txtr").value;
   arrT = t.split("\n");
   alert(findNL(arrT));

В приведенном коде тег с id = "txtr" - это текстовое поле textarea, содержащее описание списка (см. левое поле на рис. 1).

Вычисление номеров начальной и конечной строк текущего уровня списка

Для первого уровня nS = 1, а для последующих nS = nE + 1. Используем для поиска nE следующую функцию:

   // Возвращает номер конечной строки уровня L
   function clcNE(arrT, nS, L) {
    for (k = nS - 1; k < arrT.length; k++) if (parseInt(arrT[k].substring(1, 2)) > L) return k;
    return arrT.length;
   }

Вызов функции clcNE:

   t = document.getElementById("txtr").value;
   arrT = t.split("\n");
   // Ищем номер конечной строки уровня 1
   nS = 1;
   nE = clcNE(arrT, nS, 1);
   alert(nE);
   // Ищем номер конечной строки уровня 2
   nS = nE + 1;
   nE = clcNE(arrT, nS, 2);
   alert(nE);

Как и ранее, тег с id = "txtr" - это текстовое поле textarea с описанием списка.

Формирование вспомогательных массивов arrP и arrL

Массив arrP - это массив, информирующий о p-строках, arrL - это массив длин предусмотренных шаблоном номеров строк в описании списка (номера берутся без обрамляющих символов).

Выделение p-строки очевидно:

  1. Найти wС1 - длину номера текущего элемента списка.
  2. Найти wN1 - длину номера следующего элемента списка.
  3. Если wN1 > wС1, то текущий элемент списка должен быть оформлен как p-строка.

   // Формирует для текущего уровня массивы arrP и arrL
   function mkRrs(arrT, nS, nE, arrP, arrL) {
    m = -1;
    for (k = nS - 1; k < nE; k++) {
     p = arrT[k].indexOf("#", 1);
     m++;
     arrP[m] = 0;
     arrL[m] = p - 1;
     if (k < nE - 1) {
      p2 = arrT[k + 1].indexOf("#", 1);
      if (p2 > p) arrP[m] = 1;
     }
    }
   }

Вызов функции mkRrs:

   t = document.getElementById("txtr").value;
   arrT = t.split("\n");
   // Ищем номер конечной строки уровня 1
   nS = 1;
   nE = clcNE(arrT, nS, 1);
   alert(nE);
   // Создаем массивы arrP и arrL
   w = nE - nS + 1;
   arrP = new Array(w);
   arrL = new Array(w);
   // Заполняем массивы arrP и arrL
   mkRrs(arrT, nS, nE, arrP, arrL);
   // Проверка
   document.getElementById("txtr2").value = arrP.toString() + "\n" + arrL.toString();

Как и ранее, тег с id = "txtr" - это текстовое поле textarea с описанием списка. Тег с id = "txtr2" - это правое текстовое поле textarea на рис. 1.

Формирование вспомогательного массива arrD

Массив arrD - это массив, информирующий о d-строках.

Поиск d-строки также несложен:

  1. Пусть k - это индекс p-строки в массиве arrP, а w - длина массива arrP.
  2. ntFnd = true
  3. Для k2 = k + 2 По w Цикл
     Если arrL[k2] <= arrL[k] Тогда
      arrD[k2 - 1] = arrD[k2 - 1] + 1
      ntFnd = false
     КонецЕсли;
     Если ntFnd тогда arrD[w - 1] + 1
    КонецЦикла

Приведенную последовательность вычислений реализует следующая функция:

   // Формирует для текущего уровня массив arrD
   function mkRrD(nS, nE, arrP, arrL) {
    w = nE - nS + 1;
    arrD = new Array(w);
    for (m = 0; m < w; m++) arrD[m] = 0;
    for (m = 0; m < w; m++) {
     if (arrP[m]) {
      ntFnd = 1;
      for (m2 = m + 2; m2 < w; m2++) {
       if (arrL[m2] <= arrL[m]) {
        arrD[m2 - 1] += 1;
        ntFnd = 0;
        break;
       }
      }
      if (ntFnd) arrD[w - 1] += 1;
     }
    }
    return arrD;
   }

Вызов функции mkRrD:

   t = document.getElementById("txtr").value;
   arrT = t.split("\n");
   // Ищем номер конечной строки уровня 1
   nS = 1;
   nE = clcNE(arrT, nS, 1);
   // Создаем массивы arrP и arrL
   w = nE - nS + 1;
   arrP = new Array(w);
   arrL = new Array(w);
   // Заполняем массивы arrP и arrL
   mkRrs(arrT, nS, nE, arrP, arrL);
   // Заполняем массив arrD
   arrD = mkRrD(nS, nE, arrP, arrL);
   // Проверка
   document.getElementById("txtr2").value = arrP.toString() + "\n" + arrL.toString() + "\n" + arrD.toString();

Как и ранее, теги с id "txtr" и "txtr2" - это соответственно левое и правое текстовые поля на рис. 1.

Создание одного уровня раскрывающегося HTML-списка

Имея массивы arrP и arrD и зная состав (теги) генерируемого HTML-кода, несложно создать и код, отображающий раскрывающийся HTML-список:

   // Формирует код одного уровня раскрывающегося HTML-списка
   function mkNLvl(arrT, nS, nE, arrP, arrL, arrD) {
    nLvl = "";
    pS = '<p class = "p" style = "margin-left:#px" onClick = "toggle(this, N)">'
    spn = '<span id = "sN">&nbsp;+&nbsp;</span>';
    dv = '<div id = "dN" style = "display:none" >';
    zrr = '<p style = "margin-left:#px">';
    p = '</p>';
    m = -1;
    for (k = nS - 1; k < nE; k++) {
     m++;
     nW = arrL[m];
     fNW = Math.floor(0.5 * nW);
     lmnt = arrT[k].substring(nW + 2);
     if (arrP[m]) {
      nPx = "" + 10 * fNW;
      pS2 = pS.replace(/#/, nPx);
      pS2 = pS2.replace(/N/, "" + k);
      spn2 = spn.replace(/N/, "" + k);
      dv2 = dv.replace(/N/, "" + k);
      nLvl = nLvl + pS2 + spn2 + lmnt+ p + dv2 + "\n";
     }
     nPx = (fNW) ? "" + 30 * fNW : "20";
     zrr2 = zrr.replace(/#/, nPx);
     if (arrD[m]) {
      nLvl = nLvl + zrr2 + lmnt + p;
      for (k2 = 0; k2 < arrD[m]; k2++) nLvl += "</div>";
      nLvl += "\n";
     }
     if(arrP[m] + arrD[m] == 0) nLvl = nLvl + zrr2 + lmnt + p + "\n";
    }
    return nLvl;
   }

Вызов функции mkNLvl:

   t = document.getElementById("txtr").value;
   arrT = t.split("\n");
   // Ищем номер конечной строки уровня 1
   nS = 1;
   nE = clcNE(arrT, nS, 1);
   // Создаем массивы arrP и arrL
   w = nE - nS + 1;
   arrP = new Array(w);
   arrL = new Array(w);
   // Заполняем массивы arrP и arrL
   mkRrs(arrT, nS, nE, arrP, arrL);
   // Заполняем массив arrD
   arrD = mkRrD(nS, nE, arrP, arrL);
   // Формируем код одного уровня раскрывающегося HTML-списка
   nLvl = mkNLvl(arrT, nS, nE, arrP, arrL, arrD);
   // Проверка
   document.getElementById("txtr2").value = nLvl;

Как и ранее, теги с id "txtr" и "txtr2" - это соответственно левое и правое текстовые поля на рис. 1.

Создание раскрывающегося HTML-списка

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

   // Формирует раскрывающийся HTML-список
   function mkTrVw() {
    t = document.getElementById("txtr").value;
    if (t.length == 0)
     alert("Empty");
    {
     arrT = t.split("\n");
     nL = findNL(arrT);
     nS = 1;
     trvw = "";
     for (L = 1; L <= nL; L++) {
      // Ищем номер конечной строки уровня L
      nE = clcNE(arrT, nS, L);
      // Создаем массивы arrP и arrL
      w = nE - nS + 1;
      arrP = new Array(w);
      arrL = new Array(w);
      // Заполняем массивы arrP и arrL
      mkRrs(arrT, nS, nE, arrP, arrL);
      // Заполняем массив arrD
      arrD = mkRrD(nS, nE, arrP, arrL);
      // Добавляем код текущего уровня раскрывающегося HTML-списка
      trvw += mkNLvl(arrT, nS, nE, arrP, arrL, arrD);
      nS = nE + 1;
     }
     // Отображаем результат
     t2 = document.getElementById("txtr2")
     t2.value = trvw;
    }
   }

Как и ранее, теги с id "txtr" и "txtr2" - это соответственно левое и правое текстовые поля на рис. 1.
Отображенный в правом поле результат следует вставить в HTML-документ, содержащий в заголовочной части приведенные выше теги script и style.

Ввод описания списка и отображение результата

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

Содержание раскрывающегося списка HTML-код раскрывающегося списка

Кнопка "Сохранить" отвечает за запись в Cookie левого поля, а кнопка "Очистить" - за очистику правого поля.

По тестовому набору создается следующий раскрывающийся список:

 + P 1

 + P 2

 + P 3

Заключение

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

Литература

1. Поллок Д. JavaScript. Руководство разработчика. - Спб.: Питер, 2006. - 544 с.

Список работ

Рейтинг@Mail.ru