Автоматическая генерация оглавления страницы
На днях появился вопрос об автоматическом создании оглавления статьи, а позже где-то проскочил комментарий, мол неплохо бы такое добавить на docs.modx.pro — там же все документы хорошо структурированы.
И действительно, а почему бы не добавить? Выделил полчасика и набросал сниппет makeContents, который генерирует вот такое оглавление:

Алгоритм работы простой: сниппет выбирает все заголовки от h1 до h5 и генерирует из них вложенный список, а после этого добавляет якоря перед заголовками в тексте.
Заморачиваться с чанками я не стал, потому что структура списка с идентификатором #page-contents позволяет оформить его на CSS как угодно.
У самих якорей указан класс .page-contents-link, чтобы можно было указать отступ от верхнего края:
Вызывать этот сниппет нужно фильтром вывода, например вот так:
В результате, теперь мы можем давать более точные ссылки на разделы в документации, например: docs.modx.pro/komponentyi/pdotools/obshhie-parametryi#Способы-вызова-чанков
Сниппет поставляется «как есть». Автор не даёт никаких гарантий его работоспособности у вас на сайте и не несёт ответственности за любые возможные нарушения в его работе.
И действительно, а почему бы не добавить? Выделил полчасика и набросал сниппет makeContents, который генерирует вот такое оглавление:

Алгоритм работы простой: сниппет выбирает все заголовки от h1 до h5 и генерирует из них вложенный список, а после этого добавляет якоря перед заголовками в тексте.
<?php
// Если в тексте нет заголовков от h1 до h5 - выходим
if (!preg_match_all('#<h([1-5])>(.*?)</h[1-5]>#', $input, $headers)) {return;}
// Если заголовков меньше 2х - тоже выходим
if (count($headers[0]) < 2) {return;}
$base = $modx->makeUrl($modx->resource->id, '', '', 'full');
$from = $to = array();
$depth = 0;
$start = null;
// Генерация меню
$contents = '<ul id="page-contents">';
foreach ($headers[2] as $i => $header) {
$header = preg_replace('#\s+#', ' ', trim(rtrim($header, ':!.?;')));
$anchor = str_replace(' ', '-', $header);
$header = "<a href=\"{$base}#{$anchor}\">{$header}</a>";
if ($depth > 0) {
if ($headers[1][$i] > $depth) {
while ($headers[1][$i] > $depth) {
$contents .= '<ul>';
$depth ++;
}
}
elseif ($headers[1][$i] < $depth) {
while ($headers[1][$i] < $depth) {
$contents .= '</ul>';
$depth --;
}
}
}
$depth = $headers[1][$i];
if ($start === null) {
$start = $depth;
}
$contents .= '<li>' . $header . '</li>';
$from[$i] = $headers[0][$i];
$to[$i] = '<a name="' . $anchor . '" class="page-contents-link"></a>' . $headers[0][$i];
}
// Закрытие всех открытых списков
for ($i = 0; $i <= ($depth - $start); $i ++) {
$contents .= "</ul>";
}
// Добавление якорей к заголовкам
$input = str_replace($from, $to, $input);
return $contents . $input;Заморачиваться с чанками я не стал, потому что структура списка с идентификатором #page-contents позволяет оформить его на CSS как угодно.
У самих якорей указан класс .page-contents-link, чтобы можно было указать отступ от верхнего края:
a.page-contents-link {display: block; position: relative; top: -50px; visibility: hidden;}Очень полезно при фиксированной шапке, как на docs.modx.pro.Вызывать этот сниппет нужно фильтром вывода, например вот так:
[[Markdown:makeContents@Docs]]Или вот так:[[*content:makeContents]]В результате, теперь мы можем давать более точные ссылки на разделы в документации, например: docs.modx.pro/komponentyi/pdotools/obshhie-parametryi#Способы-вызова-чанков
Сниппет поставляется «как есть». Автор не даёт никаких гарантий его работоспособности у вас на сайте и не несёт ответственности за любые возможные нарушения в его работе.
Комментарии: 25
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
"Все работает!htmlbook.ru/html/h1
И их можно простить — сайт для интранет. :)
Относительные ссылки лучше… как мне кажется =)
Кто знает, что ему лучше — поправит и сам.
Собственно зачем далеко бежать)? Вот решение.
— С наступающим :-)
Подскажите, а как можно изменить спиппет что бы содержимое страницы с раскиданными якорями можно было обернуть в один div, а сам список во второй div, не вложенный в первый?
на
И используй новые плейсхолдеры где хочешь:
Что то я рано обрадовался )
Если вызвать на странице:
… у меня получается, что на странице два плейсхолдера, один из которых дублирует содержимое. Если убрать [[!+page.contents]], вот такой вариант:
… выстроит список, но якоря не раскидывает.
Василий, а как только список в плейсходрер выводить, а остальное содержимое в [[*content]] ?
========== ВОПРОС СНЯТ — РАЗОБРАЛСЯ!=======================
нужно было
заменить на
Спасибо! )
Но, у меня возникла необходимость разместить код Адсенс между списком оглавления страницы и самим контентом. А как это сделать — не знаю (в PHP не очень...).
Может кто-нибудь подскажет, как правильно вставить код Адсенс в сниппет makeContents, чтобы рекламные блоки отображались между оглавлением и самой статьей.
Спасибо!
Как это поправить?
заменяем
на
и ещё проблема, если есть одинаковые заголовки, то все анкоры будут для них одинаковыми, и соответственно, вести к первому из них. Как это решить не знаю. Нужна помощь. Возможно стоит как-то проставлять номер для каждого найденного сниппетом заголовка.
Notice: Undefined offset: 2 in /home/site.ru/public_html/core/components/jevix/model/jevix/jevix.class.php on line 135
Notice: Undefined offset: 3 in /home/site.ru/public_html/core/components/jevix/model/jevix/jevix.class.php on line 135
Как это убрать?
Т.е. при переходе по ссылкам в сформированном оглавлении будет ли происходить переход на виртуальную страницу сгенерированную PageBreaker?
1. Для начала, после замены пробелов на тире, вырезаем все сомнительные символы, которые попадают в якорь:
Ставить нужно перед формированием оглавления.
2. Затем, в следующей строке добавляем дата атрибут, т.к. с атрибутом href, jQuery в данном случае отказывается работать:
3. Ну и добавляем якорь в id:
4. На закуску, готовая функция jQuery.
Вроде все описал. На моем сайте вроде все работает, проблем не встретил.
За помощь с решением проблемы с jQuery, спасибо @Евгений Webinmd
Все прекрасно работает есть в Заголовке нет классов или ID.
Вопрос: как заставить игнорировать Class и ID, например:
Вызываю на странице [[*content:makeContents]] — выводится просто текст статьи