На HTML-страницах многих сайтов существуют гиперссылки наподобие
<a href="#idName">...</a>, которые ведут не на другую страницу, а к некоторому месту на той же сáмой странице, где и ссылка. Это обычное дело для обширных статей с оглавлением (если каждый пункт оглавления является такой гиперссылкою, которая ведёт к названному в нём заголовку) или с примечаниями (если надстрочный знак примечания служит гиперссылкою и ведёт к примечанию в конце текста, а от примечания стоит гиперссылка в обратном направлении). Таких статей немало в сетевых энциклопедиях (вики, например) или в серьёзных сетевых журналах.
К сожалению, переход по такой внутренней гиперссылке в большинстве современных браузеров Паутины совершается мгновенно, ничуть не заметно для читателя. Это совсем не то, что проматывание страницы вручную, которое происходит плавно и занимает некоторое (заметное взору) время, так что даёт читателю некоторое представление об объёме того текста, мимо которого он пролетает.
Досадно, не правда ли?
К счастью, существует плагин для jQuery, который позволяет невозбранно достичь желаемого, то есть без труда обратить всякий переход по внутренней гиперссылке документа именно в такое проматывание, во всём подобное ручному, но только совершаемое автоматически и за достаточно краткое время
(по умолчанию — за секунду), так что читатель как раз успевает осознать происходящее и оценить направление проматывания и пройденное расстояние, но ещё не успевает заскучать.
Этот плагин называется
jQuery.LocalScroll, и он, окромя jQuery, потребует для своей работы ещё другой плагин
(jQuery.ScrollTo), обёрткою для которого является. Так что достаточно установить jQuery
и оба эти плагина — и тогда в дальнейшем вызов функции, включающей автоматическое проматывание для всех внутренних гиперссылок, станет можно записывать как нельзя проще:
$($.localScroll());
Вроде бы всё хорошо. Но проблема в том,
что по умолчанию такое проматывание является
просто проматыванием: документ прокручивается в окне у читателя, и больше ничего.
А ведь, чтобы полностью воссоздать итог перехода по внутренней гиперссылке, необходимо также добавить
строку «#idName» к адресу (в строке адреса, учитываемой браузером). Не то слишком уж далеко возвращала бы читателя кнопка «Назад» в браузере: именно ею выразив желание вернуться назад к оглавлению от подраздела (или же от примечания к поясняемому им тексту), читатель неожиданно вернулся бы к предыдущей странице.
Чтобы имитация перехода по внутренней гиперссылке оставалась совершенною, то есть чтобы добавлять
строку «#idName» к адресу страницы, Ariel Flesler (автор плагина
jQuery.LocalScroll) предлагает включать параметр «hash»:
$($.localScroll({
hash: true
}));
После этого #-часть адреса действительно переменяется,
и кнопка «Назад» начинает работать нормально.
Но достигает ли желаемого совершенства такая имитация перехода по внутренней гиперссылке?
Увы, нет.
Дальнейшие проблемы начинаются, если в стилях документа используется селектор «:target»,
появившийся в CSS3. Например, стилевое правило
*:target { background-color: red } вроде бы должно подсвечивать ту область документа, к которой произошёл переход по внутренней гиперссылке. И в новых браузерах действительно подсвечивает
(как гласит MDC, этот селектор поддерживается
в Firefox 1.0, в Opera 9.5, в Safari 1.3, и в более новых). Однако, если задействован вызов
$($.localScroll()) или $($.localScroll({hash: true})), то в Файерфоксе прокрутка происходит плавно,
но CSS-подсветка вырубается. Напрочь.
Что делать?
Я нипочём не догадался бы о причине этого глюка, когда не взглянул бы в исходники
плагина LocalScroll нынешней версии (1.2.7).
Оказывается, Ариэль Флеслер решил радикально упростить себе жизнь: вместо того, чтобы дожидаться конца проматывания (и навесить на него такое событие, которое переменяло бы
#-часть адреса именно после того, как проматывание полностью завершилось), плагин
ещё до проматывания присваивает
#-части адреса страницы желаемое значение. Вот так:
location = link.hash;
Просто, не правда ли?
Но ведь такое присваивание автоматически
(и сразу!) прокрутит документ к указанному месту назначения, и тогда вся идея плавного проматывания
пойдёт прахом?
Правильно!
И потому, раз уж Ариэль Флеслер решил радикально упростить себе жизнь, то он идёт в своём намерении до конца: его плагин сперва отбирает атрибут
«id="idName"» (или «name="idName"») у места назначения гиперссылки, затем создаёт
в поле зрения (чтобы исключить прокручивание) новый пустой элемент именно с таким атрибутом, затем присваивает
#-части адреса желаемое значение (прокручивание не происходит), затем убивает этот свежесозданный пустой элемент, а месту назначения внутренней гиперссылки возвращает отобранный
атрибут — и только тогда начинает спокойно, медленно к нему проматывать.
Файерфокс, разумеется, при этом селектор «:target» применяет именно к новому пустому элементу (который оказывается убит), а гибель этого элемента и дальнейшее появление аналогичного атрибута у другого
элемента — игнорирует хладнокровнейше. Я не решил ещё, можно ли считать такое поведение багом Файерфокса. Я также не проверял, как поведут себя Opera, Safari и Chrome в этой ситуации. Мне достаточно и того, что теперь я знаю, как преодолеть это поведение.
Достаточно вызвать
$.localScroll() с нормальным человеческим обработчиком, срабатывающим
именно после того, как проматывание завершилося:
$.localScroll({
onAfter: function(target){
location = '#' + ( target.id || target.name );
}
});
Вот и всё.
Просто, не правда ли?
Оно всегда так просто, когда всё ужé ясно.