Веб-сайт с минимальными затратами: учебник HTML

Глава 11. DHTML для нашего сайта

Глава 11. DHTML для нашего сайта

Ссылки не всегда должны открывать файлы

Элемент a (от англ. anchor - «якорь») всегда даёт браузеру определённую команду: открыть файл, путь к которому записан в атрибуте href . Но что значит для браузера «открыть файл»? Если этот файл имеет внутри html-код, тогда понятно: браузер выведет на экран в своём собственном окне содержимое файла в соответствии с законами HTML-CSS. Файлы картинок в форматах GIF, PNG, JPEG браузеры тоже понимают и выводят на экран изображения (а не код файла картинки). А если открываемый файл - документ MS Word? Или какой-нибудь PDF? Браузер, как и любая другая программа, не может уметь открывать все существующие типы документов. Но приказ есть приказ, и он должен попытаться. Если сам браузер уже понял, что не сможет открыть данный файл (он определил это по расширению файла), то он начинает шарить в реестре Windows, в ветви HKEY_CLASSES_ROOT в поисках зарегистрированных типов файлов. То есть браузер начинает действовать как Проводник Windows - пытаться открыть файл программой, которая назначена данному типу файлов в реестре. Если на компьютере установлен MS Office, то все файлы с расширением DOC Проводник будет пытаться открыть программой MS Word. Так же будет поступать и браузер: запустит MS Word, и уже Word начнёт открывать файл с расширением DOC, обнаруженный вами в Интернете.

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

Лучше лишний раз застраховать пользователя от таких неожиданностей (тем более, что это основа «хорошей» идеологии веб-сайта - «пользователь контролирует всё!»). В первой части книги мы уже говорили о том, что можно, например, выкладывать в Интернет прайс-листы в формате MS Excel (если указать формат и размер файла в явном виде). Но всегда существует риск, что для пользователя эти сведения ничего не значат; увидев заветное слово «прай-лист», он просто радостно щёлкнет по ссылке. И вот тут-то мы ему и вставим фитиль: вместо открываемого файла выдадим на экран сообщение: «Вам же было сказано: 400 килобайт! Если так уж сильно хотите посмотреть, что там внутри, щёлкните по ссылке правой кнопкой мыши и выберите пункт "Сохранить объект как"».

Сделать это достаточно просто, не надо даже изменять атрибут ссылки href . Дело в том, что, прежде чем браузер начнёт выполнять команду ссылки, ему надо будет прочесть значение атрибута href (путь к файлу); но раньше в окне браузера возникнет событие onclick («при щелчке мышью»). Вот это событие мы и перехватим , добавив ссылке ещё один атрибут (он так и называется - onclick) с несложным значением "javascript:return false":

<a href="price1.xls" onclick="javascript:return false">price1.xls</a>, прайс в формате MS Excel, 400 Кб, сохранять на диск правой кнопкой мыши

Интуитивно ясно, что «return false» («вернуть ложь») означает для браузера: ничего «по общей программе» с этой ссылкой не делать и оставить пользователя любоваться текущим состоянием экрана.

Непосредственно рядом со ссылкой мы написали уведомление о размере файла и о том, что пользователь может сохранить его себе на компьютер, воспользовавшись правой кнопкой мыши. Это, вроде бы, тоже событие - щелчок правой кнопкой, но уже другое событие, оно в объектной модели документа называется oncontextmenu . Его тоже можно настраивать по своему усмотрению (например, так, чтобы пользователь и правой кнопкой ничего не мог сделать).

По событию onclick можно также добавить и небольшое сообщение для пользователя о том, что он не прав. А после сообщения - опять-таки «вернуть назад»:

onclick="javascript:alert('Сказано же, правой кнопкой!');return false"

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

Немного о фреймах

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

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

Потребность эта такова: сохранять часть содержимого экрана в неизменном виде и произвольно менять другую часть содержимого. То есть собственно Dynamic HTML. Мы видели, как это можно делать с примечаниями: то скрывать их, то показывать; но при более значительных объёмах сменяемого содержимого приходится обращаться к другим html-файлам (потому что одна-единственная страница стала бы очень большой по объёму и слишком долго бы загружалась). Вот как раз это - обращаться к другим html-файлам, оставляя часть экрана неизменной, - и позволяют самым простейшим образом делать фреймы: в одном кусочке окна браузера вы постоянно видите один и тот же файл (например, оглавление), а в другом - разные файлы, загружаемые щелчком по разным ссылкам (например, главы книги).

