Сайт Ивана Чередниченко | Создаем раскрывающийся список на Web-странице

Создаем раскрывающийся список на Web-странице

В этой статье мы создадим простой раскрывающийся список, который будет работать во всех популярных Web-браузерах (Microsoft Internet Explorer, Opera, Netscape Navigator). Мы будем использовать только стандартные возможности языка HTML, CSS и JavaScript, что может являться своеобразной гарантией того, что создаваемый нами список будет корректно работать и в остальных браузерах.

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

В языке HTML имеются специальные теги, которые позволяют формировать списки - это теги <UL> и <LI>. Данные теги наиболее часто используются при создании списков на Web-страницах, в отличие от тега <OL>; именно по этой причине мы и будем использовать теги <UL> и <LI>.

Для начала создадим простой список, например такой, как изображен ниже:

Глава 1
  Раздел 1.1
  Раздел 1.2
  Раздел 1.3
Глава 2
  Раздел 2.1
  Раздел 2.2

Если вы работаете в среде Quick Page, то вы сможете создать такой список очень простым способом! В результате вы должны получить следующий HTML-код:

Исходный код

<UL>
  <LI>Глава 1
    <UL>
      <LI>Раздел 1.1</LI>
      <LI>Раздел 1.2</LI>
      <LI>Раздел 1.3</LI>
    </UL>
  </LI>
  <LI>Глава 2
    <UL>
      <LI>Раздел 2.1</LI>
      <LI>Раздел 2.2</LI>
    </UL>
  </LI>
</UL>

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

Исходный код

<UL>
  <LI>Глава 1
    <UL ID="chapter1">
      <LI>Раздел 1.1</LI>
      <LI>Раздел 1.2</LI>
      <LI>Раздел 1.3</LI>
    </UL>
  </LI>
  <LI>Глава 2
    <UL ID="chapter2">
      <LI>Раздел 2.1</LI>
      <LI>Раздел 2.2</LI>
    </UL>
  </LI>
</UL>

Мы задали для внутренних тегов <UL> параметр ID. Этот параметр позволяет обращаться к элементу (тегу) по значению этого параметра в сценарии на языке JavaScript. Теперь самое время написать динамику - сценарий JavaScript, который будет обрабатывать список: сворачивать или раскрывать его.

Исходный код

<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
<!--
function doMenu(AIndex) {
  if ( AIndex == 1 ) {
    if ( chapter1.style.display == 'none' ) {
      chapter1.style.display = 'block';
    }
    else {
      chapter1.style.display = 'none';
    }  // if..else
  }  // if
  
  if ( AIndex == 2 ) {
    if ( chapter2.style.display == 'none' ) {
      chapter2.style.display = 'block';
    }
    else {
      chapter2.style.display = 'none';
    }  // if..else
  }  // if  
}  // doMenu
//-->
</SCRIPT>

Мы объявили функцию с именем doMenu, которая может принимать числовое значение AIndex. В зависимости от этого значения будет обрабатываться одно из двух условий. В условиях мы обращаемся к значению свойства style.display. Данное CSS-свойство позволяет задать вид элемента страницы. Всего доступно семь значений для этого свойства, но мы остановимся на двух: block и none. Если вы задаете значение block, то это делает элемент страницы блочным. При этом такой элемент можно свободно позиционировать. Ярким примером блочного элемента является тег <DIV>. Элементы списка также являются блочными, поэтому мы используем данное свойство. Если в качестве значения свойству style.display присвоить значение none, то это сделает данный элемент страницы невидимым. При этом страница будет отображаться так, будто бы этого элемента вообще не существует в исходном коде Web-страницы [2, с. 515].

Теперь нам осталось подключить данную функцию к соответствующим элементам нашего раскрывающегося списка. Это делается очень простым образом: вместо строки, в которой содержится текст <LI>Глава 1 вставьте следующий фрагмент: <LI><A HREF="javascript:doMenu(1);">Глава 1</A>. Аналогичным образом поправьте и элемент списка второй главы, чтобы он принял следующий вид: <LI><A HREF="javascript:doMenu(2);">Глава 2</A>.

На данном этапе вы должны иметь примерно следующую Web-страницу:

Исходный код

