Подготовка ссылок к печати
Aaron Gustafson, 5 сентября 2007
Заканчивая статью о работе с элементом select, я распечатал ее, чтобы моя жена Келли прочла и указала на ошибки. Поскольку это была статья для публикации в веб, я оформил ее в HTML и распечатал с тестового сервера, чтобы она выглядела именно так, как будет выглядеть на сайте, я был уверен, что мои стили для печати безупречны.
Но, только начав читать страницу, Келли раздраженно заметила: «Как я должна читать статью с этими URL посреди текста?» Я понял, что мое стремление использовать множество ссылок конфликтует с желанием сделать печатные версии максимально удобными.
Что же мне было делать? Конечно, искать решение проблемы. Посвятить эту статью я хочу Келли, теперь она может беспрепятственно помогать мне в вычитке статей.
Стили для печати от Эрика Майера
Много месяцев назад, Эрик Майер написал для A List Apart великолепную статью, в которой предложил очень интересный способ отображения ссылок при печати:
a:link:after,
a:visited:after {
content: " (" attr(href) ") ";
font-size: 90%;
}
Используя это правило в стилях для печати можно заставить любой поддерживающий CSS2 браузер вставлять после ссылки значение атрибута href, только чуть меньшим шрифтом и в скобках:
Эта техника очень полезна для пользователей распечатывающих страницу, поскольку позволяет сразу видеть цель каждой ссылки. К сожалению, если параграф содержит множество ссылок (особенно длинных) эта техника значительно снижает читабельность:
Для нас это неприемлемо.
План
Много лет занимаясь печатью, я очень хорошо относился к сноскам и концевым сноскам. Они являются стандартным решением для печати, а поскольку в данном случае мы имеем дело с печатью, то концевые сноски нам идеально подходят. Воодушевленный этой мыслю, я пришел к следующему алгоритму:
- Собираем все URI (атрибуты
hrefиcite) в пределах контейнера с контентом (ссылки из навигации и тому подобных элементов нам не нужны). - Создаем упорядоченный список ссылок и размещаем его в отдельном контейнере
- К каждой ссылке добавляем сноску с помощью тега
sup
К счастью, этот алгоритм несложно реализовать, манипулируя DOM с помощью JavaScript.
Скрипт
Прежде чем приступить к написанию скрипта, давайте подробно опишем его работу:
function footnoteLinks() {
// получаем контейнер и элемент,
// в который будут вставлены концевые сноски
// создаем заголовок для списка концевых сносок
// создаем <ol> для концевых сносок
// создаем массив в котором будем запоминать ссылки,
// чтобы иметь возможность проверить дубликаты
// создаем переменную, содержащую номер текущей ссылки,
// чтобы проставлять соответствующие индексы сносок
// собираем все элементы содержащиеся в контейнере в массив
// просматриваем все элементы массива
// в поисках атрибутов href и cite
// если дубликат
// получаем соответствующий номер из массива
// использованных ссылок
// если не дубликат
// создаем <li> и добавляем к <ol>
// сохраняем ссылку в массив использованных ссылок
// увеличиваем на единицу счетчик
// создаем <sup> и добавляем после ссылки
// добавляем заголовок и список концевых сносок
// к элементу указанному элементу
}
После столь подробного описания реализация алгоритма не должна вызвать затруднений, тем не менее, мы рассмотрим ее шаг за шагом. Чтобы упростить повторное использование скрипта будем передавать в функцию два параметра: идентификатор контейнера, в пределах которого собираются ссылки (containerID) и идентификатор элемента в который вставляется список концевых сносок (targetID):
function footnoteLinks(containerID,targetID) {
По известным идентификаторам мы легко определим соответствующие элементы:
// get the container & target
var container = document.getElementById(containerID);
var target = document.getElementById(targetID);
Следующим шагом, создадим заголовок для списка ссылок. Поскольку мы хотим, чтобы он отображался только при печати, то установим для него класс printOnly (соответствующее правило CSS напишем позже):
// создаем заголовок для списка концевых сносок
var h2 = document.createElement('h2');
addClass.apply(h2,['printOnly']);
var h2_txt = document.createTextNode('Links');
h2.appendChild(h2_txt);
Примечание: мы используем функцию addClass() из Easy! Designs jsUtilities
Теперь можно создать упорядоченный список и другие переменные, которые будут использоваться в нашей функции:
// создаем <ol> для концевых сносок
var ol = document.createElement('ol');
addClass.apply(ol,['printOnly']);
// создаем массив для хранинея использованных ссылок
// чтобы мы могли проверять ссылки на дублирование
var myArr = []; // to store all the links
var thisLink; // to store each link individually
// создаем переменную для отслеживания количества
// найденных уникальных ссылок
var num = 1;
Нам нужно пройти по всем элементам внутри контейнера, проверяя наличие атрибутов. Нужно заметить, что в XHTML 2.0 мы могли бы сделать любой элемент ссылкой, задав атрибут href, а атрибут cite может быть почти у любого элемента, а не только у blockquote и q, поэтому будем проверять наличие этих атрибутов у всех элементов без исключения:
// создаем массив содержащий все элементы
// из контейнера
var coll = container.getElementsByTagName('*');
Пройдем по всем элементам коллекции в поисках атрибутов href или cite:
for (var i=0; i<coll.length; i++) {
// проверяем наличие атрибутов
if ( coll[i].getAttribute('href') ||
coll[i].getAttribute('cite') ) {
// сохраняем ссылку
thisLink = coll[i].getAttribute('href') ? coll[i].href
: coll[i].cite;
Пока все идет неплохо. Давайте создадим надстрочные индексы:
var note = document.createElement('sup');
addClass.apply(note,['printOnly']);
Сейчас мы предполагаем, что каждый URI уникален, чуть позже внесем в скрипт исправления для проверки дубликатов:
var note_txt = document.createTextNode(num);
note.appendChild(note_txt);
Предполагаем, что имеем дело с чем угодно кроме элемента blockquote, и добавляем sup после каждой ссылки, вставляя его перед следующим за ссылкой элементом:
coll[i].parentNode.insertBefore(note, coll[i].nextSibling);
Добавляем URI в список сносок и массив, который будем использовать для проверки дубликатов:
// создаем <li> и добавляем к <ol>
var li = document.createElement('li');
var li_txt = document.createTextNode(thisLink);
li.appendChild(li_txt);
ol.appendChild(li);
// сохраняем ссылку в массив
myArr.push(thisLink);
Примечание: не все браузеры поддерживают метод push, но вы можете определить этот метод самостоятельно или воспользоваться jsUtilities.
Увеличиваем счетчик, чтобы перейти к следующему элементу, и закрываем цикл:
// увеличиваем счетчик на единицу
num++;
}
Последнее, что нам нужно сделать, это добавить заголовок и список ссылок к элементу контейнеру:
target.appendChild(h2);
target.appendChild(ol);
}
Перед тем как использовать скрипт, нужно позаботиться о корректной обработке дублирующихся ссылок. Для этого мы будем проверять наличие ссылки в массиве myArr с помощью функции inArray (доступной в jsUtilities). inArray ищет заданное значение в массиве и если находит его, возвращает индекс элемента, иначе false.
for (var i=0; i<coll.length; i++) {
if ( coll[i].getAttribute('href') ||
coll[i].getAttribute('cite') ) {
thisLink = coll[i].getAttribute('href') ? coll[i].href
: coll[i].cite;
var note = document.createElement('sup');
addClass.apply(note,['printOnly']);
var note_txt;
var j = inArray.apply(myArr,[thisLink]);
if ( j || j===0 ) { // если дубликат
// получаем соответствующий номер
// из массива ссылок
note_txt = document.createTextNode(j+1);
} else { // если не дубликат
var li = document.createElement('li');
var li_txt = document.createTextNode(thisLink);
li.appendChild(li_txt);
ol.appendChild(li);
myArr.push(thisLink);
note_txt = document.createTextNode(num);
num++;
}
note.appendChild(note_txt);
}
}
В этом коде мы проверяем есть ли ссылка thisLink в массиве myArr, а потом исходя из ее наличия или отсутствия в массиве использованных ссылок вычисляем для нее индекс. Если thisLink уже есть в массиве myArr индекс ссылки будет j+1 (индексация массива начинается с 0, а списка с 1), а если ссылки в массиве нет, нам нужно создать новый элемент списка добавить его к ol, добавить ссылку в массив myArr и создать сноску (с индексом равным следующему значению num). Обратите внимание, что функция inArray возвращает индекс элемента в массиве, а он может быть равен нулю, если соответствующая ссылка является первым элементом массива.
Следующее, что мы должны исправить это работа с элементами blockquote. В соответствии с принятым в типографике стилем, нам нужно расположить индекс сноски в конце последней строки цитаты, для этого нужно найти последнего потомка blockquote, который является содержащим текст элементом уровня блока. Значительно упростить это задачу нам поможет функция lastChildContainingText (которая включена в jsUtilities):
if (coll[i].tagName.toLowerCase() == 'blockquote') {
var lastChild = lastChildContainingText.apply(coll[i]);
lastChild.appendChild(note);
} else {
coll[i].parentNode.insertBefore(note, coll[i].nextSibling);
}
Немного доработаем скрипт, чтобы исключить возникновение ошибок, если браузер не поддерживает необходимые методы…
function footnoteLinks(containerID,targetID) {
if (!document.getElementById ||
!document.getElementsByTagName ||
!document.createElement) return false;
...или на странице нет элемента с идентификатором, переданным в функцию как идентификатор контейнера, или контейнер на странице есть, но не содержит дочерних элементов:
function footnoteLinks(containerID,targetID) {
if (!document.getElementById ||
!document.getElementsByTagName ||
!document.createElement) return false;
if (!document.getElementById(containerID) ||
!document.getElementById(targetID)) return false;
Вызывать нашу функцию будем в обработчике window.onload.
window.onload = function() {
footnoteLinks('container','container');
}
Автор пропустил еще одну важную проверку, были ли найдены в контенте ссылки:
if(myArr.length != 0) { target.appendChild(h2); target.appendChild(ol); }
И последнее, определим класс printOnly в файле, содержащем стили для отображения страницы на экране.
.printOnly {
display: none;
}
Все. Можете перейти к рассмотрению готового примера.
Неожиданности
Конечно, наша функция будет работать только, если доступен JavaScript, но, что если это не так. Чтобы не ударить в грязь лицом мы сохраним стили Эрика Майера, и будем отключать их при успешном запуске скрипта.
Добавим в файл со стилями для печати простое правило:
html.noted a:link:after,
html.noted a:visited:after {
content: "";
}
А при запуске скрипта будем задавать элементу html класс noted.
Заключение
Ну вот теперь печатная версия вашей страницы великолепна и не содержит элементов ухудшающих читабельность, а все ссылки и цитаты оформлены в виде концевых сносок. Возможно, вы захотите внести в скрипт свои коррективы, например, возможность игнорировать ссылки для которых задан класс ignore.
Вы можете загрузить последнюю сжатую версию footnoteLinks со страницы посвященной этой технике или загрузить рабочие файлы к статье с этого сайта. Все прототипы JavaScript, использованные в этой статье, содержаться в библиотеке jsUtilities 2.1.
Translated with the permission of A List Apart Magazine and the author[s].

















Евгений 5 сентября, 2007 13:45 #
Применил метод на этом сайте, печатные версии стали намного удобнее (чтобы увидеть эффект, возможно, придется обновить страницу), правда пока не работает на страницах категорий, но скоро я это исправлю.
Design For Masters » 15 минут на подготовку к печати 5 сентября, 2007 14:38 #
[...] замечательный метод подготовки ссылок к печати, работающий во всех браузерах, с помощью JavaScript, [...]
Nikita 5 сентября, 2007 16:53 #
Интересное решение. Но оправдано ли? Какой смысл в распечатке ссылок? Не думаю, что кому-то будет удобно вручную вводить ссылки. Проще зайти на сайт, где опубликована статья и потэкать на ссылки там.
Оффтоп: у вас RSS не показывает ссылку "читать далее". Не понятно, это весь пост или только его excerpt?
Евгений 5 сентября, 2007 18:25 #
Я тоже не думаю, что это жизненно важная техника, но очень уж понравилась идея, к тому-же в таком виде ссылки абсолютно не мешают, читать печатную версию (конечно, их необходимость там вопрос дискуссионный).
RSS: Не мог даже представить, что будут такие затруднения. Посмотрел на нескольких сайтах, нет такой ссылки ни у кого, это если не повод делать так, то повод задуматься нужна ли эта ссылка. Мне кажется размер except`а и поста различается настолько, что никаких пояснений не требуется, тем не менее подумаю над этим.
Nikita 5 сентября, 2007 19:08 #
RSS: на других сайтах excerpt заканчивается так: [...] поэтому можно понять, что есть продолжение. В другом случае есть ссылка "more..."
diGreez 5 сентября, 2007 23:55 #
Хорошая техника, обязательно возьму на вооружение :)
P.S. На сайте вполне нормальный RSS и достаточно информативный анонс статьи.
А.Стоблогов 6 сентября, 2007 7:47 #
Отличная подборка статей! Спасибо всем кто создает этот сайт! Обязательно включу "Дизайн для Мастеров" в свой список лучших 100 блогов :)
Евгений 6 сентября, 2007 11:37 #
Спасибо. Список лучших 100 блогов это отличная идея, прошелся по всем, блогам которые там уже есть и подписался на RSS.
Mikael 10 сентября, 2007 15:22 #
Хм, нужно попробовать. Спасибо!
LOBsTerr 16 октября, 2007 22:26 #
Отличная идея)) отличная статья!!!
Спасибо большое узнал для себя много нового
Лучшее для веб-мастеров и блогеров за октябрь 2007 » Журнал для веб-мастеров и блогеров от школы создания сайтов 1 ноября, 2007 8:21 #
[...] Подготовка ссылок к печати При подготовке страницы к печати возникает интересный вопрос, что делать с ссылками, часто на них просто не обращают внимания. Между тем в печати для указания источников цитат уже давно используются концевые сноски, почему бы не применить их в печатных версиях веб-страниц. [...]
cssing :: Архив :: CSS print framework 8 апреля, 2008 1:50 #