На нашем сайте тоже, в общем-то, была такая проблема: как сохранить на всех страницах одну и ту же «шапку» - логотип, название фирмы, навигационную панель... Мы решили эту проблему ещё более «тупым» способом, чем фреймы: html-код «шапки» в неизменном виде повторяется у нас на каждой странице сайта; но пользователь об этом не знает, и при щелчке мышью по ссылкам навигационной панели возникает полная иллюзия, что страница с «шапкой» - одна и та же, но некоторая часть текста на ней меняется. Такой псевдо-динамический HTML.

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

DHTML-шутка: сайт в одной странице

Скопируем коды всех перечисленных в навигационной панели страниц нашего сайта в первую страницу - index.html, затем, чтобы можно было управлять содержимым, заключим код каждой отдельной страницы в тэги <div class="text" id="d..."> и </div>. Мы назначили коду каждой страницы уникальный идентификатор - id, равный значениям "d01", "d02" и т. д. Каждому элементу, содержащему код исходных страниц, назначили также className (="text"), чтобы можно было управлять всеми «страницами» сразу:

<div class="text" id="d01"> <h1>Торговая группа Omnia mea</h1> <p>Торговля разнокалиберными товарами оптом, в розницу и на экспорт </p> </div> <div class="text" id="d02"> <h1>Адреса</h1> ... 

