Создать сайт самосоятельно ...

К учебнику по Dinamic HTML

"Все Технические Моменты Сайтостроения в Видеоформате". Коллекция видеокурсов, которая за 36 часов и 45 минут сделает из Вас профессионала во всех технических моментах создания сайта.



Реклама:


Глава 13. Динамическое содержание



Термин динамическое содержание (dynamic contents) подразумевает возможность доступа и изменения части содержания документа без необходимости загрузки или создания новой страницы. Хорошим примером динамического содержания являются электронные часы, которые автоматически обновляются в HTML-документе каждую секунду без необходимости генерации нового документа.

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

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

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



Манипулирование содержанием

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

Первый подход использует методы write и writeln объекта document, которые могут вставлять содержание в текущий документ при его загрузке и могут конструировать документы, но они не могут изменять содержание, которое уже было проанализировано.

В главе 6 обсуждались методы write документа. Методы write и writeln поддерживаются в браузерах Netscape Navigator версии 2.0 и более поздних и Microsoft Internet Explorer 3.0.

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

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

В главе 14 рассматриваются сильные и слабые стороны объектной модели TextRange.



Свойства динамического содержания

Элемент Body и все элементы, находящиеся внутри него, представляют четыре свойства для доступа и изменения содержания HTML: innerHTML, innerText, outerHTML и outerText. Свойство innerHTML элемента представляет его содержание, включая разметку HTML для всех дочерних элементов. Свойство innerText представляет содержащийся текст без тегов HTML. Присвоение нового значения одному из внутренних свойств элемента заменяет содержание документа. Свойства outerHTML и outerText сходны со свойствами innerHTML и innerText, но они обращаются ко всему элементу, а не к его содержанию. Присвоение значения одному из внешних свойств элемента заменяет весь элемент. В приведенном ниже примере нажатие кнопки заменяет кнопку на полужирный текст Blown away!:

<HTML>
   <HEAD>
      <TITLE> Исчезающая кнопка </TITLE>
   </HEAD>
   <BODY>
      <INPUT TYPE=BUTTON VALUE="Blow me away!"
         ONCLICK="this.outerHTML = '<B>Blown Away!</B>'">
   </BODY>
</HTML>

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

Свойства динамического содержания используют достаточно строгие правила для определения действительности HTML. Эти правила более конкретны, чем правила, используемые для исходного анализа страницы, но они не настолько строги как определение типа документа (DTD) HTML. Если вы назначаете недействительный HTML одному из данных свойств, то может возникнуть ошибка и новое содержание не будет вставлено. Хотя свойства принимают некоторые недействительные теги HTML, вам следует всегда указывать разрешенный синтаксис HTML для получения предсказуемых результатов.

Кроме данных свойств каждый элемент в теле документа также представляет два метода для вставки содержания до или после открывающего или закрывающего тега: insertAdjacentHTML и insertAdjacentText. Эти два метода полезны для быстрой вставки новых параграфов или элементов списков в документ. На рис. 13.1 показаны все способы манипулирования содержанием элемента.

Рис. 13.1. Все точки доступа и изменения HTML и текста



HTML и свойства текста

Главное различие между свойствами HTML innerHTML и outerHTML с одной стороны и текстовыми свойствами innerText и outerText с другой заключается в том, что свойства HTML представляют разметку целиком, тогда как свойства текста представляют содержание без разметки. Рассмотрим следующий фрагмент HTML:

<H1>Welcome to <EM>Scott's</EM> Home Page</H1>

Для элемента H1 в данном фрагменте в табл. 13.1 перечислены значения каждого из четырех свойств.

Таблица 13.1. Свойства HTML и свойства текста


Свойство Значение

innerText Welcome to Scott's Home Page
innerHTML Welcome to <EM>Scott's</EM> Home Page
outerText Welcome to Scott's Home Page
outerHTML <H1>Welcome to <EM>Scott's</EM> Home Page</H1>

