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

Глава 10. Примечания (сноски) в книге

Глава 10. Примечания (сноски) в книге

В книгах (не слишком научных) обычно текст сноски (примечания) помещают внизу, на той странице, на которой находится ссылка на сноску. В Интернет книги обычно помещают по главам (или другим разделам приемлемой величины), а в одной главе может быть, например 30 экранных страниц текста. Где тогда будет это книжное «внизу»? Очевидно, в конце этих самых 30 экранных страниц. И чтобы просмотреть текст сноски надо будет сильно сместить просматриваемый текст.

Наш пример слишком велик по объёму (как раз около 5 килобайт текста - 1.5-2 страницы этой книги), поэтому он так и останется ненапечатанным - будет представлен только на сайте.

ex21 Файлы примеров: htm6 (htm6)

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

<a href="#a02t">**&#62;</a>

вы переместитесь на другую ссылку с именем «a02t». Для этого, разумееется, она должна быть в документе:

<a name="a02t">&#60;**</a>

Более полный и правильный код ссылок находится в файле wattsa.htm. В этом коде ссылки сделаны перекрёстными: сноске задаётся имя «a02», а тексту сноски - атрибут href, равный «#a02». Благодаря этому легко можно переходить со сноски на текст сноски и обратно. Код «&#62;» отображает на экране символ с кодом 62, то есть закрывающую угловую скобку. Её нельзя вписывать в html-код прямо знаком «>», потому что обозреватели должны рассматривать угловые скобки как специальные символы, означающие начало или конец html-тэга. Но вы можете попробовать нарушить это условие (напечатать вместо кода сам символ «>»). Скорее всего, ничего страшного не произойдёт. Код &#60;, как вы, несомненно, уже догадались, обозначает открывающую угловую скобку.

Однако такая традиционная схема обработки сносок не идеально удобна: при перемещениях между сноской и текстом сноски и обратно «позиция чтения» всё равно будет смещаться и требовать дополнительного времени на восстановление. Гипертекст, точнее, DHTML позволяет больше приблизиться к идеалу: тексты ссылок могут появляться прямо на том месте экрана, на котором стоят сами ссылки. То есть при щелчке ссылка на сноску просто превращается в сам текст сноски, выделенный каким-либо образом, чтобы отличаться от окружающего «текущего» текста. Ещё один щелчок удаляет текст, превращает его обратно в ссылку - и граждане довольные продолжают чтение книги ровно с того места, на котором остановились.

Показать все примечания

ex22 Файлы примеров: js2 (js2)

Посмотрите на файл wattsa.htm, в котором хранится фрагмент книги: этот фрагмент именно так и устроен, как описано выше. Как ни странно, показать одновременно тексты всех сносок оказывается проще, чем показать текст одной-единственной сноски. Поэтому начнём пояснения именно с этого - с механизма действия кнопки «Показать все примечания». Предварительно нужно отметить, что текст скрипта мы на этот раз не поместили внутрь html-страницы, а поступили с ним так же, как и с текстом CSS - сохранили в отдельном файле wattsa.js и привязали этот файл к нашей странице с помощью кода:

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

Идея отображения или сокрытия заданных элементов уже вам знакома (по примерам в папке omne\js1 (js1)): с помощью JavaScript при нажатии кнопки с данной html-страницей связывается тот или иной CSS-файл - в зависимости от этого конкретного CSS-файла меняется оформление (и содержание) страницы. В случае файла wattsa.htm конкретное решение таково: все тексты примечаний (ссылок) заключаются в тэги <span class="reftxt"> и </span> и помещаются непосредственно рядом со своими ссылками:

<span class="reftxt" id='ref4'> T`ung-shan Liang-chieh. [30, с. 74].</span>

К файлу wattsa.htm изначально привязаны два разных стилевых файла (их может быть хоть двадцать два):

<link rel="stylesheet" href="wattsa.css" type="text/css">

<link id="st1" rel="stylesheet" href="watts1.css" type="text/css">

В первом файле - wattsa.css - записаны почти все правила оформления страницы, кроме одного; это отдельное правило записано в файле watts1.css, и оно очень простое:

.reftxt { display:none; }

- что означает: «все элементы с идентификатором класса reftxt не показывать на странице ни в каком виде (не показывать даже пустые места)».

Когда мы нажимаем на кнопку «Показать все примечания», начинает работать функция JavaScript shownotes, записанная в файле wattsa.js (в самом начале). Эта функция, в частности, вместо файла watts1.css привязывает к нашей открытой html-странице стилевой файл watts2.css, в котором так же кратко и категорично записано:

.reftxt { display:block; }

- то есть показать все элементы класса reftxt в виде прямоугольников

Как такое становится возможным технически - вместо одного стилевого файла назначить уже открытой странице другой? Чтобы понять это, надо вникнуть в механизм работы элемента <link rel=...>. У этого элемента есть такой же атрибут, как и у ссылки a: href. Так вот, значение этого атрибута с помощью JavaScript можно изменять динамически, то есть назначать произвольно как в процессе открытия страницы, так и на уже открытой странице.

У нас на странице несколько элементов link rel=... Как JavaScript узнает, какому из них менять атрибут? Мы позаботились об этом заблаговременно: назначили интересующему нас элементу идентификатор: link id="st1". Теперь для поиска нужного элемента в html-коде мы можем воспользоваться методом getElementById:

var st11 = document.getElementById("st1");

После того как мы нашли методом getElementById нужный элемент и присвоили эту «находку» переменной st11, переменная st11 становится программным объектом, обладающим всеми свойствами элемента link. Теперь при помощи очень простого синтаксиса мы можем менять эти свойста (хм... если они, конечно изменяемые): пишем название переменной, через точку - нужное свойство элемента link, и через знак равенства - значение этого свойства (не забывая в конце каждого действия ставить точку с запятой):

st11.href="watts2.css";

Всё, как только мы присвоили атрибуту href элемента link (id="st1") значение watts2.css, на всей странице сработает новое правило для всех элементов класса reftxt, и все примечания станут видимы.

Ещё функция shownotes пытается предоставить какие-то минимальные удобства для пользователя, предвидеть некоторые его желания: вдруг, например, пользователь захочет скрыть все примечания обратно? Как, например, тогда он узнает, в каком состоянии сейчас находятся примечания - в скрытом или видимом? Получается, что сама надпись на кнопке, запускающей функцию, должна меняться динамически: когда примечания скрыты - «Показать примечания», когда видимы - «Скрыть примечания». Для изменения надписей кнопки JavaScript опять-таки должен знать, о какой именно кнопке идёт речь, поэтому мы заблаговременно присвоили нужной кнопке идентификатор allnotes. А для обработки разных ситуаций мы применили условный оператор if, который уже знаком вам по «самому главному скрипту» (определяющему тип браузера).

Все примечания сразу показать было сравнительно легко, потому что конкретная (известная по идентификатору) кнопка должна в этом случае менять один конкретный, заранее известный элемент link. Чтобы изменить (например, щелчком мышью по любой ссылке на сноску) видимость любого, заранее не известного примечания, JavaScript должен как-то узнать, о какой ссылке и каком тексте примечания идёт речь. Ну, положим, ссылку, по которой мы щёлкаем, можно указать JavaScript'у при помощи специального слова this. Но нам ведь надо изменять совсем другой объект - текст примечания! А о том, что придётся изменять ещё и ссылку на него, мы пока даже и не знаем (это, как всегда, обнаружится позже, в процессе работы).

Указать программе на интересующие нас объекты (ссылку и текст ссылки) можно: для этого в функцию, которую мы вызываем, надо передать параметры. Есть несколько способов решения этой задачи. Мы начнём с самого очевидного, «тупого» и надёжного:

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

В той же самой папке js2 вариант файла wattsa.htm как раз и представлен с нумерацией ссылок: каждой ссылке (и каждому тексту ссылки) присвоен (в коде HTML) атрибут id с уникальным значением: abc2, ref2, abc3, ref3 и т. д., до 88, если бы мы захотели обработать весь текст книги.

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

<a onmouseover="title1('ref2','abc2')" ...

onmouseover - то событие, которое происходит в окне браузера, когда пользователь наводит на ссылку мышь. Вот мы и заставляем по этому событию работать некую функцию title1(), а значения идентификаторов ссылки и её текста передаём в функцию в явном виде: title1('ref2','abc2'). Сама функция (записанная второй по счёту в файле wattsa.js) при таком надёжном обеспечении информацией получается очень простой:

function title1(rr,aa) {
var ref=document.getElementById(rr);
var abc=document.getElementById(aa);
abc.title=ref.firstChild.nodeValue+"...";
}

rr внутри скобок в выражении title1(rr,aa) получит то значение, которое в html-коде «"><a onmouseover="title1('ref2','abc2')" ...» при вызове функции было в скобках записано первым, то есть ref2 - идентификатор текста ссылки. Ну, а aa получит значение abc2, то есть идентификатора самой ссылки. После этого запросто можно применить метод getElementById() и записать в переменные ref и abc значения самих объектов, представляющих в функции JavaScript элементы HTML: ссылку a и текст ссылки (в нашем примере - элемент span). Ясно, что выражение abc.title указывает на атрибут title нашей текущей ссылки, а вот что собой представляет выражение ref.firstChild.nodeValue, вам сейчас лучше даже у меня не спрашивать... Во втором варианте работы со ссылками и так будет предостаточно проблем с node. А пока нам достаточно того, что это работает.

Итак, подсказка всплывает. Теперь внимание - щелчок! А значение атрибута ссылки href у нас javascript:showref('ref2','abc2'). Именно так: впереди слово javascript:. Если его не написать, браузер будет пытаться открыть несуществующий файл «showref('ref2','abc2')» (можете проверить - попробуйте удалить из html-кода слово javascript:). Ладно, будем считать, что нам удалось обмануть браузер и подсунуть ему вместо файла функцию:

function showref(rr,aa){
var ref=document.getElementById(rr);
var abc=document.getElementById(aa);
ref.style.display="block";
abc.href="javascript:hideref('"+ref.id+"','"+abc.id+"')";
abc.title="Скрыть примечание";
abc.onmouseover="";
abc.innerHTML="<<<";
ref.title="Щелчок скроет примечание";
}

Таким же образом, как в предыдущей функции title1(), мы получаем объектное представление элемента span, в котором заключён текст текущей ссылки, и задаём его свойству CSS display значение block - то есть видимый на экране прямоугольник. Вообще-то span - не прямоугольный (не блочный) элемент, а строчный; но нам-то что: особенности css-оформления позволяют задавать элементам разные способы вывода на экран. В некоторых пределах, конечно. В нашем случае - точно позволяют.

Что теперь будет, если пользователь ещё раз щёлкнет по той же самой ссылке? Правильно: примечание (текст ссылки) с экрана исчезнет. Потому что после предыдущего щелчка функция showref() задала атрибуту текущей ссылки href значение «javascript:hideref('ref2','abc2')», и по щелчку теперь запустится функция hideref(), которая спрячет примечание. Почему в функции так сложно написано (такая чехарда с кавычками): "javascript:hideref('"+ref.id+"','"+abc.id+"')"? Не спрашивайте... Лучше, если непонятно, посидите, помедитируйте, глядя на эту строчку, - и дело постепенно прояснится само собой.

Функция showref() задаёт текущей ссылке значение title в явном виде - «Скрыть примечание», при наведении мыши эту подсказку совершенно незачем менять обратно на «Показать примечание», поэтому свойству ссылки onmouseover мы задаём пустое значение, чтобы никакая функция не запускалась.

innerHTML (очень важно писать названия элементов, свойств, атрибутов и др. точно - все заглавные и строчные буквы должны быть на своих местах) - любопытное свойство, оно есть практически у всех html-элементов. Оно фактически представляет html-код элемента и всех элементов, в него вложенных. С его помощью можно менять содержимое документа самым радикальным образом. Например, можно написать «body.innerHTML="";» - и весь текст документа исчезнет. А можно наоборот написать «body.innerHTML=;» - и длинный, длинный html-код, совершенно другой страницы...

Из последней строчки функции showref() мы можем узнать о том, что свойство title бывает не только у элементов a, но и у разных других элементов. Например, у span, в который мы поместили текст ссылки. Теперь, когда примечание стало видимым, если пользователь задержит над ним мышку, всплывёт надпись: «Щелчок скроет примечание». Чтобы это было правдой, мы должны были предварительно записать в html-код элемента span «onclick="javascript:hideref('ref2','abc2')"». Что мы и сделали. Можно было так же записать в каждый элемент span значение для title (о сокрытии примечания щелчком), но в данном случае с помощью JavaScript мы самым банальным образом экономим место, написав текст свойства title только один раз вместо трёх (или 88-ми для всей книги).

Функцию hideref(rr,aa) вряд ли надо комментировать: она делает всё то же самое, что и функция showref(rr,aa), только наоборот. Возвращает статус кво, положение, когда примечание скрыто.

Текст примечания - «следующий объект»

Рассмотрим вариант обработки примечаний без уникальных идентификаторов. Такое в принципе возможно, если текст примечания поместить непосредственно за ссылкой, без всяких пропусков и даже знаков препинания в тексте страницы. Такое размещение, как в нашем последнем примере не подойдёт, потому что между закрывающем тэгом <a> и открывающим <span находится точка и пробел:

<a onmouseover="title1()" href="javascript:showref()" id='abc2'>**></a>. <span...

Даже эту несчастную точку с пробелом надо поместить после закрывающего тега текста примечания </span>. Мы сделали это для всех примечаний в следующем примере:

ex23 Файлы примеров: js3 (js3)

Теперь можно использовать метод nextSibling интерфейса Node. Как звучит! После изучения материала этой главы вы всем сможете говорить, что «использовали метод нэкстС`иблин интерфейса ноуд». Это не так страшно, как может показаться на первый взгляд. Node - англ. «узел». Интерфейс - набор методов, которые создатели браузера должны предоставлять программистам для управления документом. Интерфейс Node - представление всего html-документа в виде набора узлов, «точек разветвления» конструкции:

node.gif

В таком представлении одни элементы HTML вложены в другие. Некоторые элементы вложены в один и тот же элемент (как h3 и p в div на рисунке) и находятся на одном уровне, являются потомками одного родителя. Вот по-английски «потомки одного родителя» и будут - sibling. А если они ещё и следуют друг за другом, без всяких промежутков, то о них можно будет говорить, что второй из них - nextSibling, а первый для второго, соответственно, previousSibling. Вот и вся недолга. Благодаря этому мы можем использовать конструкции типа

abc.title=abc.nextSibling.firstChild.nodeValue+"...";

firstChild, как вы догадались, - это первый потомок данного родителя, то есть первый элемент, вложенный в данный элемент. Ну, а nodeValue - содержание узла (элемента). И хорошо если оно текстовое! А то ведь бывает, что всё содержание узла состоит только из других, вложенных в него элементов...

Теперь самое важное. Мы удалили уникальные идентификаторы всех элементов; как теперь сообщить программе, для какого именно элемента задавать значение свойства title? Когда программа (функция) JavaScript работает, есть возможность передать ей сообщение об элементе, который вызвал работу этой программы. Обозначить этот элемент можно тремя (примерно) способами. 1) srcElement («src» - от англ. source - «источник»), 2) target (англ. - «цель»), 3) this (англ. - «этот»). К несчастью, srcElement работает только в Интернет Эксплорере, а target - наоборот, только в Gecko и в Опере. Поэтому никакого выбора у нас и нет, если мы не хотим (а мы именно не хотим!) лишний раз полагаться на идентификацию типа браузера.

