Автоматическая генерация оглавления страницы
На днях появился вопрос об автоматическом создании оглавления статьи, а позже где-то проскочил комментарий, мол неплохо бы такое добавить на 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
Теги h1...h6 относятся к блочным элементам, они всегда начинаются с новой строки, а после них другие элементы отображаются на следующей строке. Кроме того, перед заголовком и после него добавляется пустое пространство.
Был бы редактор — можно и оторвать. А так — менеджеры, с отметкой в резюме «уверенный пользователь ПК». :)
И их можно простить — сайт для интранет. :)
И их можно простить — сайт для интранет. :)
Внесу предложение.
Относительные ссылки лучше… как мне кажется =)
$base = $modx->makeUrl($modx->resource->id, '', '', 'abs');
Относительные ссылки лучше… как мне кажется =)
Тут недавно было сразу несколько однотипных вопросов из-за незнания о base href, так что я уже перестраховываюсь.
Кто знает, что ему лучше — поправит и сам.
Кто знает, что ему лучше — поправит и сам.
Я просто отталкиваюсь от того, что если делать js скрипт подсветки внешних ссылок (как в википедии), то они будут иметь http в начале href… а так без разницы =)
Собственно зачем далеко бежать)? Вот решение.
— С наступающим :-)
Собственно зачем далеко бежать)? Вот решение.
— С наступающим :-)
Думал как сделать навигацию по странице с фиксированным справа сайдбаром как тут. Написанный Василий сниппет оказался как нельзя кстати, но у меня возникли сложности, т.к. не знаю php :)
Подскажите, а как можно изменить спиппет что бы содержимое страницы с раскиданными якорями можно было обернуть в один div, а сам список
Подскажите, а как можно изменить спиппет что бы содержимое страницы с раскиданными якорями можно было обернуть в один div, а сам список
<ul id="page-contents"><ul>
во второй div, не вложенный в первый?
Замени в конце
И используй новые плейсхолдеры где хочешь:
return $contents . $input;
на$modx->setPlaceholder('page.contents', $contents);
$modx->setPlaceholder('page.text', $input);
И используй новые плейсхолдеры где хочешь:
[[!+page.contents]] и [[!+page.text]]
Все работает! Спасибо!!!
Если вызвать на странице:
[[*content:makeContents]]
[[!+page.text]] [[!+page.contents]]
… у меня получается, что на странице два плейсхолдера, один из которых дублирует содержимое. Если убрать [[!+page.contents]], вот такой вариант:
[[*content:makeContents]]
[[!+page.contents]]
… выстроит список, но якоря не раскидывает.
Василий, а как только список в плейсходрер выводить, а остальное содержимое в [[*content]] ?
========== ВОПРОС СНЯТ — РАЗОБРАЛСЯ!=======================
нужно было
$modx->setPlaceholder('page.contents', $contents);
$modx->setPlaceholder('page.text', $input);
заменить на
$modx->setPlaceholder('page.contents', $contents);
return $input;
Спасибо! )
Сниппетом пользуюсь давно и на нескольких сайтах. Спасибо автору!
Но, у меня возникла необходимость разместить код Адсенс между списком оглавления страницы и самим контентом. А как это сделать — не знаю (в PHP не очень...).
Может кто-нибудь подскажет, как правильно вставить код Адсенс в сниппет makeContents, чтобы рекламные блоки отображались между оглавлением и самой статьей.
Спасибо!
Но, у меня возникла необходимость разместить код Адсенс между списком оглавления страницы и самим контентом. А как это сделать — не знаю (в PHP не очень...).
Может кто-нибудь подскажет, как правильно вставить код Адсенс в сниппет makeContents, чтобы рекламные блоки отображались между оглавлением и самой статьей.
Спасибо!
У меня тегов
Как это поправить?
</ul>
появляется больше необходимого количества, на docs.modx.pro тоже самое.Как это поправить?
Вася спасибо) Буквально вчера писала такое на JS ) но на стороне сервера все равно лучше!
На здоровье!
Доброго дня, добавлю: если в заголовке есть кавычки, то либо он не попадёт в выборку, либо всё будет выглядеть криво. Чтобы этого избежать
заменяем
заменяем
$anchor = str_replace(' ', '-', $header);
на$arr = array(
' ' => '-',
'"' => '');
$anchor = strtr($header,$arr);
и ещё проблема, если есть одинаковые заголовки, то все анкоры будут для них одинаковыми, и соответственно, вести к первому из них. Как это решить не знаю. Нужна помощь. Возможно стоит как-то проставлять номер для каждого найденного сниппетом заголовка.
Хороший компонент, но после каждой очистки кэша на странице появляется notice:
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
Как это убрать?
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?
Т.е. при переходе по ссылкам в сформированном оглавлении будет ли происходить переход на виртуальную страницу сгенерированную PageBreaker?
Т.е. при переходе по ссылкам в сформированном оглавлении будет ли происходить переход на виртуальную страницу сгенерированную PageBreaker?
Ответ: только по ссылкам в пределах первой сгенерированной страницы. Ссылки на остальные страницы не формируются и соответственно перейти на них по ссылкам нельзя. А жаль. Буду что-то додумывать(
Решил воспользоваться наработкой Василия и прилепил jQuery функцию на плавный скролл до блоков. Оказалось не все так просто, пришлось немного доработать сниппет.
1. Для начала, после замены пробелов на тире, вырезаем все сомнительные символы, которые попадают в якорь:
2. Затем, в следующей строке добавляем дата атрибут, т.к. с атрибутом href, jQuery в данном случае отказывается работать:
3. Ну и добавляем якорь в id:
4. На закуску, готовая функция jQuery.
Вроде все описал. На моем сайте вроде все работает, проблем не встретил.
За помощь с решением проблемы с jQuery, спасибо @Евгений Webinmd
1. Для начала, после замены пробелов на тире, вырезаем все сомнительные символы, которые попадают в якорь:
$anchor = preg_replace( '/[^ \w-]/' , '' , $anchor);
Ставить нужно перед формированием оглавления.2. Затем, в следующей строке добавляем дата атрибут, т.к. с атрибутом href, jQuery в данном случае отказывается работать:
$header = "<a href=\"{$base}#{$anchor}\" data-id=\"#{$anchor}\">{$header}</a>";
3. Ну и добавляем якорь в id:
$to[$i] = '<a name="' . $anchor . '" id="' . $anchor . '"></a>' . $headers[0][$i];
4. На закуску, готовая функция jQuery.
$(document).ready(function () {
$(".page-contents a").on("click", function(event) {
event.preventDefault();
var anchor = $(this).data("id"), // Ищем якорь
anchorPX = $(anchor).offset().top; // Определяем положение якоря
$('html, body').animate({scrollTop: anchorPX}, 600);
});
});
Вроде все описал. На моем сайте вроде все работает, проблем не встретил.
За помощь с решением проблемы с jQuery, спасибо @Евгений Webinmd
Вот второй вариант регулярки, та что выше вырезает кириллицу.
$anchor = preg_replace( '/[^a-zа-яё \-]/iu' , '' , $anchor);
Привет.
Все прекрасно работает есть в Заголовке нет классов или ID.
Вопрос: как заставить игнорировать Class и ID, например:
Все прекрасно работает есть в Заголовке нет классов или ID.
Вопрос: как заставить игнорировать Class и ID, например:
<h2 id="my-heading" class="header">Заголовок H2</h2>
Это сообщение было удалено
Добрый день! Несколько часов не могу разобраться с проблемой задвоения содержимого на странице. Перепробовали варианты, которые тут описаны, ни один не подходит. Василий, подскажите, пожалуйста из-за чего может быть задвоение и как это убрать?
Что то не получается заставить работать сниппет
Вызываю на странице [[*content:makeContents]] — выводится просто текст статьи
Вызываю на странице [[*content:makeContents]] — выводится просто текст статьи
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.