Свойства innerText и outerText всегда возвращают одинаковое значение, но ведут себя по-разному при назначении им новых значений. Установка значения для свойства innerText заменяет только содержание элемента H1 новым содержанием. Установка значения для свойства outerText заменяет элемент H1 и его содержание новым текстом. Например, установка значения "Thank you for visiting" (Спасибо, что зашли) для этих свойств будет иметь различный результат: для свойства innerText будет получен результирующий <H1> Thank you for visiting </H1>, а если значение присвоено свойству outerText, то в коде HTML будут отсутствовать теги: Thank you for visiting.

Разметка в значениях свойств innerHTML и outerHTML необязательно совпадает с разметкой в исходном коде. Напротив, удаляются избыточные пробелы и порядок атрибутов может быть изменен. Присваивая значения свойствам, связанным с HTML, убедитесь в использовании соответствующих escape-последовательностей любых компонентов (entity). Угловые скобки < и > интерпретируются как разделители тегов. Если угловые скобки должны быть включены в содержание и не должны анализироваться как HTML, то они должны быть определены как компоненты: &lt; и &gt;. Когда вы присваиваете значения текстовым свойствам, то данные скобки автоматически преобразуются в эквиваленты escape-последовательностей.


Неразрывные пробелы

Неразрывные пробелы (nonbreaking spaces) (пробелы, в которых запрещены символы перехода строки) и обычные пробелы считаются разными символами в объектной модели, где они представлены значениями ASCII 160 и 32, соответственно. Сравнение двух символов дает значение false, как показано в приведенном ниже примере:

<SPAN ID="s1"> </SPAN>
document.all.s1.innerText == " "  // false; не пробел

Для проверки наличия в содержании элемента неразрывного пробела проверьте непосредственно значение ASCII или сравните свойство HTML с самим компонентом, как показано ниже:


document.all.s1.innerHTML == " " // true

Любой определенный компонент, который совпадает со значением внутреннего компонента, преобразуется во внутреннее имя. Компонент неразрывного пробела может быть определен как &#160; вместо идентифицирующего его ключевого слова. Динамический HTML распознает данное значение как неразрывный пробел и преобразует его в &nbsp;.



Применение свойств динамического содержания

Самым простым способом изучения различий между свойствами динамического содержания элемента является анализ примеров. В приведенных ниже разделах представлены два примера: первый пример представляет собой анализ электронных часов из главы 4, а второй представляет игру в крестики-нолики, которая демонстрирует динамическое извлечение содержания и назначение содержания документу.


Электронные часы

В примере электронных часов из главы 4 для обновления значения времени было использовано свойство innerText. Элемент Span с идентификатором clock содержит текст с текущим временем. Каждую секунду сценарий вызывает функцию buildTime для создания строки со значением текущего времени и затем выводит строку в элементе Span с идентификатором clock, используя следующую инструкцию:


document.all.clock.innerText = buildTime();


Игра в крестики-нолики

Данный пример является интерактивной игрой в крестики-нолики с использованием динамического содержания. Таблица разделена на игровые ячейки. Когда пользователь выполняет щелчок в ячейке, то ее содержание заменяется на Х или О, для чего применяется свойство innerText. Размер игровой доски может динамически изменяться путем вставки новой таблицы вместо имеющейся при помощи свойства outerHTML.