<HTML>
<BODY>
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
<!--
function doMenu(AIndex) {
  if ( AIndex == 1 ) {
    if ( chapter1.style.display == 'none' ) {
      chapter1.style.display = 'block';
    }
    else {
      chapter1.style.display = 'none';
    }  // if..else
  }  // if
  
  if ( AIndex == 2 ) {
    if ( chapter2.style.display == 'none' ) {
      chapter2.style.display = 'block';
    }
    else {
      chapter2.style.display = 'none';
    }  // if..else
  }  // if  
}  // doMenu
//-->
</SCRIPT>

<UL>
  <LI><A HREF="javascript:doMenu(1);">Глава 1</A>
    <UL ID="chapter1">
      <LI>Раздел 1.1</LI>
      <LI>Раздел 1.2</LI>
      <LI>Раздел 1.3</LI>
    </UL>
  </LI>
  <LI><A HREF="javascript:doMenu(2);">Глава 2</A>
    <UL ID="chapter2">
      <LI>Раздел 2.1</LI>
      <LI>Раздел 2.2</LI>
    </UL>
  </LI>
</UL>
</BODY>
</HTML>

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

Во-первых, почему мы не использовали свойство style.visibility вместо style.display? Просто свойство style.visibility позволяет сделать элемент страницы видимым или не видимым [2, с. 536]. Но если элемент невидим (style.visibility = 'hidden';), то это не означает, что этого элемента на странице нет. Если бы в коде мы использовали данное свойство, то при закрытых элементах первой главы между видимыми элементами существовал бы промежуток, который нам в данном случае ни к чему. Если хотите наглядно увидеть различия между этими двумя свойствами, то замените соответствующий фрагмент кода сценария на следующий блок кода и посмотрите на результат в Web-браузере при закрытых элементах первой главы:

Исходный код

  if ( AIndex == 1 ) {
    if ( chapter1.style.visibility == 'hidden' ) {
      chapter1.style.visibility = 'visible';
    }
    else {
      chapter1.style.visibility = 'hidden';
    }  // if..else
  }  // if

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

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

Изменим функцию doMenu на следующую:

Исходный код

function doMenu(AObjIndex) { 
  var subObj = document.all['chapter' + AObjIndex];
  if ( subObj.style.display == 'none' ) {
    subObj.style.display = 'block';
  }
  else {
    subObj.style.display = 'none';    
  }  // if..else  
}  // doMenu

Сохранить измененную страницу и запустите ее через Web-браузер. Ну как? Список раскрывается и закрывается? Да! Заметьте, что мы не изменяли исходный код HTML-страницы, а скорректировали функцию. Теперь мы можем добавить и другие пункты списка.

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

В вышеприведенной функции мы обращаемся к коллекции (массиву) всех элементов страницы document.all. Конечно, вместо обращения к соответствующей коллекции страницы, можно было бы использовать функцию document.getElementById, которая имеется в стандарте W3C DOM, но, по-моему мнению обращение к коллекции будет более безопасным и код в данном случае будет (должен) работать даже в старых браузерах [1, с. 112].

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

Исходный код

  <LI><A HREF="javascript:doMenu(1);">Глава 1</A>
    <UL ID="chapter1">
      <LI><A HREF="javascript:doMenu('1_1');">Раздел 1.1</A>
      <UL ID="chapter1_1">
        <LI><A HREF="javascript:doMenu('1_1_1');">Подраздел 1.1.1</A>
          <UL ID="chapter1_1_1">
            <LI>Ну дальше уже некуда</LI>
          </UL>
        </LI>
        <LI>Подраздел 1.1.2</LI>
        <LI>Подраздел 1.1.3</LI>
      </UL>
      </LI>
      <LI><A HREF="javascript:doMenu('1_2');">Раздел 1.2</A>
      <UL ID="chapter1_2">
        <LI>Подраздел 1.2.1</LI>
        <LI>Подраздел 1.2.2</LI>
        <LI>Подраздел 1.2.3</LI>
      </UL>
      </LI>
      <LI><A HREF="javascript:doMenu('1_3');">Раздел 1.1</A>
      <UL ID="chapter1_3">
        <LI>Подраздел 1.3.1</LI>
        <LI>Подраздел 1.3.2</LI>
        <LI>Подраздел 1.3.3</LI>
      </UL>
      </LI>
    </UL>
  </LI>

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

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