ex26  Файлы примеров: dhtml1 (http://figur.ir2.ru/html/omne/dhtml1)

В файле стилей dn.css запишем правило для разделов класса "text":

div.text {margin:0; display:none; }

Понятно, что после применения этого правила мы не увидим на странице вообще ничего, кроме шапки. Поэтому после открытия страницы надо применить к одному из разделов другое css-правило (типа "display:block"). Для этого в объектной модели документа и существует такое событие - onload («после окончания загрузки»). Оно наступает для каждого элемента страницы по мере их обработки браузером. Мы перехватим это событие для элемента body, изменив html-код для body следующим образом:

<body onload="topage('d01')">

То есть после окончания загрузки body будет запущена функция topage() с параметром 'd01'. Эту функцию надо, разумеется, написать; сохранить запись в файл, например, js.js и привязать этот файл к нашей html-странице с помощью атрибута src тэга script (запись должна быть помещена в раздел документа head ):

<script src='js.js'></script>

Код функции:

function topage(p) { var obj=document.getElementById(p); obj.style.display="block"; } 

Из этого кода видно, что функция будет с помощью метода getElementById(p) искать элемент по идентификатору p ; а идентификатор этот передаётся в качестве параметра и в нашем случае равен "d01". Функция по этому идентификатору найдёт первый раздел страницы и сделает его видимым.

Эту же функцию мы должны записать в качестве значения во все атрибуты href ссылок навигационной панели (изменяя каждый раз соответствующим образом идентификатор):

<p class="nav"> <a href="javascript:topage('d01')">Главная</a> • <a href="javascript:topage('d02')">Реквизиты</a> • ... 

Теперь при щелчке по ссылкам навигационной панели соответствующий идентификатору раздел становится видимым на экране. Но разделы, «посещённые» нами раньше, тоже остаются при этом видимыми. Так неинтересно. Надо добавить функцию, удаляющую с экрана ненужные в данный момент разделы. Мы, конечно, поступим проще: удалим сначала с экрана все сменяемые разделы, а затем покажем текущий. Для этого используем метод getElementsByTagName("div") - «найти все элементы, имеющие метку "div"», потом проверим className каждого найденного элемента, и если он равен "text", сделаем его невидимым:

function hideall() { var divs=document.getElementsByTagName ("div"); for (i=0; i<divs.length; i++) { if (divs[i].className=="text") { divs[i].style.display="none"; } } }

Конструкция «for (i=; i<; i++) {действия}» уже встречалась вам во II Части книги, в Главе 6. Метод getElementsByTagName выдаёт нам ( возвращает , записывает в переменную - в нашем случае divs ) не один элемент, а сразу целый список элементов. Такие списки ( упорядоченных элементов ) в JavaScript называются массивами . Конкретный элемент массива можно получить с помощью квадратных скобок после наименования массива: divs[0] (нумерация элементов начинается с нуля). Свойство массива length содержит число элементов массива. Из-за начала нумерации с нуля номер последнего элемента будет на единицу меньше длины всего массива (значения length), поэтому мы используем в операторе for выражение i<divs.length , а не i<=divs.length .

Эту функцию, скрывающую все сменяемые элементы (hideall()), мы должны вызывать каждый раз вместе с функцией, отображающей нужный элемент. Поэтому просто впишем имя новой функции первой строкой в начало функции topage(p):

function topage(p) { hideall(); var divs=document.getElementsByTagName ("div"); ... 

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

Правда, у пользователя, поползавшего некоторое время по Интернету, вырабатываются разные рефлексы: например, открывать ссылки правой кнопкой - чтобы в новом окне (вкладке). Про наши ссылки пользователь ещё не знает, что их бессмысленно пытаться открывать в новом окне, а объяснять ему это - замучаешься, лучше перепрограммировать упомянутое выше событие oncontextmenu (добавить соответствующий атрибут каждой ссылке):

<p class="nav"> <a href="javascript:topage('d01')" oncontextmenu="topage('d01')">Главная</a> • ... 

Теперь даже при щелчке правой кнопкой в нашем окне будут открываться «новые страницы». Правда, потом из-под мыши вывалится контекстное меню, что не очень красиво выглядит. Красоту в этом отношении можно, к сожалению, навести не в каждом браузере: в IE можно программно назначить отмену стандартной обработки события oncontextmenu достаточно просто: «window.event.returnValue = false;». В других браузерах подобное выражение вызывает ошибку. Поэтому придётся определять тип браузера. А также проверять, происходит ли вообще какое-либо событие (window.event):

var agent = navigator.userAgent.toLowerCase(); var j; if (agent.indexOf("msie") > -1 && agent.indexOf("opera") == -1) j=2; else j=1; function topage(p) { hideall(); var obj=document.getElementById(p); obj.style.display="block"; if (j==2 && window.event) window.event.returnValue = false; } 

В браузерах Gecko отменить событие оказалось сложнее. Вернее, учесть все особенности (сам-то код в итоге получается простым): надо не только отменить событие методом preventDefault() , но и остановить его распространение вверх по дереву DOM (оказывается, если событие наступило для ссылки, оно потом последовательно наступает для абзаца, в который вложена ссылка, для раздела div, для body...)! Сложная, в общем, идеология:

 oncontextmenu=function(e){e.preventDefault(); e.stopPropagation();} 

Эту строчку надо вписать в любое место файла js.js, лишь бы не внутри функций. Просто «oncontextmenu», без указания элемента, к которому относится это свойство, означает, что мы пытаемся присвоить его объекту window - самому верхнему в иерархии событий DOM. То есть слово «window» в таком контексте можно просто пропустить. Всё равно перехват событий будет происходить для любого элемента в окне. А это значит, мы опять угодили пальцем в небо: зачем же раздражать пользователя отсутствием контекстного меню на обычных, простых ссылках? Или, может, он захочет посмотреть какие-то свойства страницы и щёлкнет правой кнопкой в пустом месте документа - а в ответ тишина. Вернее, пустота. Придётся добавить в нашу функцию перехвата пару условных операторов, первый из которых будет определять, щёлкнули ли мы по ссылке, а второй - обычная это ссылка или запускающая JavaScript:

 oncontextmenu=preventdef; function preventdef(e){ if (e.target.tagName=="A"){ if (e.target.href.indexOf("javascript")>-1){ e.preventDefault(); e.stopPropagation(); } } return true; } 

Метод анализа элемента, по которому щёлкнули, выглядит не очень надёжным: вдруг, например, «Javascript» в html-документе будет написано с большой буквы. Ну так ведь мы же сами его писали, должны помнить, как именно написано. А для других документов придётся писать другую функцию... Не универсально как-то получилось. Ну, попытайтесь сделать лучше. А у меня пока осталась ещё одна проблема: наша псевдо-загрузка страниц выдаёт себя с головой. Вернее, «головой» - заголовком окна. Этот заголовок у нас не меняется, так как страница остаётся одна и та же. Это можно исправить.

Обыкновенно: пишем в функции document.title=«новое значение». Осталось определить, откуда брать это новое значение. Можно дописать его в явном виде в текст каждой ссылки в виде дополнительного атрибута. Но это не очень красиво, файл будет разбухать без большой причины. В общем-то у нас уже есть такой текст на каждой странице, который соответствует заголовку окна: первый заголовок, элемент h1 : «Адреса», «Каталог продукции», «История»... Правда, название самой фирмы есть у заголовка h1 только на первой странице, поэтому придётся проверять id страницы, и если он не равен "d01", дописывать название предприятия, чтобы title получался: «Торговая группа Omnia mea. Каталог продукции».

Как передать функции JavaScript topage(p) текст нужного заголовка h1? При запуске функции мы находим по идентификатору текущий раздел (элемент div ). Элемент h1 является первым элементом, вложенным в текущий раздел, то есть первым потомком элемента div (записанного у нас, кстати, в переменную obj). Это значит, мы можем найти нужный заголовок по такой формуле: ofc=obj.firstChild. Вы уже немного знакомы с теорией DOM - Node (с узлами объектной модели документа) HTML: у найденного нами объекта h1, записанного теперь в переменную ofc , должен быть первый потомок - текст, а у этого текста - значение (nodeValue). Так уж всё придумали в W3C. Поэтому фрагмент функции, формирующий заголовок окна, должен выглядеть примерно так:

... var ofc=obj.firstChild; var h1 = ofc.firstChild.nodeValue; var m="Торговая группа Omnia mea"; document.title=(p=="d01")?m:m+". "+h1; ... 

В последней строчке кода мы использовали Условную операцию («?»): (проверяемое условие)?«действия, если условие выполнено»:«действия, если условие не выполнено». Условие у нас такое: идентификатор текущего раздела равен "d01" (у этого раздела заголовок h1 включает название фирмы). То есть если текущий раздел не "d01", мы должны добавлять впереди к элементу title название фирмы (записанное нами в переменную m ), затем точку с пробелом. Кажется, всё: в Интернет Эксплорере работает, проверяем в Firefox (Gecko)... Проблемы!

Используя Инспектор DOM Firefox'а, обнаруживаем неприятную вещь: первым потомком наших главных разделов div является не заголовок h1 , а text ! Какой ещё, в космос, текст, когда мы не печатали между div и h1 ни одного символа!

<div class="text" id="d02"> <h1>Адреса</h1> <p>г. Одесса, Малая Арнаутская, 22 

В примере с примечаниями (omne\js5) мы должны были удалить между следующими друг за другом элементами точку и пробел. Но в html-коде файла index.html нет между тэгами < div class="text" id="d02"> и < h1 > никаких точек с пробелами! Однако чудес не бывает. Включите в своём текстовом редакторе Bred «Показывать управляющие символы» (через меню Настройки - Параметры) и увидите следующую картину:

par1.gif

Похожую картину можно увидеть и в MS Word, если включить показ непечатаемых символов:

par1.gif

В конце строки < div class="text" id="d02"> мы всё-таки ввели один символ нажатием клавиши Enter - символ конца абзаца. Для браузеров Gecko это тоже текст. Для IE - в данном случае нет. По правилам W3C знак конца абзаца в HTML-коде должен интерпретироваться обозревателями как пробел. А если, например, знаков абзаца (или знаков пробела) несколько подряд, обозреватели должны превращать их в один пробел. Есть такие правила в HTML. Только разные браузеры их выполняют по-разному. Поэтому придётся дописывать в функцию topage(p) обработку этих разных ситуаций. В данном случае нет смысла использовать идентификацию браузера (мы не знаем, как будут себя вести другие), лучше рассмотреть варианты «первый потомок - text» и «первый потомок - h1» (других-то вариантов на нашей странице нет).

Как узнать, является узел текстом или элементом? Для этого существует такое свойство узлов, как nodeType ; если проверить это свойство для нашего объекта ofc (например, вставив в функцию строчку: alert(ofc.nodeType); ), мы увидим, что в IE тип первого узла-потомка для нашего раздела div будет равен «1» («Элемент»), а в Firefox - «3» («Текст»). Нам не обязательно знать сейчас полностью, что всё это значит, достаточно того, что мы с помощью метода alert увидели числа «1» и «3», их-то мы и вставим во вновь добавленную условную операцию:

... var h1 = (ofc.nodeType==3) ? ofc.nextSibling.firstChild.nodeValue : ofc.firstChild.nodeValue; ... 

Если первый потомок - Текст, мы находим для него «следующего потомка того же родителя» (nextSibling). Весь код нашей функции теперь будет выглять так:

function topage(p) { hideall(); var obj=document.getElementById(p); obj.style.display="block"; /*формирование заголовка окна (title)*/ var ofc=obj.firstChild; var h1 = (ofc.nodeType==3) ? ofc.nextSibling.firstChild.nodeValue : ofc.firstChild.nodeValue; var m="Торговая группа Omnia mea"; document.title=(p=="d01")?m:m+". "+h1; /*отмена встроенных событий для IE*/ if (j==2 && window.event) window.event.returnValue = false; } 

После внесённых изменений в файл js.js в Интернет Эксплорере и браузерах Gecko динамическая страница будет работать практически идеально; в Опере - будет работать только по щелчку основной кнопкой мыши (всякие oncontextmenu она хладнокровно игнорирует).

Динамическая загрузка прайс-листа в том же окне

Пользователю структура веб-сайта «пять в одном», может быть, и удобна, а вам - не всегда. Например, наш небольшой прайс-лист по металлопрокату занимает всего 15 килобайт, его код вполне можно было бы тоже дописать к основной странице и показывать-скрывать «динамически», а не с помощью открытия нового html-файла. Но как вы собираетесь потом этот прайс-лист обновлять? Ответ на этот вопрос - одна из решающих причин модульного строения сайта. В первой части книги мы выстроили технологию обновления прайс-листа: из бухгалтерской программы - в Excel, из Excel - в HTML, затем html-файл просто копируется на интернет-сервер. Если же наш прайс-лист будет входить в состав другого файла, нам придётся вручную открывать этот другой файл в текстовом редакторе и копировать туда обновлённый код прайс-листа. Это дольше, к тому же возрастает риск ошибки. То есть это нетехнологично .

Но экранное место нашей страницы pricelist.html вполне позволяет уместить рядом со ссылками текст одного небольшого прайс-листа. Это место пустует. Оно, можно даже сказать, взывает к нам своей пустотой. Тут как раз может помочь идеология фреймов - показ на фрагменте данной страницы содержания другой страницы. Только мы не хотим использовать «обычные» фреймы.

Существует другая возможность, реализованная, по меньшей мере, двумя элементами HTML: iframe и object . В этом примере мы используем первую. Сам код включения одной страницы в другую достаточно прост, добавим его в конец файла pricelist.html, перед закрывающим тэгом </body>:

... <a href="price2.zip">Компьютеры, html в zip-архиве, 19 Kb</a> </p> </div> <iframe id="tovar" frameborder="0" marginwidth="0" marginheight="0" src="../img/price1.html"></iframe> </body></html> 

ex27  Файлы примеров на CD omne\dhtml2 (http://figur.ir2.ru/html/omne/dhtml2)

После записи этого кода и сохранения файла, если открыть страницу pricelist.html в браузере, вы увидите внизу небольшой прямоугольник с двумя полосами прокрутки, в котором находится всё содержимое страницы price1.html. Полосы прокрутки у отдельного фрагмента страницы имеют смысл, когда таких фрагментов на странице несколько. В нашем случае это не так, поэтому лучше от них избавиться: увеличить с помощью правил CSS ширину и высоту элемента iframe . После нескольких экспериментов находим приблизительно минимальные значения: 47 и 260 em и записываем в файл dn.css правило для именованного элемента tovar :

#tovar {display:none; width:47em; height:260em; border:0; padding:0;background:transparent;}

Как видите, кроме правильной ширины и высоты мы задали нашему iframe'у ещё и невидимость, полное отсутствие на странице. Видимым он должен становиться по щелчку на ссылке первого прайса в списке. Для этого мы должны изменить её код (в файле pricelist.html):

<a id="tovarhr" href="javascript:tovarshow()" title="Просмотр прайс-листа">Показать прайс-лист 'Металлопрокат'</a>

Ну, и записать в файл js.js, привязанный к странице элементом script , две функции: для показа первого прайса и для сокрытия его обратно:

var t, thr; function tovarshow() { t=document.getElementById("tovar"); thr=document.getElementById("tovarhr"); thr.href="javascript:tovarhid()"; thr.title="Скрыть"; thr.firstChild.nodeValue="Убрать прайс-лист 'Металлопрокат'"; t.style.display="block"; } function tovarhid() { thr.href="javascript:tovarshow()"; thr.title="Показать"; thr.firstChild.nodeValue="Показать прайс-лист 'Металлопрокат'"; t.style.display="none"; } 

В коде всё вроде бы понятно: находим по идентификатору iframe и ссылку на него, затем изменяем какие-то свойства найденных объектов. Только не до конца понятно, почему мы не можем всё-таки просто написать «thr.nodeValue», а надо ещё добавлять какой-то firstChild . Мы уже несколько раз сталкивались с похожими ситуациями, разберёмся теперь в них до конца. Согласно спецификации W3C dom3-core (на www.w3.org): узел (node) не то же самое, что элемент . Узлы могут быть разного типа: и текстовые, и «документовые», и даже атрибут элемента является, по определению W3C, узлом. Вся информация, которую мы видим на странице, находится в узлах типа TEXT_NODE (составляет содержание , VALUE этих узлов), а эти узлы являются потомками каких-то элементов - p , a , div и др. Вот все возможные типы узлов с их кодами:

 ELEMENT_NODE = 1; ATTRIBUTE_NODE = 2; TEXT_NODE = 3; CDATA_SECTION_NODE = 4; ENTITY_REFERENCE_NODE = 5; ENTITY_NODE = 6; PROCESSING_INSTRUCTION_NODE = 7; COMMENT_NODE = 8; DOCUMENT_NODE = 9; DOCUMENT_TYPE_NODE = 10; DOCUMENT_FRAGMENT_NODE = 11; NOTATION_NODE = 12; 

Более подробную информацию можно найти, например, на стр. http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247. Есть это всё и на нашем CD, в папке omne\doc - файл DOM2-Core.zip или dom3core.zip (http://figur.ir2.ru/html/omne/doc/dom3core.zip).

У элемента a в нашем примере есть несколько потомков , и один из них (по счастью, самый первый, и он же - последний) - это просто текст; так вот, у этого узла (node) - «просто-текста» (имеющего тип TEXT_NODE) - мы и меняем содержимое (nodeValue). Всё это можно увидеть, используя Инспектор DOM браузеров Gecko:

dom01.gif

Там надо выбрать Javascript Object

dom02.gif

и найти в его «дереве» Links ).

dom03.gif

А вот с помощью скрипта в файле dom.htm (в папке CD omne\soft или на http://figur.ir2.ru/html/omne/soft), к сожалению, увидеть такие детали нельзя. Чего-то там недоработано. Ну, вы учитесь хорошо, и потом, когда доработаете сами, мне напишете (на omneinitium@narod.ru).

У нас возникла ещё одна маленькая неувязка: когда прайс-лист загружается в iframe, ему совершенно не нужна самая верхняя ссылка «На главную» (если щёлкунть по этой ссылке, в iframe загрузится именно главная страница - index.html, а нам это надо?). Если вы решите оставить вариант загрузки в iframe, эту ссылку надо будет убирать: либо каждый раз после генерации прайс-листа из MS Excel удалять из файла price1.html вручную код «<a href='index.html'>На главную</a>», либо залезть внутрь надстройки priceH.xla и внести изменения в её модуль , написанный на самом обыкновенном VBA (код надстройки доступен для изменения).

Гляжу я на наш урод... тьфу, то есть хотел сказать, учебный сайт, и думаю, что неплохо было бы закрепить приобретённые навыки работы с элементом iframe на страничке «Каталог товаров». Да, эта страничка даже прямо-таки нуждается в доработке. А раз возникло такое чувство, такой зуд перепрограммирования, его ни в коем случае нельзя сдерживать. Тем более, что начало работы с файлом katalog1.html будет совсем простое, проще, чем с прайс-листом:

<iframe id="katalog" frameborder="0" marginwidth="0" marginheight="0"></iframe>

- нам не надо даже записывать в iframe атрибут src , потому что мы заранее не знаем, портрет какого товара «в полный рост» захочет увидеть пользователь. Это будет определяться «на ходу», то есть динамически. Не забудем добавить правила CSS для селектора идентификатора (так это называется по-научному) - для нашего именованного iframe'а «katalog». Но сначала, чтобы не усложнять себе жизнь бесконечными повторениями CSS-правил для разных именованных iframe'ов, «вынесем за скобки» общую часть оформления для этих элементов:

iframe {display:none; border:0; padding:0;background:transparent;}

А вот теперь запишем правила для «katalog»:

#katalog {width:65em; height:70em; position:absolute; top:12em; left:11em}

Ширину и высоту без полос прокрутки подбираем опытным путём (и с небольшим запасом) для самого большого файла - a02.html. Следующий момент - position:absolute : мы хотим, чтобы наш элемент появлялся не просто где-то внизу страницы, а в совершенно конкретном, заданном нами месте. Это место тоже определяем опытным путём (можете попробовать изменить в файле dn.css правила «top:12em» и «left:11em» и посмотреть, что получится).

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

... <h1>Каталог продукции</h1> <p class="k1"> <a href="javascript:showkat('a01.html')" target="_blank"> <img src="../img/i01-1.gif" alt="Аппарат 1"> Аппарат 1</a></p> <p class="k1"> <a href="javascript:showkat('a02.html')" target="_blank"> <img src="../img/i02-1.gif" alt="Аппарат 2"> Аппарат 2</a></p> <p class="k1"> ... 

Обратите внимание на то, что мы убрали атрибуты target, чтобы браузер не пытался «открыть ссылку» в новом окне. Теперь создадим самоё функцию и запишем её в файл js.js (она оказывается на удивление простой):

function showkat(f) { k=document.getElementById("katalog"); k.style.display="block"; k.src=f; } 

Всё, каталог продукции в стиле «iframe» готов. Но никакой радости почему-то не появляется, если пытаешься представить, как пользователь будет с ним в Интернете бороться. Попробуйте сохранить нашу обновлённую страницу (через меню браузера «Файл» - «Сохранить как»), и узнаете, почему. Хоть iframe и не самый плохой из фреймов, но кое-какими недостатками «фреймовой идеологии» обладает вполне: теперь пользователь не может, например, сохранить информацию, щёлкнув по ссылке правой кнопкой мыши. И вообще не может быть уверен в том, что именно он сохраняет. Поэтому лучше нам не экономить место и работу, а удвоить количество ссылок, чтобы пользователь всё-таки мог выбирать: открывать страницу в новом окне или во фрейме.

Вернём старые ссылки в исходное состояние и создадим новый блок ссылок на файлы с товарами (без мелких картинок, конечно):

<div id="katref"> <a href="javascript:showkat('a01.html')">Аппарат 1</a> | <a href="javascript:showkat('a02.html')">Аппарат 2</a> | <a href="javascript:showkat('a03.html')">Аппарат 3</a> | <a href="javascript:showkat('a04.html')">Аппарат 4</a> | </div> 

ex28  Файлы примеров: dhtml3 (http://figur.ir2.ru/html/omne/dhtml3)

Всё равно, в какое место файла вы запишете код этого блока, потому что мы зададим ему абсолютную позицию (определённую приблизительно, путём подбора):

#katref {position:absolute; top:9em; left:17em}

Вот теперь наш файл katalog1.html начинает быть на что-то похож. Надо только добавить ещё к ссылкам атрибуты title с указанием, где будет появляться новая информация: в новом окне или в этом же:

<div id="katref"> <a href="javascript:showkat('a01.html')" title="На этой же странице"> Аппарат 1</a> | ... 
<p class="k1"> <a href="a01.html" target="_blank" title="В новом окне"> <img src="../img/i01-1.gif" alt="Аппарат 1"> Аппарат 1</a></p> ... 

Нужна ли схема проезда?

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

Начнём, разумеется, с того, что запишем в начало файла requisit.html инструкцию <script src='js.js'></script> , затем добавим к коду картинки фрагмент «id="karta"», затем в файл dn.css добавим правило #karta {display:none;} - наконец-то надоевшая картинка уберётся с экрана!

Вместо ссылки сделаем на этот раз кнопку (чтоб узнавать новое):

<button id="k1" onclick="kartashow()">Схема проезда к офису:</button>

В последний раз, если вы помните (в папке CD omne\js5), кнопка у нас была написана кодом <input type="button" ...>; можно писать и просто «<button...» - в нашем случае это роли не играет. Кнопку можно раскрасить (добавив правила в dn.css):

#k1 {width:14.5em; line-height:1em; height:2em; font-size:.9em; font-weight:600; color:#cccccc; background-color:#336666}

И запрограммировать (добавив функцию в файл js.js):

function kartashow(){ obj=document.getElementById("karta"); butt=document.getElementById("k1"); obj.style.display="block"; butt.firstChild.nodeValue="Скрыть схему проезда"; butt.onclick=kartahid; } 

Правой кнопкой мыши по кнопке на экране никто щёлкать не будет (это не ссылка), поэтому мы избавлены от необходимости программировать событие oncontextmenu . Но если вспомнить, зачем мы его программировали для ссылок... Пользователь ведь всё равно будет с подозрением относиться к элементам, обещающим ему вывести на экран что-то новое. Ссылку он попытается открыть в новом окне. А кнопку? Он, скорее всего, наведёт на неё мышь и задумается: давить или не давить? Можно, конечно, выдать в этот момент на экран пространное рассуждение о том, что картинка уже загружена на страницу, только невидима, а это значит, что дополнительное время на скачивание тратиться не будет, картинка появится мгновенно (даже в режиме оф-лайн). Но проще, наверное, показать это пользователю наглядно.

То есть можно запустить ту же функцию kartashow() по событию onmouseover . Пользователь только двинул мышкой над кнопкой, а картинка уже и выскочила. Но так будет не очень правильно: мы же не дали пользователю время подумать («Жать или не жать?»). Время, чтобы подумать можно задать с помощью специального метода JavaScript setTimeout . Мы зададим его прямо в HTML-коде страницы, там, где назначаем кнопке определённое действие:

<button id="k1" onmouseover="setTimeout(kartashow, 1000)">Схема проезда к офису:</button>

Число 1000 показывает количество миллисекунд, то есть одну секунду. Таким образом, пользователь проведёт кнопкой над мышкой, задумается о чём-то своём, вот тут-то карта и выскочит! Злые мы какие-то. Лучше, конечно пользователя так не пугать, а сделать так, чтобы карта появлялась не сразу, а как-нибудь постепенно... Сделаем такой «визуальный фильтр». Даже два. А вы выберете, какой больше понравится. Назначим для начала нашей кнопке для onmouseover другую функцию - kartafilter (kartashow нам ещё пригодится для щелчков): :

<button id="k1" onmouseover="setTimeout(kartafilter, 1000)">Схема проезда к офису:</button>

Запишем теперь эту функцию в файл js.js и вынесем из функций определение переменных obj, butt, i (мы планируем передавать их значение от одной функции к другой):

var obj, butt, i=0; function kartafilter(){ obj=document.getElementById("karta"); butt=document.getElementById("k1"); obj.parentNode.style.display="block"; obj.parentNode.style.overflow="hidden"; obj.style.display="block"; butt.onmouseover=""; filter1(); } 

Что мы пытаемся изобразить? Картинка вложена у нас в элемент p , его обычный способ вывода на экран - строчный (inline); мы задаём ему (называя его словом parentNode) способ вывода block , то есть прямоугольник. Пока у него нет никакой высоты (потому что у элемента inline высота определяется вложенными в него элементами, а вложенная в данном случае картинка имела высоту нулевую). overflow="hidden" - очень важный момент: по умолчанию overflow не hidden, а это значит, что картинка просто проигнорирует высоту родительского элемента, и вылезет из него сразу вся, целиком. Далее мы задаём картинке полную видимость, отменяем всякие действия по событию onmouseover кнопки (чтобы избежать логической путаницы) и запускаем функцию, которая будет постепенно увеличивать высоту родительского элемента картинки: картинка будет раскрываться постепенно, сверху вниз:

function filter1(){ i++; obj.parentNode.style.height=i*10+'px'; timerID=setTimeout("filter1()", 25); if (i>25) { clearInterval(timerID); timerID = null; butt.firstChild.nodeValue="Скрыть схему проезда"; setTimeout("butt.onclick=kartahid", 500); } } 

i++ - увеличивает значение i (которое при открытии документа, вне функций было задано нами равным нулю) на единицу.

Следующая строчка задаёт высоту родительского элемента картинки равной (i х 10) пикселов. Вот, собственно, и вся функция постепенного вывода картинки на экран. Просто её надо теперь повторить несколько раз (i при этом будет постоянно расти, и высота элемента тоже) с определённым интервалом. Это мы и делаем в следующей строке:

timerID=setTimeout("filter1()", 25);

- задаём с помощью метода setTimeout интервал в 25 миллисекунд, с которым будет запускаться наша функция filter1(). Функция будет запускаться бесконечно, и высота элемента расти - тоже. Чтобы остановить этот неконтролируемый рост, надо записать в функцию условие, при котором она должна прекратить это безобразие методом clearInterval . Условие мы задаём в следующей строчке: «при i > 25» (то есть при высоте открытия картинки больше 250 пикселов).

Дальше мы меняем надпись на кнопке, и ещё через полсекунды задаём действие по щелчку мыши - «Скрыть схему». Если не поставить эти полсекунды, пользователь может успеть щёлкнуть по кнопке с новым свойством «раньше времени», и картинка закроется, не успев толком открыться.

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

Такие фокусы иногда помогают развлечь пользователя в случае серенького, как наша схема, содержимого. Неважно, что плохо нарисовано, - главное, правильно подать на экран! Обычные приёмчики циркового конферанса и шоу-бизнеса. В таком поведении (делать из дерьма конфетку) самом по себе ничего плохого нет, в конце концов, мир всё равно от этого становится немного лучше. Но обилие «красот» на сайте будет всегда настораживать пользователя: а вдруг это обилие внешних красивостей - компенсация низкого качества содержания?

Комментарии