<HTML>
   <HEAD>
      <TITLE> Крестики-нолики </TITLE>
      <STYLE TYPE="text/css">
         TD {font-weight:bold}
         #board TD {width:50px; height:50px; text-align:center;
            font-size:18pt; cursor:hand}
         .X {color:blue}
         .O {color:red}
         .draw {color:green}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">
         function TicTac() {
            // Объект для отслеживания игры
            this.lastMove = true;
            this.inProcess = true;
            this.scores = new Object();
            this.scores.xScore = 0; 
            this.scores.oScore = 0;
            this.scores.draws = 0;
            this.size = 3;
            this.drawBoard = initBoard;
         }

         function buildTable() {
            // Построение HTML-таблицы, которая должна 
            // быть вставлена в документ.
            var tb = "<TABLE BORDER ID=board
               ONCLICK='doBoardClick();'>";
            for (var intRow = 0; intRow < game.size; intRow++) {
               tb += "<TR>";
               for (var intCell = 0; intCell < game.size; intCell++)
                  tb += "<TD> </TD>";
               tb += "</TR>";
            }
            tb += "</TABLE>";
            return tb;
         }

         function initBoard() {
            document.all.board.outerHTML = buildTable();
            game.inProcess = true;
            game.lastMove = true;
         }

         function checkWinner(xCount, oCount) {
            // Обработка результатов для выяснения 
            // возможности победы.
            if (game.size == xCount) {
               alert("X Wins!");
               game.scores.xScore++;
               return false;
            }
            if (game.size == oCount) { 
               alert("O Wins!");
               game.scores.oScore++;
               return false;
            }
            return true;
         }

         function checkGame() {
            // Проверка всех направлений для 
            // выяснения вероятности выигрыша.
            var xCount = 0, oCount = 0, total = 0;
            var el = document.all.board;
            // Проверка строк.
            for (var intRows = 0; intRows < el.rows.length;
                  intRows++) {
               xCount = 0, oCount = 0;
               for (var intCells = 0;
                     intCells < el.rows[intRows].cells.length;
                     intCells++) {
                  var strCell = el.rows[intRows].cells[intCells];
                  if ("X" == strCell.innerText)
                     xCount++;
                  if ("O" == strCell.innerText)
                     oCount++;
               }
               game.inProcess = checkWinner(xCount, oCount);
               if (!game.inProcess) 
                  return;
               total += xCount + oCount;
            }
            // Проверка столбцов.
            for (var intCells = 0; intCells < el.rows.length;
                  intCells++)  {
               xCount = 0, oCount = 0;
               for (var intRows = 0;
                     intRows < el.rows[intCells].cells.length;
                     intRows++) {
                  var strCell = el.rows[intRows].cells[intCells];
                  if ("X" == strCell.innerText)
                     xCount++;
                  if ("O" == strCell.innerText)
                     oCount++;
               }
               game.inProcess = checkWinner(xCount, oCount);
               if (!game.inProcess) return;
            }

            // Проверка диагонали (от верхнего левого до 
            // нижнего правого угла).
            xCount = 0, oCount = 0;
            for (var intRows = 0; intRows < el.rows.length;
                  intRows++) {
               var strCell = el.rows[intRows].cells[intRows];
               if ("X" == strCell.innerText)
                  xCount++;
               if ("O" == strCell.innerText)
                  oCount++;
            }
            game.inProcess = checkWinner(xCount, oCount);
            if (!game.inProcess) return;

            // Проверка диагонали (нижний левый 
            // угол - верхний правый угол).
            xCount = 0, oCount = 0;
            for (var intRows = 0; intRows < el.rows.length;
                  intRows++) {
               var strCell =
                  el.rows[game.size - intRows - 1].cells[intRows];
               if ("X" == strCell.innerText)
                  xCount++;
               if ("O" == strCell.innerText)
                  oCount++;
            }
            game.inProcess = checkWinner(xCount, oCount);
            if (!game.inProcess)
               return;
            if (total == game.size * game.size) {
               alert("draw");
               game.inProcess = false;
               game.scores.draws++;
               return
            }
         }

         function updateScore() {
            // Вывод нового счета.
            for (scores in game.scores) 
               document.all[scores].innerText = game.scores[scores];
         }
            
         function doBoardClick() {
            if (game.inProcess) {
               if ("TD" == event.srcElement.tagName) {
                  var strCell = event.srcElement;
                  // Проверка доступности ячейки.
                  if (" " == strCell.innerHTML) {
                     strCell.innerText = (game.lastMove ? "X" : "O");
                     event.srcElement.className = 
                        game.lastMove ? "X" : "O";
                     game.lastMove = !game.lastMove;
                  }
               }
               checkGame();
               if (!game.inProcess)
                  updateScore();
            }
         }

         // Управление переменными игры
         var game = new TicTac;
      </SCRIPT>
      <SCRIPT FOR="size" EVENT="onclick()" LANGUAGE="JavaScript">
         // Разделяемый обработчик событий для 
         // кнопок-переключателей размера доски
         game.size = parseInt(this.value);
         game.drawBoard();
      </SCRIPT>
   </HEAD>
   <BODY>
      <H1>Tic-Tac-Toe</H1>
      <P><INPUT TYPE=BUTTON VALUE="New Game"
            ONCLICK="game.drawBoard();">
      <P><INPUT NAME=size TYPE=RADIO VALUE="3" ID="x3" checked>
         <LABEL FOR="x3">3 x 3</LABEL><BR>
         <INPUT NAME=size TYPE=RADIO VALUE="4" ID="x4">
         <LABEL FOR="x4">4 x 4</LABEL><BR>
         <INPUT NAME=size TYPE=RADIO VALUE="5" ID="x5">
         <LABEL FOR="x5">5 x 5</LABEL>
      <P>
      <SCRIPT LANGUAGE="JavaScript">
         document.write(buildTable());
      </SCRIPT>
      <TABLE>
         <TR class=x><TD>X Wins:</TD><TD ID=xScore>0</TD></TR>
         <TR class=o><TD>O Wins:</TD><TD ID=oScore>0</TD></TR>
         <TR class=draw><TD>Draws:</TD><TD ID=draws>0</TD></TR>
      </TABLE>
   </BODY>
