закрыть
Глаза страшат, а руки делают

Сноски в тексте


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

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

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

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

Основа скрипта

На страницу помещается невидимый блок-контейнер с position: absolute, но без указания top и left: это будет указано в скрипте. В этот контейнер вкладывается ещё один блок, в котором, собственно, и будет располагаться текст сноски и ссылка «закрыть». Стили обоих блоков заранее прописываются в тэге <style>. В стилях также указываем параметры для <span>, в который будем заключать номера сносок и ссылку «закрыть». Вот эта конструкция.

Стили:

<style type="text/css">
/*div-контейнер, который появляется при клике по сноске*/
#cnt {position: absolute; Z-Index: 20; display: none; width: 450px;}
/*вложенный в него div с текстом комментария*/
#obj {position: relative; top: 0px; left: 0px; background-color: #FFFFE0; border: 1px solid Black; padding: 6px;}
span.rem {font-weight: bold; font-style: italic; color: Blue; cursor: pointer;}
</style>

Ширину блока я дал 450 px, но это на ваше усмотрение (как и цвет фона, и прочие дизайнерские настройки).

Код HTML (помещается сразу под тэгом <body>):

<!-- болванка комментария -->
<div id="cnt">
  <div id=
"obj">
    <div id=
"text"></div>
    <div align=
"right">
    <span class=
"rem"
    onClick="document.getElementById('cnt').style.display='none';">закрыть</span>
    </div>
  </div>
</div>

Собственно JavaScript

1. Создаём массив текстов сносок. Чтобы не загромождать код web-страницы, имеет смысл вынести его в отдельный файл.js (например, cmnt.js):

var commtxt1 = new Array;
commtxt1[0] = "Раньше все эти формы, имеющие признаки как рептилий, так и амфибий, объединяли в особый подкласс батрахозавров, но ныне считают, что батрахозавры — сборная группа, и название это не употребляется.";
commtxt1[1] = "Это особенно интересно на фоне того, что ранее, в девоне, существуют находки амфибий и за пределами тропического пояса, например в Австралии. Дело в том, что девонские амфибии были еще существами чисто водными, а в воде, как известно, температурные перепады между климатическими зонами сильно выровнены.";
commtxt1[2] = "Весьма своеобразная эндотермия возникает даже у некоторых рыб — высокоскоростных хищников вроде тунца или меч-рыбы, но как раз у рыб-то, с их двухкамерным сердцем и единственным кругом кровообращения, кровь разделена на венозную и артериальную изначально!";
commtxt1[3] = "У насекомых, тоже пошедших по пути изоляции покровов и экономии воды, конечным продуктом белкового обмена также является мочевая кислота.";
/* и т.д. */

2. Теперь создаём скрипт, управляющий показом комментариев. Он небольшой, и его можно поместить прямо в «голове» кода web-страницы.

/* Функция для позиционирования комментария */
function move() {
   var
myrem = eval("document.getElementById('cnt')");
/* Это моя позиция блока: можете задать свою */
   myrem.style.top = document.body.scrollTop + 100;
   myrem.style.left = document.body.scrollLeft + 100;
}

/* сохранение позиции при изменении размеров окна */
window.onresize = move;
/* сохранение позиции при скроллинге (не работает в Мозилле 1 версии */
window.onscroll = move;
/* специально для Мозиллы, чтобы позиционировалось при клике по сноске */
window.onсlick = move;

/*Функция для показа комментария:
fld - id блока-контейнера
txt - id блока или абзаца, содержащего текст комментария
rem - имя массива с комментариями
nr - номер нужного элемента.*/

function showrem(fld,txt,rem,nr) {
   document.getElementById(fld).style.display = "block";
   var explain = rem[nr-1];
   document.getElementById(txt).innerHTML = explain;
}

Чтобы это работало, необходимо добавить в тэг <body> событие onLoad:

<body onload="move();">

Поясню некоторые моменты.

document.body.scrollTop и document.body.scrollLeft задают позицию левой верхней точки экрана браузера независимо от степени прокрутки страницы. Я поставил смещение на 100 px в обе стороны от угла. Вы можете настроить по-своему.

В процессе чтения мы прокручиваем текст, а также можем изменить размеры окна. Нужно, чтобы скрипт учитывал и «ловил» эти изменения, то есть реагировал на события window.onscroll и window.onresize.

Но есть одна «закавыка». «Мозилла» 1-й версии (до эпохи «Firefox») не реагирует на window.onscroll при прокрутке колёсиком мышки. Первый комментарий появляется где надо, а последующие (если мы работаем колёсиком, а не правой полосой прокрутки) будут появляться в той же точке, откуда «поезд уже давно ушёл».

Но каждый раз, когда мы вызываем комментарий, мы щёлкаем по сноске, а значит, вызываем событие window.onclick. На это событие реагирует и ранняя «Мозилла». Поэтому добавляем window.onсlick = move.

По функции showrem(fld,txt,rem,nr).

Сноски обычно начинаются с единицы. А элементы массива — с нуля. Чтобы не забивать этим голову при установке каждой конкретной ссылки, вносим эту поправку в функцию:

var explain = rem[nr-1];

3. Вызов комментария из текста.