Теперь мы попытаемся придать современный вид раскрывающемуся списку. Для этого нам понадобиться объявить стиль, а также нарисовать два рисунка. Для каждого элемента, который имеет подэлементы необходимо добавить тег изображения, которому необходимо задать идентификатор ID. Задавайте универсальные значения этому свойству, чтобы не пришлось делать каких-либо условий и исключений в коде сценария. Самым оптимальным для данного случая является ID="chapter_imgX", где вместо символа X необходимо поставить соответствующий числовой идентификатор. Например, для элемента "Глава 1" необходимо добавить тег: <IMG SRC="treeOpened.png" ID="chapter_img1" />; для элемента "Раздел 1" - <IMG SRC="treeOpened.png" ID="chapter_img1_1" /> и так далее.

Некоторые пользователи отключают отображение рисунков, поэтому для тегов изображений желательно указывать и размеры с помощью параметров WIDTH и HEIGHT. Для простоты примера этого сделано не было, но если вы хотите защитить свой список при отключенном отображении рисунков не пренебрегайте этими двумя параметрами тега <IMG>.

Теперь изменим функцию doMenu до следующего вида:

Исходный код

function doMenu(AObjIndex) { 
  var subObj = document.all['chapter' + AObjIndex];
  var imgObj = document.all['chapter_img' + AObjIndex];
  if ( subObj.style.display == 'none' ) {
    subObj.style.display = 'block';
    imgObj.src = 'treeOpened.png';
  }
  else {
    subObj.style.display = 'none';    
    imgObj.src = 'treeClosed.png';
  }  // if..else  
}  // doMenu

Здесь мы создаем дополнительную переменную imgObj, которая будет являться ссылкой на объект изображения. Здесь можно использовать коллекцию document.images, но чтобы не путать вас, в коде используется уже знакомая вам коллекция всех элементов страницы - document.all. Далее, если необходимых подэлементов не видно, над этой ссылкой работает строка imgObj.src = 'treeOpened.png'. Эта строка позволяет загрузить изображение treeOpened.png в экземпляр imgObj.

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

Исходный код

<STYLE>
UL {
  list-style-type : none;
}
</STYLE>

Вот мы и создали более-менее красивый раскрывающийся список, который вы сможете весьма просто модифицировать и усовершенствовать. Если вы хотите, чтобы при нажатии на изображение происходило раскрытие или закрытие соответствующего списка, то вам необходимо вставить в тег <IMG> следующий фрагмент кода: ONCLICK="doMenu(X);", где вместо X идентификатор соответствующего списка, который должен быть закрыт или показан при нажатии на конкретном изображении.

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

Аналогичные раскрывающиеся списки используются в разделах содержания справок продуктов, а также в разделе Карта сайта.

Чтобы повысить быстродействие приведенных в статье сценариев, можно порекомендовать несколько способов.

Первый способ заключается в том, что в исходном коде HTML-страницы, которая использует раскрывающийся список, создать два тега <IMG>, указав следующие параметры для каждого из них: STYLE="visibility : hidden; display : none;" WIDTH="0" HEIGHT="0". Эти параметры позволяют скрыть элемент от пользователя, а указание ширины и высоты рисунка, равные нолям позволяют сделать его скрытым от пользователя даже в том случае, если браузер не понимает свойства display. Соответствено один из тегов будет содержать изображение для открытого списка (например, treeOpened.png), а второй для закрытого (treeClosed.png). То есть, изображения при загрузке страницы будут загружены, но пользователь их видеть не будет. В нужный момент просто присвоим в коде: imgMyCollapseImg1.src = imgClosed.src. Возможно, данный способ позволит быстрее обрабатывать изображения, но к сожалению, в языке JavaScript не доступна команда наподобие следующей из языка Delphi: imgMyCollapseImg1.Picture := imgClosed.Picture - тогда было бы точно ускорение.

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

Исходный код

<STYLE>
#sh {
  border-color    : blue;
  border-width    : 1px;
  border-style    : solid;
  text-decoration : none;
  font-size       : 7pt;
}
</STYLE> 

<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
<!--
function showHide(AObjIndex) {
  var spanObj = document.all['shSpan' + AObjIndex];
  if ( spanObj.innerHTML == '+' )
    spanObj.innerHTML = '&minus;';
  else
    spanObj.innerHTML = '+';
}  // showHide
//-->
</SCRIPT>

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