</HTML>

На рис. 13.2 показана игра в крестики-нолики в действии.

Рис. 13.2. Крестики-нолики



Использование методов Adjacent

Методы insertAdjacentHTML И insertAdjacentText вставляют HTML и текст до или после открывающего или закрывающего тегов. Оба метода принимают два аргумента: первый аргумент определяет место вставки содержания, а второй определяет реальное содержание.

Четыре действительных значения для первого аргумента представляют соответственно четыре места вставки: beforeBegin, afterBegin, beforeEnd и afterEnd, где слово Begin представляет открывающий тег, а слово End - закрывающий. Эти методы полезны для вставки содержания, которое не оказывает влияния на текущее содержание документа.


Создание примечаний

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

<HTML>
   <HEAD>
      <TITLE> Динамические примечания </TITLE>
      <STYLE TYPE="text/css">    
         SPAN {display:none}
         SUP.FNID {color:blue; cursor:hand}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">
         function setupFootnotes() {
            // Получение семейства всех элементов Span.
            var spans = document.all.tags("SPAN");
            for (var i = 0; i < spans.length; i++) {
               var el = spans[i];
               // Если элемент является примечанием, то он обрабатывается.
               if ("footnote" == el.className) {
                  // Добавление номера примечания в виде цифры индекса.
                  el.insertAdjacentHTML("beforeBegin",
                     "<SUP CLASS=FNID>" + (i + 1) + " </SUP>");
                  // Связывание номера примечания с элементом Span.
                  document.all[el.sourceIndex - 1].linkFN = el;
               }
            }
         }

         function displayFN() {
            // Если пользователь щелкнул по номеру примечания, то на 
            // экран выводится его текст. 
            if ("FNID" == event.srcElement.className)
               if (null != event.srcElement.linkFN)
                  alert(event.srcElement.linkFN.innerText);
         }
      
         window.onload = setupFootnotes;
         document.onclick = displayFN;
      </SCRIPT>
   </HEAD>
   <BODY>
      <H1>Dynamic Footnotes
         <SPAN CLASS="footnote">
            Copyright (C) 1997 by Scott Isaacs.
         </SPAN>
      </H1>
      <P>Dynamic HTML is a "powerful way of creating Web pages"
         <SPAN CLASS="footnote">Scott Isaacs, "Inside Dynamic HTML."
         </SPAN>
         and "Soon Dynamic HTML will be used in most applications."
         <SPAN CLASS="footnote">
            Joe-Cool Developer, "The Future of the Web."
         </SPAN>
      <P>This page automatically generates and numbers the footnotes
         at load time. The footnotes are stored as hidden contents on
         the page.</P>
   </BODY>