Таким образом, первую функцию title1(abc) мы будем вызывать из html-документа следующим кодом:

<A onmouseover="title1(this)"...

А сама функция будет выглядеть так:

function title1(abc) {
abc.title=abc.nextSibling.firstChild.nodeValue+"...";
}

Она будет искать то, что ей передано в качестве параметра abc, и найдёт слово this, имеющее в данном случае значение «элемент, который вызвал функцию». Поэтому мы можем спокойно использовать abc как синоним самого элемента a. Во всех браузерах.

Однако попытка проделать ту же штуку с атрибутом href не удаётся:

<A onmouseover="title1(this)" href="javascript:showref(this)">

В таком виде (в атрибуте href) параметр передать невозможно - работать не будет. Не знаю, почему. Надо принять, как факт, и найти другой способ. Мы ведь как-то передаём программе в первый раз, через метод onmouseover значение текущего элемента. Нельзя ли это значение записать, сохранить его для других функций? Оказывается, можно. Присвоить его переменной совсем просто: obj=abc; - и всё. Вопрос в том, при каких условиях другие функции будут видеть эту переменную и воспринимать её новое значение? Ответ: если эту переменную объявить (var obj;) вне всех функций, просто в теле скрипта. Что мы и сделали в файле wattsa.js текущего примера (папка omne\js3). После этого слово obj будет обозначать ссылку, над которой прошла мышь, и мы можем во всех остальных функциях прямо так и писать:

obj.nextSibling.style.display="block";

Возникает вопрос: а если мы захотим быстро-быстро, пока программа ничего не сообразила, щёлкнуть по другой ссылке? так, по-хитрому? Ответ: ничего у нас не получится. Потому что для щелчка по ссылке мышь должна сначала появиться над ссылкой. Это неизбежно, как налоги. Событие onmouseover всегда (по определению) наступает раньше события onclick (щелчок).

Нам придётся написать ещё одну, отдельную функцию hidetext(txt) для случая, если пользователь захочет в новых условиях скрыть примечание щелчком по самому тексту примечания (мы ведь обещали это ему всплывающей подсказкой!). Функция построена по той же логике, что и hideref(), с точностью до наоборот (вместо display="block" display="none", и вместо nextSibling - previousSibling) и с той разницей, что hideref() берёт значение объекта из инициализированной ранее переменной obj, а в hidetext(txt) ничто нам не мешает передать функции в качестве параметра (txt) значение самого элемента, вызвавшего функцию - через слово this:

function hidetext(txt){
txt.style.display="none";
txt.previousSibling.onclick="showref(this)";
txt.previousSibling.innerHTML="**>";
txt.previousSibling.title=txt.firstChild.nodeValue+"...";
}

Наша система обработки ссылок почти готова. Осталось кое-какие мелочи «после сборки доработать напильником». Дело в том, что, опытный пользователь всё-таки может обмануть нашу программу. Потому что «нажать ссылку» можно и без мышки: по ссылкам можно перемещаться с помощью клавиши Tab. Попробуйте в активном окне браузера нажать эту клавишу несколько раз подряд - вы увидите, как ссылки последовательно становятся активными: вокруг каждой по очереди появляется тонкая рамка из точек (и экран сдвигается). Ссылка, вокруг которой появилась рамка, становится активной, или, по-научному, говорят, что она «получает фокус». Если в тот момент, когда ссылка активна, нажать Enter, для ссылки это будет равносильно щелчку мыши. Но мышь перед этим над ссылкой не появлялась, а значит, переменная obj не получила нужного значения, и ни одна функция не сработает.

Исправление мелких неточностей

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

<a onfocus="title1(this)" onmouseover="title1(this)" href="javascript:showref()">

ex24 Файлы примеров: js4 (js4)

И заодно уж откорректируем идеологию «скрыть - показать» элемент. Дело в том, что в кнопке «Показать все ссылки» и в «индивидуальной программе показа» мы используем разные методы, что приводит к видимому недостатку: если какую-то ссылку скрыть-показать отдельно, щелчком по ней, то потом кнопка «Показать все ссылки» на эту ссылку не действует. Потому что кнопка меняет (с помощью файла CSS) стиль целого класса элементов (reftxt), а щелчок по ссылке - стиль отдельного элемента. Вполне разумно, что оформление элемента имеет приоритет перед оформлением класса. Это «естественная» логика.

