Делаем содержание (оглавление) для больших статей

1575 просмотров Alan_Writer 23.03.2016

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

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

1. Простое оглавление (← демо). Итак, при написании статьи, каждому заголовку или блоку нового абзаца, задаем свой уникальный id, например так:
Код
<h2 id="steer">Бычок</h2>
Идет бычок, качается,
Вздыхает на ходу:
- Ох, доска кончается,
Сейчас я упаду!
<h2 id="bunny">Зайка</h2>
Зайку бросила хозяйка -
Под дождем остался зайка.
Со скамейки слезть не мог,
Весь до ниточки промок.
<h2 id="bear">Мишка</h2>
Уронили мишку на пол,
Оторвали мишке лапу.
Все равно его не брошу -
Потому что он хороший.

В блоке с содержанием, каждой ссылке соответствующей ее абзацу из статьи - прописываем соответствующий id (после знака #), вот так:
Код
<nav>
<h2>Содержание:</h2>
<ul>
<li><a href="#steer">Бычок</a>
<li><a href="#bunny">Зайка</a>
<li><a href="#bear">Мишка</a>
</ul>
</nav>

В принципе все элементарно, и должно быть понятно любому.

2. Нумерованное оглавление. Тут все так-же, только тег ul в блоке нужно заменить на ol:
Код
<nav>
<h2>Содержание:</h2>
<ol>
<li><a href="#steer">Бычок</a>
<li><a href="#bunny">Зайка</a>
<li><a href="#bear">Мишка</a>
</ol>
</nav>

3. Многоуровневое нумерованное оглавление. Тут уже посложнее. Начнем с самой статьи, допустим она состоит из двух основных тем (тег заголовка h2), в каждой из которых имеется несколько под-тем (тег заголовка h3):
Код
<h2 id="raz">Четверостишия</h2>
<h3 id="steer">Бычок</h3>
Идет бычок, качается,
Вздыхает на ходу:
- Ох, доска кончается,
Сейчас я упаду!
<h3 id="bunny">Зайка</h3>
Зайку бросила хозяйка -
Под дождем остался зайка.
Со скамейки слезть не мог,
Весь до ниточки промок.
<h3 id="bear">Мишка</h3>
Уронили мишку на пол,
Оторвали мишке лапу.
Все равно его не брошу -
Потому что он хороший.

<h2 id="dva">Стихи</h2>
<h3 id="r">Буква «Р»</h3>
Пять лет Сереже в январе,
Пока — четыре, пятый,
Но с ним играют во дворе
И взрослые ребята.
<h3 id="bullfinch">Снегирь</h3>
На Арбате, в магазине,
За окном устроен сад.
Там летает голубь синий,
Снегири в саду свистят.
Я им буду любоваться,
Будет петь он на заре...
Может, снова можно драться
Завтра утром во дворе?

Блок содержания в таком случае тоже строиться соответствующим образом:
Код
<nav class="toc">
<h2>Содержание:</h2>
<ol>
<li><a href="#raz">Четверостишия</a>
  <ol>
  <li><a href="#steer">Бычок</a></li>
  <li><a href="#bunny">Зайка</a></li>
  <li><a href="#bear">Мишка</a></li>
  </ol>
  </li>
<li><a href="#dva">Стихи</a>
  <ol>
  <li><a href="#r">Буква «Р»</a></li>
  <li><a href="#bullfinch">Снегирь</a>
  </li>
  </ol>
  </li>
</ol>
</nav>

Без стилей тут тоже не обойтись:
Код
.toc > ol {counter-reset: li;}
.toc > ol > li {counter-increment: li;}
.toc ol ol {counter-reset: lili;}

.toc ol ol li {
position: relative;
list-style: none;}

.toc ol ol li:before {
content: counter(li) "." counter(lili) ".";
counter-increment: lili;
position: absolute;
left: -2em;}

4. Фиксированное меню сверху. Блок ссылок теперь выглядит так:
Код
<nav class="toc">
<a href="#steer">Бычок</a>
<a href="#bunny">Зайка</a>
<a href="#bear">Мишка</a>
</nav>

В данном случае, поскольку хэш-ссылка прокручивает страницу к самому началу тега с соответствующим id, то фиксированное вверху меню накрывает часть этого тега. Чтобы страница прокручивалась немного выше, нужно прописать этим тегам псевдоэлемент :before
Код
.toc {  
position: fixed;
top: 0;
left: 0;
right: 0;
border: 4px solid #456;}

h2:target:before {
content: "";
display: block;
height: 2em;
margin-top: -2em;
visibility: hidden;}

5. Фиксированное меню сбоку, с подсветкой текущего пункта. Тут наша цель в том, чтоб тот пункт блока с содержанием, которому соответствует некий абзац, был неким образом выделен в тот момент когда вы (точнее окно браузера) находитесь в области этого абзаца.

Начнем с самого текста статьи. Теперь уникальными идентификаторами мы выделяем не заголовки, а полностью блоки этих абзацев (тег section), и идентификаторы эти теперь должны всегда начинаться с nav (nav1, nav2, nav3, итд). Например так:
Код
<section id="nav1">
<h2>Бычок</h2>
<p>Идет бычок, качается,
Вздыхает на ходу:
- Ох, доска кончается,
Сейчас я упаду!
</section>
<section id="nav2">
<h2>Зайка</h2>
<p>Зайку бросила хозяйка -
Под дождем остался зайка.
Со скамейки слезть не мог,
Весь до ниточки промок.
</section>
<section id="nav3">
<h2>Мишка</h2>
<p>Уронили мишку на пол,
Оторвали мишке лапу.
Все равно его не брошу -
Потому что он хороший.
</section>

Ну и блок содержания соответственно так:
Код
<nav>
<a href="#nav1">Бычок</a>
<a href="#nav2">Зайка</a>
<a href="#nav3">Мишка</a>
</nav>

Естественно, для выделения текущего пункта, нам теперь нужен еще и скрипт. Его прописываем в самый низ страницы:
Код
<script>
window.addEventListener('scroll', function(e) {
var nav = document.querySelectorAll('section[id^="nav"]');
for (var i = 0; i < nav.length; i++) {  
document.querySelector('a[href="#' + nav[i].id + '"]').className=((1 >= nav[i].getBoundingClientRect().top && nav[i].getBoundingClientRect().top >= 1-nav[i].offsetHeight) ? 'red' : '');
}
}, false);
</script>

Ну и конечно-же стили:
Код
section {
display:inline-block;
width:100%;}

nav {
position:fixed;
bottom:50%;
right:1em;
transform:translate(0, 50%);}

nav a {
display:block;
border-left:8px solid green;
padding:6px 8px;
text-decoration:none;}

nav a:hover{border-left:8px solid #f66;}
.red {border-left:8px solid red;}

6. Плавная прокрутка. Ну и последнее, это по сути небольшое дополнение к пятому примеру, суть которого в плавной прокрутке страницы. Просто добавляете этот скрипт к тому, что был дан в пятом примере:
Код
var linkNav = document.querySelectorAll('[href^="#nav"]'),
V = 0.6; // скорость прокрутки
for (var i = 0; i < linkNav.length; i++) {
linkNav[i].onclick = function(){
var w = window.pageYOffset,
hash = this.href.replace(/[^#]*(.*)/, '$1');
t = document.querySelector(hash).getBoundingClientRect().top,
start = null;
requestAnimationFrame(step);
function step(time) {
if (start === null) start = time;
var progress = time - start,
r = (t < 0 ? Math.max(w - progress/V, w + t) : Math.min(w + progress/V, w + t));
window.scrollTo(0,r);
if (r != w + t) {requestAnimationFrame(step)} else {location.hash = hash}
}
return false;
}
}

Ну вот и все. Если материал оказался полезным - оценивайте и делитесь им, это сука важно!

Теги: контент, оглавление, прокрутка, Содержание, хеш-ссылки
0 Комментариев