</HTML>

Вы можете вывести на экран примечания в виде всплывающих подсказок, а не в окнах сообщений, установив атрибут TITLE каждого номера примечания для текста примечания. Можно также вывести содержание примечания в тексте документа, после щелчка по номеру примечания. Для применения данного метода настройте функцию displayFN для изменения атрибута стиля display текста примечания.


Создание полей со списком в формате HTML

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

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

<HTML>
   <HEAD>
      <TITLE> Индивидуальные HTML-поля со списками </TITLE>
      <STYLE TYPE="text/css">
         .list {cursor:hand; overflow:auto; height:75pt; width:150pt;
            border:1pt black solid}
         .list UL {list-style-type:none; margin-left:2pt;
            margin-top:0pt; margin-bottom:0pt}
         .list UL LI {margin-top:0pt; margin-bottom:0pt}
         .list UL LI.selected {background:navy; color:white}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">
         function checkParent(src, tag) {
            while ("HTML" != src.tagName) {
               if (tag == src.tagName)
                  return src;
               src = src.parentElement;
            }
            return null;
         }

         function selectItem(list) {
            var el = checkParent(event.srcElement, "LI");
            if ("LI" == el.tagName) {
               if (null != list.selected)
                  list.selected.className = "";
               if (list.selected != el) {
                  el.className = "selected";
                  list.selected = el;
               }
               else
                  list.selected = null;
            }
         }

         function copy(src, dest) {
            var elSrc = document.all[src];
            var elDest = document.all[dest];
            if (elSrc.selected != null) {
               elSrc.selected.className = "";
               elDest.insertAdjacentHTML("beforeEnd",
                  elSrc.selected.outerHTML);
               elSrc.selected.outerHTML = "";
               elSrc.selected = null; // начальная установка выбора
            }
         }
      </SCRIPT>
   </HEAD>
   <BODY>
      <H1>Custom HTML List Boxes</H1>
      <P>The bulleted lists simulate rich HTML selection lists.</P>
      <TABLE>
         <TR> 
            <TD>
               <DIV CLASS="list">
                  <UL ID="src" ONCLICK="selectItem(this);"
                        ONDBLCLICK="copy('src', 'dest');">
                     <LI>Scott's <EM>Home</EM> Page</LI>
                     <LI>Parents' Home Page</LI>
                     <LI><IMG SRC="foo.gif"></LI>
                     <LI>Inside Dynamic HTML Home Page</LI>
                     <LI>Microsoft Home Page</LI>
                     <LI>Item 6</LI>
                     <LI>Item 7</LI>
                  </UL>
               </DIV>
            </TD><TD>
               <P><INPUT TYPE=BUTTON VALUE="—>"
                  ONCLICK="copy('src', 'dest');">
               <P><INPUT TYPE=BUTTON VALUE="<—"
                  ONCLICK="copy('dest', 'src');">
            </TD><TD>
               <DIV class="list">
                  <UL ID="dest" ONCLICK="selectItem(this);"
                     ONDBLCLICK="copy('dest','src');">
                  </UL>
               </DIV>
            </TD>
         </TR>
      </TABLE>
   </BODY>
</HTML>

На рис. 13.3 показаны индивидуальные поля со списками.

Рис. 13.3. Два поля со списками, созданными на основе существующих HTML-элементов



Доступ к содержанию

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