Сам код такой ссылки будет выглядеть следующим образом:

Исходный код

<A HREF="javascript:showHide(1);" ID="sh"><SPAN ID="shSpan1" NAME="shSpan1">&minus;</SPAN></A>

Более полезный фрагмент кода можно привести далее:

function doMenu(AObjIndex) { 
  var subObj = document.all['chapter' + AObjIndex];
  var spanObj = document.all['shSpan' + AObjIndex];
  if ( subObj.style.display == 'none' ) {
    subObj.style.display = 'block';
    spanObj.innerHTML = '&minus;';
  }
  else {
    subObj.style.display = 'none';    
    spanObj.innerHTML = '+';
  }  // if..else
}  // doMenu

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

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

Для этого нам необходимо будет несколько изменить сценарий (смотрите article_12_9.html из архива с примерами к статье). Для этого необходимо реализовать новые функции: doMenu_Hide, которая будет скрывать содержимое списка, то есть сворачивать его и doMenu_Show, которая будет раскрывать содержимое списка, то есть отображать его содержимое. Функция doMenu была также изменена, чтобы она могла реализовать все возможности нового подхода. Также добавляем новую функцию onBodyLoad, в которой описываем действия при загрузке страницы - то есть какие разделы раскрывающегося списка должны быть видны при открытии страницы, а какие нет. Получаем следующий код:

Исходный код

// Сворачивает (скрывает) содержимое.
function doMenu_Hide(ASubObj, ASpanObj) {
  ASubObj.style.display = 'none';
  ASpanObj.innerHTML = '+';
}  // doMenu_Hide

// Раскрывает (отображает) содержимое.
function doMenu_Show(ASubObj, ASpanObj) {
  ASubObj.style.display = 'block';
  ASpanObj.innerHTML = '&minus;';
}  // doMenu_Show

// Скрывает или раскрывает содержимое.
function doMenu(AObjIndex) { 
  var subObj = document.all['chapter' + AObjIndex];
  var spanObj = document.all['shSpan' + AObjIndex];
  if ( subObj.style.display == 'none' ) {
    doMenu_Show(subObj, spanObj);
  }
  else {
    doMenu_Hide(subObj, spanObj);
  }  // if..else
}  // doMenu

// Действия при загрузке данной страницы.
function onBodyLoad() {
  // Сворачиваем все подразделы главы 1.
  doMenu_Hide(document.all['chapter1_1'], document.all['shSpan1_1']);
  doMenu_Hide(document.all['chapter1_1_1'], document.all['shSpan1_1_1']);
  doMenu_Hide(document.all['chapter1_2'], document.all['shSpan1_2']);
  doMenu_Hide(document.all['chapter1_3'], document.all['shSpan1_3']);
  // Принудительно раскрываем главу 2.
  doMenu_Show(document.all['chapter2'], document.all['shSpan2']);
}  // onBodyLoad

Заметили как непривычно выглядит код функции onBodyLoad? Здесь используется обращение к document.all, чтобы указать соответствующие параметры, которые должна принимать функция doMenu_Hide (аналогичные параметры получает также функция doMenu_Show).

Сейчас необходимо изменить тег <BODY> на <BODY ONLOAD="onBodyLoad();">. Готовая страница, в которой при загрузке отображаются не все элементы списка находится в архиве с примерами к статье (article_12_9.html).

В этой статье мы создали раскрывающийся список абсолютно разными способами: статичным, когда каждый элемент списка обращается к своей собственной функции; динамичным (с ипользованием рисунков и альтернативы в качестве текста). Автор надеется, что данная статья поможет вам создавать раскрывающиейся списки на ваших Web-страницах, и вы по достоинству оцените данный труд и все те преимущества, о которых говорилось выше в данной статье.

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

В качестве итога данной статьи хочется поделиться с вами иходными кодами списков, которые мы создали в этой статье.

Скачать примеры раскрывающихся списков (6.17 KB).

В середине июня 2011 года автором был создан специальный продукт, который позволяет генерировать древовидные раскрывающиеся страницы содержания для Web-страниц - Cherednichenko HTML TreeView Generator.

Источники