Решить проблему можно, заставив функцию обработки отдельной ссылки действовать на том же уровне, что и общую кнопку - на уровне назначения (или изменения) классов, а не отдельных элементов. Для этого в файле wattsa.css создадим оформление ещё для одного класса - reftxtblock. Это оформление ничем не будет отличаться. от оформления класса reftxt, за исключением того, что reftxtblock имеет для свойства display значение block, а не none.

После этого во все наши функции show... и hide... вносим соответствующие изменения:

1) obj.nextSibling.className="reftxtblock";

2) obj.nextSibling.className="reftxt";

3) txt.className="reftxt";

Теперь что-то будет работать лучше, а что-то хуже:

1) если мы покажем одну ссылку и затем скроем её, кнопка «Все ссылки» будет теперь работать нормально;

2) если мы покажем одну ссылку и не скроем потом, кнопка «Все ссылки» на неё действовать не будет, потому что подключаемые файлы watts1.css и watts2.css влияют только на отображение класса reftxt, а не вновь созданного reftxtblock;

3) если мы отобразим все ссылки кнопкой, то ни одну из них нельзя будет скрыть индивидуально;

4) кроме того, кнопкой вообще не очень удобно пользоваться, потому что она расположена в одном конкретном месте документа, и если документ большой, её придётся искать (или просто перемещаться к ней, например, в начало документа клавишей «Home», потеряв текущий текст).

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

У меня логика получилась такая (вы, конечно, можете попробовать применить другие принципы):

1) кнопка в любой ситуации (были активированы отдельные ссылки или нет) отображает все ссылки;

2) кнопка не может скрыть ссылки, отображённые индивидуально;

3) при показе всех примечаний кнопка удаляет с экрана все ссылки на примечания (потому что после применения кнопки они перестают действовать), но зато действует щелчок по тексту примечания;

4) после отображения с помощью кнопки щелчок по любому примечанию скрывает все примечания (то есть запускает ту же процедуру, что и кнопка);

5) вместо кнопки можно нажимать клавишу F9.

Для начала пришлось всем ссылкам на примечания назначить className: <a class="a1"..., а в динамически подключаемые файлы watts1.css и watts2.css добавить правила «.a1 {display:none;}» и, соответственно, «.a1 {display:inline;}». Обратите внимание, что отображаем мы ссылки инструкцией не display:block, a «.a1 {display:none;}», потому что ссылка не является блочным элементом (и нам не надо, чтобы она отображалась отдельным прямоугольником, в отличие от элемента span). Теперь при отображении текстов примечаний сами ссылки на примечания исчезают с экрана. Чтоб не мозолили зря глаза и от соблазна подальше: щелчок по ним ведь всё равно ни к чему не приводит.

Зато щелчок по любому тексту примечания (если примечания были отображены кнопкой) скроет все тексты примечаний и покажет обратно все ссылки. Для этого в функцию hidetext(txt) мы добавили следующий кусок кода:

 var alln = document.getElementById("allnotes");
 var st11 = document.getElementById("st1");
 if (alln.value=="Скрыть примечания") {
  st11.href="watts1.css";
  alln.value="Показать все примечания";
 } 

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

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

 var agent = navigator.userAgent.toLowerCase();
 var i;
 if (agent.indexOf("msie") > -1 && agent.indexOf("opera") == -1) i=2;
 else i=1;

/*обработка нажатия клавиш для Gecko*/
document.onkeypress=keypressed;
function keypressed(e) {
 if (e.keyCode == 120) shownotes();
}
/*конец кода для Gecko*/

/*обработка нажатия клавиш для IE (в html-странице элементам body или div 
должна быть присвоена функция: <div onkeypress="keypressed1()">;
для Оперы надо ещё добавить onkeyup: 
<div onkeypress="keypressed1()" onkeyup="keypressed1()"> (только не спрашивайте меня, почему!)*/
function keypressed1() {
 if (i==2) 
  if (event.keyCode == 120) shownotes();
}
/*конец кода для IE*/

ex25 Файлы примеров: js5\wattsa.js (js5/wattsa.js)