if ("complete" == document.readyState) {
   // Манипулирование содержанием.
}
else {
   // Отображение предупреждения или 
   // выполнение альтернативного действия.
}


Обработка ошибок изображений

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

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

<HTML>
   <HEAD>
      <TITLE> Обработка ошибок изображений </TITLE>
      <STYLE TYPE="text/css">
         SPAN.error {background:yellow; font-weight:bold}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">
         var Errors = new Array();
         Errors[0] = 0;

         function badImage(el) {
            if (document.readyState != "complete") {
               Errors[0]++;
               Errors[Errors[0]] = el;
            }
            else  // Документ загружается. Прямой вывод ошибки.
               el.outerHTML =
                  "<SPAN CLASS='error'>Error Loading Image: " +
                     el.title + "</SPAN>";
         }
 
         function reviewErrors() {
            for (var i = 1; i <= Errors[0]; i++)
               Errors[i].outerHTML =
                  "<SPAN CLASS='error'>Error Loading Image: " +
                     Errors[i].title + "</SPAN>";
         }
  
         window.onload = reviewErrors;
      </SCRIPT>
   </HEAD>
   <BODY>
      <P><IMG SRC="bad.gif" ONERROR="badImage(this);"
         TITLE="Cool Picture">
      <P><A HREF="http://www.insideDHTML.com">
         <IMG SRC="bad.gif" ONERROR="badImage(this);"
            TITLE="Inside Dynamic HTML Web Site"></A>
   </BODY>
</HTML>

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

Если замена изображения на текст невозможна, то можно легко изменить атрибут title, добавив сообщение об ошибке. (Атрибут title в Internet Explorer 4.0 отображается как всплывающая подсказка). Данная модификация может быть реализована без создания очереди ошибок, потому что атрибуты элементов могут быть изменены до загрузки страницы. Приведенный ниже фрагмент кода демонстрирует добавление данного элемента. Эта схема работает без дополнительного кода:

<IMG SRC="bad.gif" TITLE="Cool Picture"
   ONERROR="this.title = 'Error Loading: ' + this.title";>



Динамическое содержание и метод document.write

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



Создание строки заголовка

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

<HTML>
   <HEAD>
      <TITLE> Документ баннера </TITLE>
      <STYLE TYPE="text/css">  
         DIV#bannerContents {display:none}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">
         function outputBanner() {
            if (null != parent.frames[0]) {
               parent.frames[0].document.open();
               parent.frames[0].document.write(
                  document.all.bannerContents.outerHTML);
               parent.frames[0].document.close();
            }
            else  // Отсутствует набор фреймов; включается баннер.
               document.all.bannerContents.style.display = "block";
         }

         window.onload = outputBanner;
      </SCRIPT>
   </HEAD>
   <BODY>
      <DIV ID=bannerContents>
         <H1>Inside Dynamic HTML</H1>
      </DIV>
      <P><EM>Inside Dynamic HTML</EM> teaches the Web developer 
         how to create interactive and live Web pages.</P>
   </BODY>
</HTML>

Элемент DIV баннера может содержать любой код HTML, включая сценарии. Все содержание будет скопировано в другой фрейм.

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

<HTML>
   <HEAD>
      <TITLE> Набор фреймов баннера </TITLE>
      <SCRIPT LANGUAGE="JavaScript">
         function fixup() {
            // Автоматическое изменение размера фрейма баннера.
            document.all.FS.rows =
               window.frames.Banner.document.body.scrollHeight +
               ", *";
         }

         window.onload = fixup;
      </SCRIPT>
   </HEAD>
   <FRAMESET ROWS="100, *" ID="FS" FRAMEBORDER=0>
      <FRAME NAME="Banner" SCROLLING=NO NORESIZE>
      <FRAME SRC="Banner.htm">
   </FRAMESET>
</HTML>

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



Расширенные индексы и таблицы содержания

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

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


Индексы ссылок и закладок

