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

Глава 9. JavaScript: баловство или необходимость?

Глава 9. JavaScript: баловство или необходимость?

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

Самый главный скрипт

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

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

Вот как можно получить информацию о браузере:

<script type="text/javascript">
  var agent = navigator.userAgent.toLowerCase();
  document.write(agent);
</script>

Запишите этот код в файл browsr01.html (файл есть на CD в папке omne\js1, а также по адресу: omne/js1) и откройте файл в браузере. Интернет Эксплорер версии 5 напечатает на вашей странице следующую строчку:

mozilla/4.0 (compatible; msie 5.01; windows nt 5.0)

Браузер Mozilla Firefox - другую:

mozilla/5.0 (windows; u; windows nt 5.0; ru; rv:1.8) gecko/20051111 firefox/1.5

Мы смотрим на две разные строки и, анализируя их содержание, находим разницу. Примерно то же самое можно делать с помощью программы, написанной на JavaScript: надо специальной функцией проанализировать, распознать полученную строку, которую мы занесли в переменную agent. Эта функция (на самом деле не функция, а метод объекта String, но пока это неважно) называется indexOf() и используется так: Строка1.indexOf(Строка2). «Строка1» - строка, в которой надо найти последовательность символов; «Строка2» - последовательность символов, которую мы хотим найти в «Строке1». Важно подобрать правильное слово для поиска. Если мы для идентификации браузера будем искать, например, слово mozilla, то не сможем отличить IE от Firefox, потому что в обеих строчках искомое слово есть. Поэтому для опознания IE будем искать в названии браузера слово msie, а для опознания Firefox или SeaMonkey - слово gecko. Добавьте к первоначальному коду скрипта следующие строчки и сохраните новый текст в файле browsr02.html:

if (agent.indexOf("msie") > -1) document.write("<br>Internet Explorer");
if (agent.indexOf("gecko") > -1) document.write("<br>Gecko");

Попробуйте открыть файл browsr02.html в разных браузерах и посмотрите, что будет написано на странице.

Распознавание браузера необходимо не только для коррекции очень сложного веб-дизайна, но и для обычной, отнюдь не затейливой html-вёрстки (размещения текстов и картинок на странице). Разные браузеры могут отображать по-разному даже очень простой фрагмент. Допустим, мы решили заключить в рамку определённого размера один заголовок и один абзац из этой книги, и в предварительной отладке использовали Firefox. После несложной работы (результат - файлы browsr03.html и js1.css) мы в этом самом Firefox'е видим то, что хотели получить и успокаиваемся:

Глава 1. Создание вашей первой HTML-страницы

Что мешает вам создать веб-сайт и поместить его в Интернет? На этот вопрос можно найти довольно много ответов. Точнее, можно найти много причин, по которым создание интернет-страницы кажется вам невероятно сложной задачей.

Потом мы (так, на всякий случай - ведь на странице не было ничего такого!) открываем тот же файл в Интернет Эксплорере, и вдруг видим, что наша простая вёрстка из двух абзацев куда-то «поехала»:

Глава 1. Создание вашей первой HTML- страницы

Что мешает вам создать веб-сайт и поместить его в Интернет? На этот вопрос можно найти довольно много ответов. Точнее, можно найти много причин, по которым создание интернет-страницы кажется вам невероятно сложной задачей.

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

Наш фрагмент отображается в обозревателях по-разному, потому что Интернет Эксплорер считает ширину блочных элементов вместе с величиной padding и толщиной border, а Gecko (и Opera) исключают из размера блока padding и border. В стилевом файле js1.css мы задали ширину раздела div.b1 в 29em, а когда добавили к этому разделу рамку и отступы содержимого от краёв (padding), обозреватели Gecko оставили ширину полезной площади прежней - 29em и дорисовали отступы в 1em и рамку толщиной 0.4em вокруг раздела, увеличив тем самым его общую ширину на 2.8em. Интернет Эксплорер же втиснул рамку в заданную ширину 29em, чем фактически уменьшил ширину полезной площади на 2.8em. Вот строчки и «поехали»...