Для перехвата нажатия клавиш в Gecko надо использовать функцию с параметром: keypressed(e) - название функции и параметр e произвольны; о том, что нужно анализировать («просматривать», «прослушивать») все нажатия клавиш, мы сообщаем браузеру Gecko в инструкции document.onkeypress=keypressed;. По счастью, Интернет Эксплорер эту инструкцию почему-то просто игнорирует, поэтому мы даже не стали вписывать её в условную конструкцию с проверкой типа браузера.

Собственно обработка, анализ кода нажатой пользователем (любой!) клавиши происходит в конструкции if (e.keyCode==120) (а для IE - в конструкции if (event.keyCode == 120).

Если для Gecko инструкцию перехвата мы записали прямо в коде JavaScript, то для IE (и Оперы) эта инструкция записывается в html-коде страницы - в качестве назначения свойства onkeypress (onkeyup для Оперы) элементу body. У нас, правда, нет в явном виде элемента body - ну что ж, в этом случае сойдёт и элемент div, в который в нашем документе вложены все остальные элементы (мы же сами их туда вложили, так что ошибки, связанной с болтающимся где-то «вне» элементом не будет).

Вот теперь, наконец, можно будет вздохнуть свободно и выложить нашу страничку в Интернет - никакие уловки пользователей и разнообразие браузеров нам больше не страшны. Хотя, на самом деле, работает наша конструкция всё-таки разнообразно и даже довольно забавно: лучше всего, без всяких оговорок, клавиша F9 работает в Firefox и, как ни странно, в капризной Опере; в Интернет Эксплорере и SeaMonkey клавиша F9 начинает работать по нашей программе только после какой-либо другой активизации скрипта (щелчком мышью по ссылке на примечание или по кнопке «Показать всё»); в SeaMonkey, как оказалось, клавиша F9 выполняет ещё и встроенную функцию - открывает боковую панель с кое-какими служебными кнопками и полем для поиска в Интернете.

Довести нашу работу до идеала вы, как всегда, можете самостоятельно. Можно, например, попробовать при загрузке страницы показать-скрыть какое-нибудь примечание (и этим инициировать работу скрипта). Можно поэкспериментировать с заменой свойств onkeypress - onkeyup - onkeydown. Ну, и конечно же, заменить функциональную клавишу F9 на какую-то другую. Вот их виртуальные коды (в принятой в спецификациях W3C шестнадцатеричной системе, а в скобках - в более понятной десятичной): F1 – 0x70 (112); F2 – 0x71 (113); F3 – 0x72 (114); F4 – 0x73 (115); F5 – 0x74 (116); F6 – 0x75 (117); F7 – 0x76 (118); F8 – 0x77 (119); F9 – 0x78 (120); F10 – 0x79 (121); F11 – 0x7A (122); F12 – 0x7B (123).

Полностью виртуальные коды всех возможных клавиш можно посмотреть на стр. http://www.w3.org/TR/2000/WD-DOM-Level-3-Events-20000901/events.html или, например, на http://www.xulplanet.com/references/objref/KeyboardEvent.html - на этой странице (в отличие от W3C) коды даются в десятичном представлении. Впрочем, перевести число из одной системы в другую не очень сложно: надо умножить первую цифру шестнадцатеричного кода на 16 и добавить вторую цифру (учитывая, что буквы от A до F обозначают, соответственно, «цифры» от 10 до 15).

При самостоятельном исследовании спецификаций W3C вы наверняка натолкнётесь на «проблему развития»: в более поздних версиях (начиная с 2002 года) в документе DOM-Level-3-Events виртуальные коды имеют другие значения (в частности, для «нашей» клавиши F9 - код x22 или, в десятеричной форме, 34). Для сравнения мы приводим два небольших файла из двух разных спецификаций в папке CD omne\doc (doc): idl2000.txt и idl2002.txt. Более поздние значения вроде бы должны быть и «более правильными», однако попробуйте заставить разработчиков интернет-браузеров каждый раз перелопачивать свои программы вслед за движением передовой мысли W3C! Действующими сейчас (IE 6.0, Firefox 1.5) по-прежнему являются стандарты 2000 года: если вы попытаетесь перехватывать событие виртуального кода 34, то примечания в нашем примере будут появляться и исчезать при нажатии клавиши Page Down.

Комментарии