Приведенный ниже код создает окно индекса, содержащее копии всех элементов Anchor документа. Код использует свойства innerHTML всех якорей для копирования их содержания, поэтому изображения корректно воспроизводятся в окне индекса.

<HTML>
   <HEAD>
      <TITLE> Автоиндексация </TITLE>
      <SCRIPT LANGUAGE="JavaScript">  
         function setupIndex() {
            // Open new window.
            var winIndex = null;
   
            /* Если имеется набор фреймов и фрейм меню, то вывести
               в данном фрейме. В противном случае вывести в новом окне.  */
            if (window.parent != self)
               if (null != parent.menu)
                  winIndex = parent.menu;
            if (null == winIndex)
               winIndex = window.open("", "Index",
                  "width=300; height=500");
            // Запустить создание документа индекса.
            winIndex.document.open();
            winIndex.document.write("<HTML>");
            winIndex.document.write("<TITLE>Index</TITLE>");

            // Определение базового HREF и базовой цели.
            var baseHREF = null;
            var baseTarget = null;
            // Проверка наличия тегов <BASE> в документе.
            var base = document.all.tags("BASE");
            for (var i = 0; i < base.length; i++) {
               // Извлечение базового HREF и цели, если она указана.
               if (null != base[i].href)
                  baseHREF = base[i].href;
               if (null != base[i].target)
                  baseTarget = base[i].target;
            }
  
            // Установка имени целевого окна в случае 
            // отсутствия базового HREF.
            if ((null == baseTarget) || ("" == baseTarget)) {
               if ("" == window.name)  
                  window.name = "outputhere";
               baseTarget = window.name;
            }
  
            // В случае отсутствия базового HREF установить текущий путь.
            if ((null == baseHREF) || ("" == baseHREF)) {
               baseHREF = location.protocol + location.pathname;
            }

            // Вывод базового HREF в окне.
            winIndex.document.writeln("<BASE TARGET=" +
               "'" + baseTarget + "'" + "HREF='" + baseHREF + "'>");
            winIndex.document.writeln("<H1>Links</H1>");
            // Поскольку используется innerHTML, то изображения и
            // расширенный HTML извлекаются автоматически.

            // Перечисление всех элементов Anchor; 
            // пропуск карт изображений.
            for (var i = 0; i < document.links.length; i++) {
               var el = document.links[i];
               if ("A" == el.tagName) {
                  var hText = el.outerHTML;
                  winIndex.document.writeln("<P>" + hText);
               }
            }

            winIndex.document.writeln("<H1>Bookmarks</H1>");
            // Перечисление всех закладок.
            for (var i = 0; i < document.anchors.length; i++) {
               var el = document.anchors[i];
               if ("A" == el.tagName) {
                  var hText = el.innerHTML;
                  winIndex.document.writeln(
                     "<P><A HREF='#" + el.name + "'>" + hText +
                     "</A>");
               }
            }
            
            winIndex.document.close();
         }
         window.onload = setupIndex;
      </SCRIPT>
   </HEAD>
   <BODY>
      <H1><A NAME="top">Auto Indexing</A></H1>
      <H2><A NAME="links">Link Demonstrations</A></H2>
      <P>The following links will appear in the link index:</P>
      <P><A HREF="http://www.insideDHTML.com">Inside Dynamic HTML</A>
      <P><A HREF="http://www.microsoft.com">Microsoft's Web Site</A>
      <P>Images also work:
      <P><A HREF="http://www.insideDHTML.com"><IMG SRC="open.gif"></A>
      <P>Rich HTML anchors are automatically picked up:
      <P><A HREF="http://www.insideDHTML.com">Inside <EM>Dynamic</EM>
         HTML</A>
   </BODY>
</HTML>


Таблица содержания

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