Дело, очевидно, можно исправить, увеличив ширину раздела div.b1 на ушедшие на рамку и padding 2.8em, то есть задав её в 31.8em. Тогда в IE заголовок уместится в одну строку, и остальной текст будет выглядеть, как задумано. Но в Gecko ширина раздела тоже увеличится на 2.8em (а она уже была нормальной) - и строчки опять «поползут». Вот примерно для таких случаев и существует решение - с помощью скрипта привязывать к странице разные CSS-файлы, в зависимости от типа браузера. Создайте новый стилевой файл js2.css с точно таким же содержимым, что и в js1.css, затем измените в нём ширину раздела div.b1 с 29em на 31.8em. Добавьте также в файле js1.css (для обозревателей Gecko) к правилам для body font-size:16px - таково в IE значение размера шрифта по умолчанию, такой же размер должен быть и в других браузерах (значение по умолчанию в них не совпадает со значением в IE). Затем создайте новый файл browsr04.html с точно таким же содержимым, как в browsr03.html, и переделайте скрипт в файле browsr04.html на следующий:

<script type="text/javascript">
 var agent = navigator.userAgent.toLowerCase();
 if (agent.indexOf("gecko") > -1) 
   document.write("<link rel='stylesheet' href='js1.css' type='text/css'>");
 if (agent.indexOf("msie") > -1) 
   document.write("<link rel='stylesheet' href='js2.css' type='text/css'>");
 else 
  document.write("<link rel='stylesheet' href='js1.css' type='text/css'>");
</script>

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

ex19 Файлы примеров: js1 (js1)

Как это работает

Скрипт - это, скорее, жаргонное название, по-научному его обычно называют «сценарием» (от англ. script - «сценарий»). Так вот, скрипт начинает работать по той же причине, по которой расставленные вами html-тэги и записанные правила CSS превращаются в видимые глазом изменения шрифта и цвета страницы. Эта причина - браузер: когда программа просмотра интернет-страниц находит в html-тексте метку <p>, она (программа) делает на экране абзацный отступ по правилам, найденным ею в соответствующем файле CSS. Когда браузер находит в тексте html-страницы метки <script type="text/javascript">...</script>, всё, что находится между этими метками, он рассматривает как команды, написанные на языке JavaScript, и пытается эти команды выполнить.

Например, если мы запишем в html-текст (между соответствующими обозначениями начала и конца скрипта) команду скрипта document.write(«Привет!»), браузер при открытии документа выведет на экран слово «Привет!».

Так же можно заставить браузер вставить в текст страницы невидимую глазом запись <link rel="stylesheet"...>, привязывающую к странице файл CSS. Эта запись получается невидимой по самой своей природе: браузеры обучены рассматривать текст, заключённый в тэге <link rel=...>, как определённую инструкцию, не предназначенную для вывода на экран. Невидимая на экране команда JavaScript вставляет в текст страницы невидимую глазом инструкцию - и вид страницы изменяется. Всё просто.

Красота решения

На языках HMTL, JavaScript, так же, как и на русском языке, можно описать одни и те же предметы по-разному. Мы не планируем в этой книге вдаваться в стилистические тонкости, поэтому понятие «по-разному» попытаемся конкретизировать, сузить. Мы будем считать, что разные решения возникают, в основном, от борьбы двух тенденций, двух не всегда осознаваемых устремлений человека: 1) экономия мыслительных усилий; 2) экономия материала. Например, у меня в голове (или в душе?) такая борьба началась в тот самый момент, как я написал для этой книги скрипт, позволяющий менять оформление страницы в зависимости от браузера.

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

Если посмотреть на текст скрипта из файла browsr04.html повнимательней, можно заметить, что строчка «document.write("<link rel='stylesheet' href='js2.css' type='text/css'>");» повторяется в нём три раза почти без изменений. Отличаются эти три строки только одной цифрой в имени файла: js1.css - js2.css. Возникает вопрос: нельзя ли эту строчку записать только один раз, в конце скрипта, а цифру представить каким-нибудь хитрым образом, чтобы она прямо при записи менялась в зависимости от обстоятельств. Ответ: на то и существуют переменные.

Объявим в начале нашего скрипта с помощью волшебного слова var переменную i:

var i; (каждое «действие» в сценарии JavaScript должно заканчиваться знаком ; - точка с запятой).

Самую последнюю строчку, печатающую результат в файл, сделаем такой:

document.write("<link rel='stylesheet' href='js" + i + ".css' type='text/css'>");