<p>Среди карбоновых амфибий появляются небольшие (менее 1 м), похожие на современных саламандр антракозавры, имеющие несомненные приспособления к наземной жизни и явно ориентированные на питание беспозвоночными (а не рыбой — как их более крупные родственники). Именно антракозавров считают предками рептилий(<span class="rem" onClick="showrem('cnt', 'text', commtxt1, Number(this.innerHTML))">1</span>); первые рептилии — карбоновые капториниды — напоминали по внешнему виду крупных ящериц, причем, судя по строению их челюстного аппарата, они специализировались именно на питании насекомыми.</p>

Чтобы не писать в каждом вызове события onClick разные параметры для аргумента nr, можно «вытаскивать» нужный номер из содержимого самого тэга:

Number(this.innerHTML))

Примечание: при использовании этого способа внутри тэга должно быть только число, и никаких скобок, звёздочек, и т. п.

Полный код скрипта:

<head>
...
<style type="text/css">
#cnt {position: absolute; Z-Index: 20; display: none; width: 450px;}
#obj {position: relative; top: 0px; left: 0px; background-color: #FFFFE0; border: 1px solid Black; padding: 6px;}
span.rem {font-weight: bold; font-style: italic; color: Blue; cursor: pointer;}
</style>
<!-- Ссылка на скрипт с массивом текстов комментариев -->
<script src="cmnt.js" type="text/javascript"></script>
<script type="text/javascript">

function move() {
   var
myrem = eval("document.getElementById('cnt')");
   myrem.style.top = document.body.scrollTop + 100;
   myrem.style.left = document.body.scrollLeft + 100;
}

window.onresize = move;
window.onscroll = move;
window.onсlick = move;

function showrem(fld,txt,rem,nr) {
   document.getElementById(fld).style.display = "block";
   var explain = rem[nr-1];
   document.getElementById(txt).innerHTML = explain;
}
</script>
</head>

<body onload="move();">
<!-- болванка комментария -->
<div id="cnt">
  <div id=
"obj">
    <div id=
"text"></div>
    <div align=
"right">
    <span class=
"rem"
    onClick="document.getElementById('cnt').style.display='none';">закрыть</span>
    </div>
  </div>
</div>

<!-- текст на странице -->
<p>Среди карбоновых амфибий появляются небольшие (менее 1 м), похожие на современных саламандр антракозавры, имеющие несомненные приспособления к наземной жизни и явно ориентированные на питание беспозвоночными (а не рыбой — как их более крупные родственники). Именно антракозавров считают предками рептилий(<span class="rem" onClick="showrem('cnt', 'text', commtxt1, Number(this.innerHTML))">1</span>); первые рептилии — карбоновые капториниды — напоминали по внешнему виду крупных ящериц, причем, судя по строению их челюстного аппарата, они специализировались именно на питании насекомыми.</p>
...
</body>

А теперь смотрим, как это работает

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

В уроке использован текст из книги К. Ю. Еськова «История Земли и жизни на ней».

Дополнение (2010)

Когда я писал эту статью, то ещё не углублялся в тонкости блочной вёрстки, связанные с тэгом DOCTYPE (об этом тэге см. в статье «CSS и DOCTYPE»).

Всё вышеизложенное прекрасно работает, когда DOCTYPE определён как <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">. При более современном и корректном определении — <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> — всплывающие сноски перестают появляться (то есть они появляются, но в верхней части страницы, а не экрана; и если страница прокручена вниз, то они не видны).

Для совместимости со строгим (strict) определением DOCTYPE следует поменять свойство body на свойство documentElement, то есть вместо

   myrem.style.top = document.body.scrollTop + 100;
   myrem.style.left = document.body.scrollLeft + 100;

нужно записать

   myrem.style.top = document.documentElement.scrollTop + 100;
   myrem.style.left = document.documentElement.scrollLeft + 100;

Но этот вариант не работает при определении DOCTYPE как Transitional.

А есть ли вариант, работающий с любым значением DOCTYPE?

Дополнение (2012)

Сначала мне показалось, что я нашёл варианты.

Например, вызвать элемент body методом getElementsByTagName(). Поскольку этот метод вызывает массив тэгов, то и тэг <body> также является массивом, правда, состоящим из одного (нулевого) элемента. Этот элемент нужно указать.

Вместо document.body нужно вызвать document.getElementsByTagName("body").item(0).

То есть:

myrem.style.top = document.getElementsByTagName("body").item(0).scrollTop + 100;
myrem.style.left = document.getElementsByTagName("body").item(0).scrollLeft + 100;

Попробовал я и ещё один кроссплатформенный вариант, для которого нужно задать для <body> какой-нибудь id, например:

<body id="telo">

И воспользоваться методом getElementById():

myrem.style.top = document.getElementById("telo").scrollTop + 100;
myrem.style.left = document.getElementById("telo").scrollLeft + 100;

Но оказалось, что они работают не всегда и не везде. Так что вопрос окончательно не решён. Единственное, что я могу порекомендовать, это устанввливать на подобных страницах DOCTYPE как Transitional. Но тут могут возникнуть проблемы с вёрсткой (см. «CSS и DOCTYPE»)



 005559