<HTML>
   <HEAD>
      <TITLE> Манипулирование заголовками </TITLE>
      <SCRIPT LANGUAGE="JavaScript">  
         // Переменная для таблицы содержания
         var winTOC = null;

         function setupHeaders() {
            var levels = new Object;
            var level = 0;

            if (window.parent != self)
               if (null != parent.menu)
                  winTOC = parent.menu;
            if (null == winTOC)
               winTOC = window.open("", "Index",
                  "width=300; height=500");
      
            winTOC.document.open();
            winTOC.document.write("<HTML>");

            winTOC.document.writeln("<TITLE>Contents</TITLE>");
            // Запись обработчика события нажатия кнопки для 
            // прокручивания элемента в окне.
            winTOC.document.writeln("<SCRIPT> function gotoHeader()" +
               "{if (event.srcElement.linkTo != null)" +
               "event.srcElement.linkTo.scrollIntoView(true)} </" +
               "SCRIPT>");
            winTOC.document.writeln("<BODY ONCLICK='gotoHeader()'" +
               "STYLE='cursor:hand'" +
               "onmouseover='if (event.srcElement.tagName ==" +
               ""SPAN")" +
               "event.srcElement.style.textDecorationUnderline " +
               "= true' onmouseout = " +
               "'event.srcElement.style.textDecorationUnderline " +
               "= false'>");
            winTOC.document.writeln("<H1>Contents</H1>");
            var level = 0;

            // Перечисление всех элементов Heading.
            for (var i = 0; i < document.all.length; i++) {
               var el = document.all[i];
               var char1 = el.tagName.substring(0, 1);
               var val2 = parseInt(el.tagName.substring(1, 2));
               if (("H" == char1) && (2 == el.tagName.length) &&
                     (val2 > 0) && (val2 < 7)) {
                  // Установка вложенного или простого списка.
                  if (val2 > level)
                     for (; level < val2; level++) {
                        if (levels[level] == null)
                           levels[level] = 0;
                        winTOC.document.writeln("<DL>");
                     }
                  else if (level > val2)
                     for (; level > val2; level--) {
                        levels[level - 1] = 0;
                        winTOC.document.writeln("</DL>");
                     }
                  levels[level - 1]++;
                  var hText = document.all[i].innerText;
                  winTOC.document.writeln("<DT><SPAN>");
                  var strNum = "";
                  for (var iOut = 0; iOut < level; iOut++) {
                     winTOC.document.write(levels[iOut].toString() +
                        ".");
                     strNum += levels[iOut] + ".";
                  }
                  document.all[i].insertAdjacentText("afterBegin",
                     strNum + " ");
                  winTOC.document.writeln(" " + hText + "</SPAN>");

                  // Добавляет свойство со ссылкой на заголовок.
                  winTOC.document.all[winTOC.document.all.length _
                     1].linkTo = el; 
               }
            }
            winTOC.document.close();
         }

         window.onload = setupHeaders;
         // Если содержание выводится во фрейме, то данный обработчик 
         // события unload удаляется. 
         window.onunload = new Function
            ("if (!winTOC.closed) winTOC.close()");
      </SCRIPT>
   </HEAD>
   <BODY ONLOAD="setupHeaders();">
      <H1>Dynamic HTML Auto-Numbering</H1>
      <P>All the headers are automatically numbered and
         a table of contents is generated.</P>
      <P>Below are sample headings to be numbered.
      <H2>Finds all the headers.</H2>
      <P>All done automatically!</P>
      <H2>Automatically fills in a heading number.</H2>
      <H1>Better Performance and Less Maintenance</H1>
      <H2>Just maintain the page.</H2>
      <H2>Don't worry about renumbering headings.</H2>
      <H3>Test header 3</H3>
      <H2>No need to maintain separate contents document.</H2>
   </BODY>
</HTML>

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

На рис. 13.4 показана работа приложения Table of Contents. Таблица содержания и нумерованные заголовки автоматически генерируются после загрузки страницы.

Рис. 13.4. Таблица содержания и нумерованные заголовки

Урок 14. Пользовательские операции выделения и редактирования