Знак + в данном случае соединяет в единую строку несколько разных фрагментов текста. Если бы мы, например, написали строку «2 + 2», JavaScript заставил бы браузер вывести на экран «4» (а не «22»). Но мы записываем "js" + 2 + ".css", и получим в итоге "js2.css". Обратите внимание на то, как расставлены кавычки. Все текстовые строки в JavaScript должны быть заключены в кавычки - всё равно, в какие: двойные или одинарные. Мы выбрали двойные. Но в результирующей строке языка HTML тоже нужны кавычки, и если мы попытаемся написать "document.write("<link rel="stylesheet"" (заключив слово stylesheet в двойные кавычки), то JavaScript запутается: будет считать, что на «rel="» строка закончилась, а голое, без начальной кавычки слово stylesheet - вообще непонятно, к чему относится. Короче, выскочит, скорее всего, какое-нибудь «предполагается наличие )» (или ещё какая-нибудь «незавершённая строковая константа»), и скрипт свою работу не выполнит, загнётся на полпути.

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

Теперь надо поточнее определить, какое же значение получит новая переменная i: 1 или 2? Вы, наверное, обратили внимание на немного странную логику в условных конструкциях скрипта (начинающихся со слова if - английского «если»): сначала мы пишем «Если в строке есть слово gecko, то файл будет js1.css»; затем пишем «Если в строке есть слово msie, то файл будет js2.css»; потом зачем-то ещё добавляем «Иначе (если таки слова msie в строке нету) файл будет js1.css». Последнюю строчку мы добавили на случай, если в строке нет ни слова gecko, ни слова msie. Понятно, что такое возможно; но файл-то мы записываем тот же, что и для gecko! Поэтому условную конструкцию вполне можно упростить: «Если в строке есть слово msie, i будет равно 2; Иначе i = 1».

Потребуется ещё небольшое уточнение. Проблема в том, что браузер Opera иногда любит представляться, как Интернет Эксплорер, и тогда браузер в ответ на наш запрос о userAgent'е может написать:

mozilla/4.0 (compatible; msie 6.0; windows nt 5.0; en) opera 9.00

В этой строке, как видите, тоже есть слово msie. Но зато там есть и слово opera. Что позволяет нам отделить мух от супа: «Если в строке есть слово msie и если в строке нет слова opera, i будет равно 2». После внесённых изменений скрипт будет выглядеть так:

<script type="text/javascript">
 var agent = navigator.userAgent.toLowerCase();
 var i;
 if (agent.indexOf("msie") > -1 && agent.indexOf("opera") == -1) i=2;
 else i=1;
  document.write("<link rel='stylesheet' href='js" + i + ".css' type='text/css'>");
</script>

В таком виде скрипт смотрится уже более прилично. Его вполне можно использовать и для «борьбы» с таблицами (вписать в файлы js1.css и js2.css разные правила оформления таблиц). Осталось пояснить кое-какие детали. По порядку.

navigator.userAgent - волшебные слова, писать их нужно очень точно, в JavaScript не всё равно, маленькая буква или большая: userAgent и Useragent - разные слова! Волшебные они в том плане, что непонятные. Непонятно, что это такое, почему именно так пишется, но если написать правильно - это работает. Если же попытаться понять, точно определить место этих слов под солнцем, на это понадобится отдельная книга.

Строка1.toLowerCase() - функция (или метод), которая преобразует все буквы строки «Строка1» в строчные. После этого мы будем уверены, что слово msie в строке «Строка1» (если оно там встретится) будет целиком написано маленькими буквами (а не какими-нибудь MSIE или Msie), и мы будем искать его методом Строка1.indexOf("msie") именно в таком виде - все буквы строчные.

&& - логическая связка И («Если в строке есть слово msie И в строке нет слова opera...»).

Откуда в выражениях поиска строки взялась минус единица? Дело в том, что метод Строка1.indexOf(Строка2) не просто обнаруживает, является ли Строка2 частью Строки1, но и указывает позицию первого символа Строки2 в Строке1. Начинается отсчёт с цифры ноль. То есть, если Строка1 прямо начинается со Строки2, то метод indexOf вернёт (говорят, что метод или функция возвращают значения) число ноль. Если же Строка2 никаким боком не входит в Строку1, тогда метод indexOf вернёт значение -1 (минус единица). Вот мы и проверяем, не появится ли у нас после работы метода indexOf минус единица - в этом случае Строка2 не входит в Строку1.

== - таким знаком проверяется в условных операторах, равно ли выражение какому-либо значению. Если написать одинарный знак равенства = (if (a=3)), то JavaScript просто присвоит переменной a значение «3», то есть условие автоматически станет истинным, и мы не узнаем, была ли a на самом деле равна трём, или нет.

Общий синтаксис условного оператора: «Если (условие в скобках) {команды в фигурных скобках;} Иначе {команды в фигурных скобках;}» - «if (a == 3) {b = 2;} else {b = 1;}». Но фигурные скобки иногда можно и опускать. Когда именно, вы узнаете сами (забудете их поставить, и скрипт выдаст ошибку). В нашем случае - можно было. А вот условие всегда надо писать в простых скобках.

document.write("Привет!") - этот метод создаёт во время загрузки, «на лету», html-код документа. Но после того как документ полностью загружен, открыт в окне, этот метод больше использовать нельзя. Менять содержимое уже открытой страницы можно другими методами.

Полезные скрипты

Часто бывает необходимо поместить на страницу какой-нибудь символ - не русскую или английскую букву, а, например, знак копирайта © или полиграфические кавычки «». В Интернете можно найти много страниц с описанием этих так называемых спецсимволов. Вы можете какую-то из этих страниц сохранить себе на компьютер и обращаться к ней по мере необходимости. Но эти сохранённые страницы постоянно куда-то теряются. Лучше (как-то надёжнее) иметь у себя алгоритм построения такой страницы, воплощённый в небольшом скрипте (файл charcod0.htm в папке omne\char на CD; omne/char/charcod0.htm):

<link rel="stylesheet" href="char01.css" type="text/css">
<script type="text/javascript">
document.write("<table><tr style='vertical-align:top'>");
document.write("<td><b> Basic Latin</b><br>"); 
for (i=1; i<=127 ; i++) 
 {document.write("<u>"+i+"</u>&nbsp;&#"+i+";<br>");}
document.write("<td><b> Latin-1 Supplement</b><br>"); 
for(i=128; i<=255 ; i++) 
 {document.write("<u>"+i+"</u>&nbsp;&#"+i+";<br>");}
document.write("<td><b> Arrows</b><br>"); 
for(i=8592; i<=8703 ; i++) 
 {document.write("<u>"+i+"</u>&nbsp;&#"+i+";<br>");}
document.write("<td><b> Geometric Shapes</b><br>"); 
for(i=9632; i<=9727 ; i++) 
 {document.write("<u>"+i+"</u>&nbsp;&#"+i+";<br>");}
document.write("<td><b> Cyrillic</b><br>"); 
for(i=1024; i<=1279 ; i++) 
 {document.write("<u>"+i+"</u>&nbsp;&#"+i+";<br>");}
document.write("</table>");
</script>

и в небольшом файле стилей char00.css:

body {font-family: Arial}

table { border-collapse:collapse}

td {border:1px solid #006600; background:#ffffcc; line-height:1.2em }

b, u {font-size:.7em; font-family:Arial; text-decoration:none;}

ex20 Файлы примеров: char (char)

Скрипт генерирует таблицу, в которой пытается вывести на экран более 720 символов (включая обычные русские и латинские буквы) с их кодами. Например, кружок &bull; имеет код 149; это значит, что мы можем написать в html-коде не &bull;, а &#149; (перед кодом надо ставить знак решётки - #), и получим примерно тот же результат: • (•).

В скрипте применяется конструкция for (i=; i<; i++) {действия}, которая расшифровывается как «Для (i начиная с 1; до 127; с каждым шагом цикла увеличивать i на единицу) {печатать в документ код символа i}».

Не всегда попытки программы оказываются успешными: например, начальные символы из набора «Basic Latin» (а также и многие другие диапазоны символов) не имеют представления в наборе символов текущего шрифта (Times new roman) и поэтому отображаются Интернет Эксплорером на экране пустыми квадратиками. Скрипт печатает только 5 наборов символов - диапазонов Unicode, их в природе существует, конечно, гораздо больше. Вы можете посмотреть, какие бывают диапазоны Юникод, на стр. http://www.unicode.org/charts/charindex.html.

Не всегда пустой квадратик в таблице означает, что символ вообще не может иметь графического представления: некоторые символы для своего отображения требуют применения специальных шрифтов. Попробуйте сделать копию файла charcod0.htm под именем charcod1.htm, исправьте в новом файле инструкцию «link rel=» - впишите в неё имя файла char01.css; создайте этот файл копированием из char00.css и исправьте шрифт в body на Palatino Linotype:

body {font-family: "Palatino Linotype"}

Сохраните новые файлы и откройте charcod1.htm в Интернет Эксплорере. На первый взгляд (судя по диапазону «Basic Latin»), ничего особо не изменилось: только в шрифте Palatino Linotype отсутствующие символы отображаются не квадратиками, а какими-то такими говёшками (только в IE!): . Но, приглядевшись внимательней, вы можете заметить, что в диапазоне Кириллицы первый символ (код 1024), представленный шрифтом Palatino Linotype, выглядит уже как буква Е с надстрочным знаком (а не как пустой квадратик). А главное, этим шрифтом можно печатать так любимую новыми (и старыми тоже) русскими букву ять: Извѣстия.

Любопытно, что наши заботливые построения с подбором шрифтов совершенно бесполезны в браузерах Gecko и Opera: каким-то образом эти обозреватели ухитряются печатать все возможные символы, даже если в выбранном нами шрифте такого символа вовсе нет. Посмотреть, какие символы существуют в данном шрифте можно в стандартной программе Windows Charmap.exe (Пуск - Программы - Стандартные - Служебные - Таблица символов). Так вот, Charmap показывает, что у шрифта Arial в диапазоне Юникода «Стрелки» (Arrows) есть только семь символов. Ну, иногда может показывать побольше (вероятно, это зависит от версии шрифта). Но Mozilla Firefox показывает на экране в шрифте (якобы) Arial до 90 стрелок, а если на компьютере были установлены дополнительные «экзотические» шрифты, то может показывать и больше 100! Это чудо, и с ним приходится мириться. Видимо, «продвинутые» браузеры анализируют коды символов, которые им требуется вывести на экран, и ищут соответствующие кодам начертания во всех шрифтах, установленных на системе Windows. Но шрифт для редких символов всё равно надо назначать Palatino Linotype (или «Code2000»), потому что очень много людей смотрят в Интернет через окно Интернет Эксплорера (а не Firefox или Opera).

Забегая вперёд

Свободно распространяется в Сети скрипт автора Cyanide_7 «Dom Browser». Этот скрипт показывает, как именно обозреватели представляют себе структуру html-документа. Скрипт есть на CD в папке soft - файл dom.htm (а также - omne/soft/dom.htm). Внутри файла записана инструкция: его код почти весь нужно скопировать в раздел head любого html-документа, а маленький кусочек - код формы с кнопкой - вставить в раздел body. В документе появится кнопка с надписью «Open DOM Browser».

В браузерах Gecko есть более мощное встроенное средство для анализа структуры документа - «Инспектор DOM» (DOM - Document object model = «Объектная модель документа»), так что в них использовать этот скрипт большого смысла нет.

Часть III. Изменение html-документа после открытия. Dynamic HTML

По-научному изменение содержания (и оформления) уже открытого документа называется DHTML - Dynamic HTML. Предполагается, что документ может изменяться в ответ на какие-то действия пользователя. Или в ответ на какие-то события, происходящие во время просмотра документа. Например, пользователь смотрит на текст заголовка, а рядом кнопка; если пользователь на эту кнопку нажмёт, заголовок станет фиолетовым. Или наоборот, зелёным. Зачем это надо?.. Для того чтобы красить заголовки зелёным цветом, наверное, не надо. Но DHTML может помочь удобно выдавать информацию.

В виде HTML-страницы информация должна выдаваться пользователю а) быстро; б) понятно - так, чтобы пользователь мог быстро сориентироваться в ней. «а)» требует сравнительно небольших объёмов в килобайтах. Допустим, 5-10 Кб текста для одной страницы - это небольшой объём, она загрузится довольно быстро. Но что такое 5-10 Кб для чтения? Это 1/3 - 2/3 газетной полосы. Или 2-4 страницы формата дипломной работы. То есть для первоначального восприятия и анализа 5-10 Кб - это много!

«б)» требует сжатия всех 5-10 Кб одной страницы (включая намёки на содержание других страниц сайта) до размера 800х600 (т. е. 1-1.5 Кб), чтобы можно было охватить одним взглядом. То есть на первой странице пользователь должен видеть Аннотацию, очень конкретную и очень простую (без всяких словесных «украшений» и отвлекающих весёлых картинок).

Иногда а) и б) вступают в противоречие:

1) маленькая аннотация содержит много небольших разделов, которые по желанию пользователя должны выдаваться в развёрнутой форме;

2) если развёрнутая форма хранится в отдельных файлах, то может получиться так, что на её загрузку будет тратиться больше времени, чем если бы полная форма хранилась на одной странице с краткой.

Можно сравнить отображение такой сложной страницы со структурой электронного документа в MS Word: такой документ можно «свернуть» до заголовков самого верхнего уровня или раскрывать последовательно любые заголовки до самого нижнего уровня - текста.

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

Комментарии