4

Как быстро сделать содержание (оглавление) статьи на блоге.

Здравствуйте, уважаемые читатели. Сегодня поговорим о том как сделать содержание статьи на своем блоге.

Что такое содержание (оглавление) статьи и для чего оно используется?

Содержание или как его еще называют оглавление, не что иное, как структурированный список разделов статьи, который дает ясное понимание о чем статья и где, что искать. Вы спросите, а зачем это надо? Просто читай статью от начала до конца.

Я тоже задавался этим вопросом, когда начал посещать блоги и изучать интернет технологии. Часто бывают такие ситуации, когда поисковая система выдает статью не совсем соответствующую запросу. Вот в этом случае, достаточно первого взгляда на содержание, чтобы понять о чем статья и стоит ли ее читать.

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

Поэтому в статьях объемом более 5000-6000 знаков, желательно делать разбивку по разделам и устанавливать блок с содержанием. Ну что, убедил? Это такая же нужная опция, как и “хлебные крошки”,  постраничная навигация и другие. Совместно, все эти фишки, улучшают юзабилити сайта и способствуют продвижению в поисковой выдаче.

Простой способ создания содержания в статье.

Это простой способ создания оглавления. Для этого вам потребуется следующий код.

<strong>Из этой статьи вы узнаете:</strong>
 
<a href="#a1"><li>Здесь первый пункт содержания</a><br>
<a href="#a2"><li>Здесь второй пункт содержания</a><br>
<a href="#a3"><li>Здесь третий пункт содержания</a><br>

 

<a2 id="a1">Сюда будет перебрасывать первая ссылка</a2>
<a2 id="a2">Сюда будет перебрасывать вторая ссылка</a2>
<a2 id="a3">Сюда будет перебрасывать третья ссылка</a2>

Давайте разберемся что здесь и зачем. Посмотрите на первый пункт содержания к этой статье (Что такое содержание (оглавление) статьи и для чего оно используется?) Чтобы сделать его ссылкой на соответствующий раздел текста необходимо, в текстовом редакторе блога, переключиться в режим «Текст». Для этого перейдите по  по соответствующей вкладке.

как-сделать-содержание-статьи-1

Заключаем оглавление раздела статьи  “Что такое содержание (оглавление) статьи и для чего оно используется?” в теги ссылки. Должно получится следующее.

<a href="#1"><li>Что такое содержание (оглавление)статьи и для чего оно нужно?</a><br>

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

<a2 id="1">Участок текста статьи для перехода по ссылке</a2>

Все готово, вам остается только опубликовать статью нажав кнопку «Опубликовать».

Внимание; Не забудьте создание ссылки и места перехода (анкора) и дальнейшая публикация статьи обязательно должны  производиться в режиме «Текст» визуального редактора сайта. Иначе у вас ничего не получится.

Таким образом вы можете создать сколько вам угодно пунктов содержания статьи и сделать их ссылками на определенные участки текста. Способ очень простой и при определенной сноровке, не требует больших усилий и временных затрат.

Вы думаете я на этом успокоился? Конечно нет! Хотелось максимально автоматизировать процесс, чтобы содержание формировалось автоматически, было привязано к оглавлениям абзацев (Н1-Н6) и  при этом, была возможность быстро и легко разместить содержание в нужном мне месте поста. Вот такое “По щучьему велению, по моему хотению…!”

Решение было найдено на отличном сайте wp-kama.ru, автора Тимура Камаева.  Где предложено оригинальное решение данной задачи.

 

Автоматическое создание содержания статьи.

Для начала копируйте нижеприведенный код.

/** * Содержание (оглавление) для больших постов. * * Author: Kama * Page: http://wp-kama.ru/?p=1513 * ver: 3.9 * * Changelog: http://wp-kama.ru/?p=1513#obnovleniya */ class Kama_Contents { // defaults options public $opt = array( // Отступ слева у подразделов в px. 'margin' => 40, // Теги по умолчанию по котором будет строиться оглавление. Порядок имеет значение. // Кроме тегов, можно указать атрибут classа: array('h2','.class_name'). Можно указать строкой: 'h2 h3 .class_name' 'selectors' => array('h2','h3','h4'), // Ссылка на возврат к оглавлению. '' - убрать ссылку 'to_menu' => 'к содержанию ↑', // Заголовок. '' - убрать заголовок 'title' => 'Содержание:', // Css стили. '' - убрать стили 'css' => '.kc__gotop{ display:block; text-align:right; } .kc__title{ font-style:italic; padding:1em 0; } .kc__anchlink{ color:#ddd!important; position:absolute; margin-left:-1em; }', // Минимальное количество найденных тегов, чтобы оглавление выводилось. 'min_found' => 2, // Минимальная длина (символов) текста, чтобы оглавление выводилось. 'min_length' => 2000, // Ссылка на страницу для которой собирается оглавление. Если оглавление выводиться на другой странице... 'page_url' => '', // Название шоткода 'shortcode' => 'contents', // Оставлять символы в анкорах 'spec' => '\'.+$*~=', // Какой тип анкора использовать: 'a' - <a name="anchor"></a> или 'id' - 'anchor_type' => 'id', // Включить микроразметку? 'markup' => false, // Добавить 'знак' перед подзаголовком статьи со ссылкой на текущий анкор заголовка. Укажите '#', '&' или что вам нравится :) 'anchor_link' => '', // минимальное количество символов между заголовками содержания, для которых нужно выводить ссылку "к содержанию". // Не имеет смысла, если параметр 'to_menu' отключен. С целью производительности, кириллица считается без учета кодировки. // Поэтому 800 символов кириллицы - это примерно 1600 символов в этом параметре. 800 - расчет для сайтов на кириллице... 'tomenu_simcount' => 800, ); public $contents; // collect html contents private $temp; static $inst; function __construct( $args = array() ){ $this->set_opt( $args ); return $this; } /** * Create instance * @param array [$args = array()] Options * @return object Instance */ static function init( $args = array() ){ is_null( self::$inst ) && self::$inst = new self( $args ); if( $args ) self::$inst->set_opt( $args ); return self::$inst; } function set_opt( $args = array() ){ $this->opt = (object) array_merge( (array) $this->opt, (array) $args ); } /** * Обрабатывает текст, превращает шоткод в нем в оглавление. * @param (string) $content текст, в котором есть шоткод. * @param (string) $contents_cb callback функция, которая обработает список оглавления. * @return Обработанный текст с оглавлением, если в нем есть шоткод. */ function shortcode( $content, $contents_cb = '' ){ if( false === strpos( $content, '['. $this->opt->shortcode ) ) return $content; // get contents data if( ! preg_match('~^(.*)\['. $this->opt->shortcode .'([^\]]*)\](.*)$~s', $content, $m ) ) return $content; $contents = $this->make_contents( $m[3], $m[2] ); if( $contents && $contents_cb && is_callable($contents_cb) ) $contents = $contents_cb( $contents ); return $m[1] . $contents . $m[3]; } /** * Заменяет заголовки в переданном тексте (по ссылке), создает и возвращает оглавление. * @param (string) $content текст на основе которого нужно создать оглавление. * @param (array/string) $tags массив тегов, которые искать в переданном тексте. * Можно указать: имена тегов "h2 h3" или классы элементов ".foo .foo2". * Если в теги добавить маркер "embed" то вернется только тег <ul> * без заголовка и оборачивающего блока. Нужно для использования внутри текста, как список. * @return html код оглавления. */ function make_contents( & $content, $tags = '' ){ // return if text is too short if( mb_strlen( strip_tags($content) ) < $this->opt->min_length ) return; $this->temp = $this->opt; $this->contents = array(); if( ! $tags ) $tags = $this->opt->selectors; if( is_string($tags) ) $tags = array_map('trim', preg_split('/[ ,]+/', $tags ) ); $tags = array_filter($tags); // del empty // check tags foreach( $tags as $k => $tag ){ // remove special marker tags and set $args if( in_array( $tag, array('embed','no_to_menu') ) ){ if( $tag == 'embed' ) $this->temp->embed = true; if( $tag == 'no_to_menu' ) $this->opt->to_menu = false; unset( $tags[ $k ] ); continue; } // remove tag if it's not exists in content $patt = ( ($tag[0] == '.') ? 'class=[\'"][^\'"]*'. substr($tag, 1) : "<$tag" ); if( ! preg_match("/$patt/i", $content ) ){ unset( $tags[ $k ] ); continue; } } if( ! $tags ) return; // set patterns from given $tags // separate classes & tags & set $class_patt = $tag_patt = $level_tags = array(); foreach( $tags as $tag ){ // class if( $tag{0} == '.' ){ $tag = substr( $tag, 1 ); $link = & $class_patt; } // html tag else $link = & $tag_patt; $link[] = $tag; $level_tags[] = $tag; } $this->temp->level_tags = array_flip( $level_tags ); // replace all titles & collect contents to $this->contents $patt_in = array(); if( $tag_patt ) $patt_in[] = '(?:<('. implode('|', $tag_patt) .')([^>]*)>(.*?)<\/\1>)'; if( $class_patt ) $patt_in[] = '(?:<([^ >]+) ([^>]*class=["\'][^>]*('. implode('|', $class_patt) .')[^>]*["\'][^>]*)>(.*?)<\/'. ($patt_in?'\4':'\1') .'>)'; $patt_in = implode('|', $patt_in ); $this->temp->content = $content; // collect and replace $_content = preg_replace_callback("/$patt_in/is", array( &$this, '__make_contents_callback'), $content, -1, $count ); if( ! $count || $count < $this->opt->min_found ){ unset($this->temp); // clear cache return; } $this->temp->content = $content = $_content; // $_content was for check reasone // html static $css; $embed = isset($this->temp->embed); $_tit = & $this->opt->title; $_is_tit = ! $embed && $_tit; // markup $ItemList = $this->opt->markup ? ' itemscope itemtype="http://schema.org/ItemList"' : ''; $contents = ( $_is_tit ? '<div class="kc__wrap"'. $ItemList .' >' : '' ) . ( ( ! $css && $this->opt->css ) ? '<style>'. preg_replace('/[\n\t ]+/', ' ', $this->opt->css ) .'</style>' : '' ) . ( $_is_tit ? '<span style="display:block;" class="kc-title kc__title" id="kcmenu"'. ($ItemList?' itemprop="name"':'') .'>'. $_tit .'</span>'. "\n" : '' ) . '<ul class="contents"'. ( (! $_tit || $embed) ? ' id="kcmenu"' : '' ) . ( ($ItemList && ! $_is_tit ) ? $ItemList : '' ) .'>'. "\n". implode('', $this->contents ) . '</ul>'."\n" . ( $_is_tit ? '</div>' : '' ); unset($this->temp); // clear cache return $this->contents = $contents; } ## callback function to replace and collect contents private function __make_contents_callback( $match ){ $temp = & $this->temp; // it's only class selector in pattern if( count($match) == 5 ){ $tag = $match[1]; $attrs = $match[2]; $title = $match[4]; $level_tag = $match[3]; // class_name } // it's found tag selector elseif( count($match) == 4 ){ $tag = $match[1]; $attrs = $match[2]; $title = $match[3]; $level_tag = $tag; } // it's found class selector else{ $tag = $match[4]; $attrs = $match[5]; $title = $match[7]; $level_tag = $match[6]; // class_name } $anchor = $this->__sanitaze_anchor( $title ); $opt = $this->opt; // make live easier $level = @ $temp->level_tags[ $level_tag ]; if( $level > 0 ) $sub = ( $opt->margin ? ' style="margin-left:'. ($level*$opt->margin) .'px;"' : '') . ' class="sub sub_'. $level .'"'; else $sub = ' class="top"'; // collect contents // markup $_is_mark = $opt->markup; $temp->counter = empty($temp->counter) ? 1 : $temp->counter+1; $this->contents[] = "\t". '<li'. $sub . ($_is_mark?' itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"':'') .'> <a rel="nofollow"'. ($_is_mark?' itemprop="item"':'') .' href="'. $opt->page_url .'#'. $anchor .'"> '.( $_is_mark ? '<span itemprop="name">'. $title .'</span>' : $title ).' </a> '.( $_is_mark ? ' <meta itemprop="position" content="'. $temp->counter .'" />':'' ).' </li>'. "\n"; $anchlink = $opt->anchor_link ? '<a rel="nofollow" class="kc__anchlink" href="#'. $anchor .'">'. $opt->anchor_link .'</a> ' : ''; if( $anchlink ) $title = $anchlink . $title; $new_el = "\n<$tag id=\"$anchor\" $attrs>$anchlink$title</$tag>"; if( $opt->anchor_type == 'a' ) $new_el = '<a class="kc__anchor" name="'. $anchor .'"></a>'."\n<$tag $attrs>$title</$tag>"; $to_menu = ''; if( $opt->to_menu ){ // go to contents $to_menu = '<a rel="nofollow" class="kc-gotop kc__gotop" href="'. $opt->page_url .'#kcmenu">'. $opt->to_menu .'</a>'; // remove '$to_menu' if simbols beatween $to_menu too small (< 300) $pos = strpos( $temp->content, $match[0] ); // mb_strpos( $temp->content, $match[0] ) - в 150 раз медленнее! if( empty($temp->elpos) ){ $prevpos = 0; $temp->elpos = array( $pos ); } else { $prevpos = end($temp->elpos); $temp->elpos[] = $pos; } $simbols_count = $pos - $prevpos; if( $simbols_count < $opt->tomenu_simcount ) $to_menu = ''; } return $to_menu . $new_el; } ## URL transliteration function __sanitaze_anchor( $str ){ $str = strip_tags( $str ); $iso9 = array( 'А'=>'A', 'Б'=>'B', 'В'=>'V', 'Г'=>'G', 'Д'=>'D', 'Е'=>'E', 'Ё'=>'YO', 'Ж'=>'ZH', 'З'=>'Z', 'И'=>'I', 'Й'=>'J', 'К'=>'K', 'Л'=>'L', 'М'=>'M', 'Н'=>'N', 'О'=>'O', 'П'=>'P', 'Р'=>'R', 'С'=>'S', 'Т'=>'T', 'У'=>'U', 'Ф'=>'F', 'Х'=>'H', 'Ц'=>'TS', 'Ч'=>'CH', 'Ш'=>'SH', 'Щ'=>'SHH', 'Ъ'=>'', 'Ы'=>'Y', 'Ь'=>'', 'Э'=>'E', 'Ю'=>'YU', 'Я'=>'YA', // small 'а'=>'a', 'б'=>'b', 'в'=>'v', 'г'=>'g', 'д'=>'d', 'е'=>'e', 'ё'=>'yo', 'ж'=>'zh', 'з'=>'z', 'и'=>'i', 'й'=>'j', 'к'=>'k', 'л'=>'l', 'м'=>'m', 'н'=>'n', 'о'=>'o', 'п'=>'p', 'р'=>'r', 'с'=>'s', 'т'=>'t', 'у'=>'u', 'ф'=>'f', 'х'=>'h', 'ц'=>'ts', 'ч'=>'ch', 'ш'=>'sh', 'щ'=>'shh', 'ъ'=>'', 'ы'=>'y', 'ь'=>'', 'э'=>'e', 'ю'=>'yu', 'я'=>'ya', // other 'Ѓ'=>'G', 'Ґ'=>'G', 'Є'=>'YE', 'Ѕ'=>'Z', 'Ј'=>'J', 'І'=>'I', 'Ї'=>'YI', 'Ќ'=>'K', 'Љ'=>'L', 'Њ'=>'N', 'Ў'=>'U', 'Џ'=>'DH', 'ѓ'=>'g', 'ґ'=>'g', 'є'=>'ye', 'ѕ'=>'z', 'ј'=>'j', 'і'=>'i', 'ї'=>'yi', 'ќ'=>'k', 'љ'=>'l', 'њ'=>'n', 'ў'=>'u', 'џ'=>'dh' ); $str = strtr( $str, $iso9 ); $spec = preg_quote( $this->opt->spec ); $str = preg_replace("/[^a-zA-Z0-9_$spec\-]+/", '-', $str ); // все ненужное на '-' $str = trim( $str, '-'); return strtolower( $str ); } ## cut the shortcode from the content function strip_shortcode( $text ){ return preg_replace('~\['. $this->opt->shortcode .'[^\]]*\]~', '', $text ); } } /** * 3.9 - при 'anchor_type=a' не работал параметр 'anchor_link' * 3.8 - баг синтаксиса при заполнении свойства $this->contents в PHP 7.1 * 3.7 - добавил элемент position при маркировке schema.org * 3.6.1 - тег заголовка "Содержание" изменил с DIV на SPAN * 3.6 - исправление парсинга тегов - удаление пустых при разбиении по [ ,] * 3.5 - стабильность. в параметр selectors можно указывать строку с элементами через запятую. * 3.4 - параметр 'tomenu_simcount' * 3.3 - smart 'to contents' link show - not show next link if symbols between prev smaller than 500 */ ## Обработка шоткода [contents] в тексте add_filter('the_content', 'kama_contents_shortcode'); function kama_contents_shortcode( $content ){ $args = array(); //$args['shortcode'] = 'list'; // [list] вместо [contents] if( is_singular() ){ //$args['margin'] = 30; //$args['page_url'] = get_permalink(); $args['to_menu'] = 'к оглавлению ↑'; $args['title'] = 'Оглавление:'; return Kama_Contents::init( $args )->shortcode( $content ); } // вырежем шорткод else return Kama_Contents::init( $args )->strip_shortcode( $content ); }

 

/**
 * Содержание (оглавление) для больших постов.
 *
 * Author: Kama
 * Page: http://wp-kama.ru/?p=1513
 * ver: 3.9
 *
 * Changelog: http://wp-kama.ru/?p=1513#obnovleniya
 */
class Kama_Contents {

	// defaults options
	public $opt = array(
		// Отступ слева у подразделов в px.
		'margin'     => 40,
		// Теги по умолчанию по котором будет строиться оглавление. Порядок имеет значение.
		// Кроме тегов, можно указать атрибут classа: array('h2','.class_name'). Можно указать строкой: 'h2 h3 .class_name'
		'selectors'  => array('h2','h3','h4'),
		// Ссылка на возврат к оглавлению. '' - убрать ссылку
		'to_menu'    => 'к содержанию ↑',
		// Заголовок. '' - убрать заголовок
		'title'      => 'Содержание:',
		// Css стили. '' - убрать стили
		'css'        => '.kc__gotop{ display:block; text-align:right; }
						 .kc__title{ font-style:italic; padding:1em 0; }
						 .kc__anchlink{ color:#ddd!important; position:absolute; margin-left:-1em; }',
		// Минимальное количество найденных тегов, чтобы оглавление выводилось.
		'min_found'  => 2,
		// Минимальная длина (символов) текста, чтобы оглавление выводилось.
		'min_length' => 2000,
		// Ссылка на страницу для которой собирается оглавление. Если оглавление выводиться на другой странице...
		'page_url'   => '',
		// Название шоткода
		'shortcode'  => 'contents',
		// Оставлять символы в анкорах
		'spec'       => '\'.+$*~=',
		// Какой тип анкора использовать: 'a' - <a name="anchor"></a> или 'id' -
		'anchor_type' => 'id',
		// Включить микроразметку?
		'markup'      => false,
		// Добавить 'знак' перед подзаголовком статьи со ссылкой на текущий анкор заголовка. Укажите '#', '&' или что вам нравится :)
		'anchor_link' => '',
		// минимальное количество символов между заголовками содержания, для которых нужно выводить ссылку "к содержанию".
		// Не имеет смысла, если параметр 'to_menu' отключен. С целью производительности, кириллица считается без учета кодировки.
		// Поэтому 800 символов кириллицы - это примерно 1600 символов в этом параметре. 800 - расчет для сайтов на кириллице...
		'tomenu_simcount' => 800,
	);

	public $contents; // collect html contents

	private $temp;

	static $inst;

	function __construct( $args = array() ){
		$this->set_opt( $args );
		return $this;
	}

	/**
	 * Create instance
	 * @param  array [$args = array()] Options
	 * @return object Instance
	 */
	static function init( $args = array() ){
		is_null( self::$inst ) && self::$inst = new self( $args );
		if( $args ) self::$inst->set_opt( $args );
		return self::$inst;
	}

	function set_opt( $args = array() ){
		$this->opt = (object) array_merge( (array) $this->opt, (array) $args );
	}

	/**
	 * Обрабатывает текст, превращает шоткод в нем в оглавление.
	 * @param (string) $content текст, в котором есть шоткод.
	 * @param (string) $contents_cb callback функция, которая обработает список оглавления.
	 * @return Обработанный текст с оглавлением, если в нем есть шоткод.
	 */
	function shortcode( $content, $contents_cb = '' ){
		if( false === strpos( $content, '['. $this->opt->shortcode ) )
			return $content;

		// get contents data
		if( ! preg_match('~^(.*)\['. $this->opt->shortcode .'([^\]]*)\](.*)$~s', $content, $m ) )
			return $content;

		$contents = $this->make_contents( $m[3], $m[2] );

		if( $contents && $contents_cb && is_callable($contents_cb) )
			$contents = $contents_cb( $contents );

		return $m[1] . $contents . $m[3];
	}

	/**
	 * Заменяет заголовки в переданном тексте (по ссылке), создает и возвращает оглавление.
	 * @param (string)        $content текст на основе которого нужно создать оглавление.
	 * @param (array/string)  $tags    массив тегов, которые искать в переданном тексте.
	 *                                 Можно указать: имена тегов "h2 h3" или классы элементов ".foo .foo2".
	 *                                 Если в теги добавить маркер "embed" то вернется только тег <ul>
	 *                                 без заголовка и оборачивающего блока. Нужно для использования внутри текста, как список.
	 * @return                html код оглавления.
	 */
	function make_contents( & $content, $tags = '' ){
		// return if text is too short
		if( mb_strlen( strip_tags($content) ) < $this->opt->min_length )
			return;

		$this->temp     = $this->opt;
		$this->contents = array();

		if( ! $tags )
			$tags = $this->opt->selectors;

		if( is_string($tags) )
			$tags = array_map('trim', preg_split('/[ ,]+/', $tags ) );

		$tags = array_filter($tags); // del empty

		// check tags
		foreach( $tags as $k => $tag ){
			// remove special marker tags and set $args
			if( in_array( $tag, array('embed','no_to_menu') ) ){
				if( $tag == 'embed' ) $this->temp->embed = true;
				if( $tag == 'no_to_menu' ) $this->opt->to_menu = false;

				unset( $tags[ $k ] );
				continue;
			}

			// remove tag if it's not exists in content
			$patt = ( ($tag[0] == '.') ? 'class=[\'"][^\'"]*'. substr($tag, 1) : "<$tag" );
			if( ! preg_match("/$patt/i", $content ) ){
				unset( $tags[ $k ] );
				continue;
			}
		}

		if( ! $tags ) return;

		// set patterns from given $tags
		// separate classes & tags & set
		$class_patt = $tag_patt = $level_tags = array();
		foreach( $tags as $tag ){
			// class
			if( $tag{0} == '.' ){
				$tag  = substr( $tag, 1 );
				$link = & $class_patt;
			}
			// html tag
			else
				$link = & $tag_patt;

			$link[] = $tag;
			$level_tags[] = $tag;
		}

		$this->temp->level_tags = array_flip( $level_tags );

		// replace all titles & collect contents to $this->contents
		$patt_in = array();
		if( $tag_patt )   $patt_in[] = '(?:<('. implode('|', $tag_patt) .')([^>]*)>(.*?)<\/\1>)';
		if( $class_patt ) $patt_in[] = '(?:<([^ >]+) ([^>]*class=["\'][^>]*('. implode('|', $class_patt) .')[^>]*["\'][^>]*)>(.*?)<\/'. ($patt_in?'\4':'\1') .'>)';

		$patt_in = implode('|', $patt_in );

		$this->temp->content = $content;

		// collect and replace
		$_content = preg_replace_callback("/$patt_in/is", array( &$this, '__make_contents_callback'), $content, -1, $count );

		if( ! $count || $count < $this->opt->min_found ){
			unset($this->temp); // clear cache
			return;
		}

		$this->temp->content = $content = $_content; // $_content was for check reasone

		// html
		static $css;
		$embed   = isset($this->temp->embed);
		$_tit    = & $this->opt->title;
		$_is_tit = ! $embed && $_tit;

		// markup
		$ItemList = $this->opt->markup ? ' itemscope itemtype="http://schema.org/ItemList"' : '';

		$contents =
			( $_is_tit ? '<div class="kc__wrap"'. $ItemList .' >' : '' ) .
			( ( ! $css && $this->opt->css ) ? '<style>'. preg_replace('/[\n\t ]+/', ' ', $this->opt->css ) .'</style>' : '' ) .
			( $_is_tit ? '<span style="display:block;" class="kc-title kc__title" id="kcmenu"'. ($ItemList?' itemprop="name"':'') .'>'. $_tit .'</span>'. "\n" : '' ) .
				'<ul class="contents"'. ( (! $_tit || $embed) ? ' id="kcmenu"' : '' ) . ( ($ItemList && ! $_is_tit ) ? $ItemList : '' ) .'>'. "\n".
					implode('', $this->contents ) .
				'</ul>'."\n" .
			( $_is_tit ? '</div>' : '' );

		unset($this->temp); // clear cache

		return $this->contents = $contents;
	}

	## callback function to replace and collect contents
	private function __make_contents_callback( $match ){
		$temp = & $this->temp;

		// it's only class selector in pattern
		if( count($match) == 5 ){
			$tag   = $match[1];
			$attrs = $match[2];
			$title = $match[4];

			$level_tag = $match[3]; // class_name
		}
		// it's found tag selector
		elseif( count($match) == 4 ){
			$tag   = $match[1];
			$attrs = $match[2];
			$title = $match[3];

			$level_tag = $tag;
		}
		// it's found class selector
		else{
			$tag   = $match[4];
			$attrs = $match[5];
			$title = $match[7];

			$level_tag = $match[6]; // class_name
		}

		$anchor = $this->__sanitaze_anchor( $title );
		$opt = $this->opt; // make live easier

		$level = @ $temp->level_tags[ $level_tag ];
		if( $level > 0 )
			$sub = ( $opt->margin ? ' style="margin-left:'. ($level*$opt->margin) .'px;"' : '') . ' class="sub sub_'. $level .'"';
		else
			$sub = ' class="top"';

		// collect contents
		// markup
		$_is_mark = $opt->markup;

		$temp->counter = empty($temp->counter) ? 1 : $temp->counter+1;

		$this->contents[] = "\t".
			'<li'. $sub . ($_is_mark?' itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"':'') .'>
				<a rel="nofollow"'. ($_is_mark?' itemprop="item"':'') .' href="'. $opt->page_url .'#'. $anchor .'">
					'.( $_is_mark ? '<span itemprop="name">'. $title .'</span>' : $title ).'
				</a>
				'.( $_is_mark ? ' <meta itemprop="position" content="'. $temp->counter .'" />':'' ).'
			</li>'. "\n";

		$anchlink = $opt->anchor_link ? '<a rel="nofollow" class="kc__anchlink" href="#'. $anchor .'">'. $opt->anchor_link .'</a> ' : '';
		if( $anchlink ) $title = $anchlink . $title;

		$new_el = "\n<$tag id=\"$anchor\" $attrs>$anchlink$title</$tag>";
		if( $opt->anchor_type == 'a' )
			$new_el = '<a class="kc__anchor" name="'. $anchor .'"></a>'."\n<$tag $attrs>$title</$tag>";

		$to_menu = '';
		if( $opt->to_menu ){
			// go to contents
			$to_menu = '<a rel="nofollow" class="kc-gotop kc__gotop" href="'. $opt->page_url .'#kcmenu">'. $opt->to_menu .'</a>';

			// remove '$to_menu' if simbols beatween $to_menu too small (< 300)
			$pos = strpos( $temp->content, $match[0] ); // mb_strpos( $temp->content, $match[0] ) - в 150 раз медленнее!
			if( empty($temp->elpos) ){
				$prevpos = 0;
				$temp->elpos = array( $pos );
			}
			else {
				$prevpos = end($temp->elpos);
				$temp->elpos[] = $pos;
			}
			$simbols_count = $pos - $prevpos;
			if( $simbols_count < $opt->tomenu_simcount ) $to_menu = '';
		}

		return $to_menu . $new_el;
	}

	## URL transliteration
	function __sanitaze_anchor( $str ){
		$str = strip_tags( $str );

		$iso9 = array(
			'А'=>'A', 'Б'=>'B', 'В'=>'V', 'Г'=>'G', 'Д'=>'D', 'Е'=>'E', 'Ё'=>'YO', 'Ж'=>'ZH',
			'З'=>'Z', 'И'=>'I', 'Й'=>'J', 'К'=>'K', 'Л'=>'L', 'М'=>'M', 'Н'=>'N', 'О'=>'O',
			'П'=>'P', 'Р'=>'R', 'С'=>'S', 'Т'=>'T', 'У'=>'U', 'Ф'=>'F', 'Х'=>'H', 'Ц'=>'TS',
			'Ч'=>'CH', 'Ш'=>'SH', 'Щ'=>'SHH', 'Ъ'=>'', 'Ы'=>'Y', 'Ь'=>'', 'Э'=>'E', 'Ю'=>'YU', 'Я'=>'YA',
			// small
			'а'=>'a', 'б'=>'b', 'в'=>'v', 'г'=>'g', 'д'=>'d', 'е'=>'e', 'ё'=>'yo', 'ж'=>'zh',
			'з'=>'z', 'и'=>'i', 'й'=>'j', 'к'=>'k', 'л'=>'l', 'м'=>'m', 'н'=>'n', 'о'=>'o',
			'п'=>'p', 'р'=>'r', 'с'=>'s', 'т'=>'t', 'у'=>'u', 'ф'=>'f', 'х'=>'h', 'ц'=>'ts',
			'ч'=>'ch', 'ш'=>'sh', 'щ'=>'shh', 'ъ'=>'', 'ы'=>'y', 'ь'=>'', 'э'=>'e', 'ю'=>'yu', 'я'=>'ya',
			// other
			'Ѓ'=>'G', 'Ґ'=>'G', 'Є'=>'YE', 'Ѕ'=>'Z', 'Ј'=>'J', 'І'=>'I', 'Ї'=>'YI', 'Ќ'=>'K', 'Љ'=>'L', 'Њ'=>'N', 'Ў'=>'U', 'Џ'=>'DH',
			'ѓ'=>'g', 'ґ'=>'g', 'є'=>'ye', 'ѕ'=>'z', 'ј'=>'j', 'і'=>'i', 'ї'=>'yi', 'ќ'=>'k', 'љ'=>'l', 'њ'=>'n', 'ў'=>'u', 'џ'=>'dh'
		);

		$str = strtr( $str, $iso9 );

		$spec = preg_quote( $this->opt->spec );
		$str  = preg_replace("/[^a-zA-Z0-9_$spec\-]+/", '-', $str ); // все ненужное на '-'
		$str  = trim( $str, '-');

		return strtolower( $str );
	}

	## cut the shortcode from the content
	function strip_shortcode( $text ){
		return preg_replace('~\['. $this->opt->shortcode .'[^\]]*\]~', '', $text );
	}
}

/**
 * 3.9 - при 'anchor_type=a' не работал параметр 'anchor_link'
 * 3.8 - баг синтаксиса при заполнении свойства $this->contents в PHP 7.1
 * 3.7 - добавил элемент position при маркировке schema.org
 * 3.6.1 - тег заголовка "Содержание" изменил с DIV на SPAN
 * 3.6 - исправление парсинга тегов - удаление пустых при разбиении по [ ,]
 * 3.5 - стабильность. в параметр selectors можно указывать строку с элементами через запятую.
 * 3.4 - параметр 'tomenu_simcount'
 * 3.3 - smart 'to contents' link show - not show next link if symbols between prev smaller than 500
 */
## Обработка шоткода [contents] в тексте
add_filter('the_content', 'kama_contents_shortcode');
function kama_contents_shortcode( $content ){
	$args = array();

	//$args['shortcode'] = 'list'; // [list] вместо [contents]

	if( is_singular() ){
		//$args['margin'] = 30;
		//$args['page_url'] = get_permalink();
		$args['to_menu']  = 'к оглавлению ↑';
		$args['title']    = 'Оглавление:';

		return Kama_Contents::init( $args )->shortcode( $content );
	}
	// вырежем шорткод
	else
		return Kama_Contents::init( $args )->strip_shortcode( $content );

}

Теперь надо вставить данный код в файл function.php вашего шаблона сайта. Можете это сделать используя  FTP соединение или при помощи админ панели управления вашего сайта. Для этого заходите в панель, переходите по вкладкам «Консоль» => « Внешний вид» (1) => «Редактор» (2) => «function.php» (3).

Как-быстро-создать-оглавление-(содержание)-для-больших-постов-в-WordPress

В самом конце файла, вставляете скопированный код и не забываете нажать кнопку «Обновить файл».

Как-сделать-содержание-статьи-2

Далее копируете код стилей оформления.

.contents{ list-style-type:none; counter-reset:list; }
/* цвет чисел */
.contents li:before{ color:#555; }
/* уровень 0 */
.contents li.top{ counter-increment:list; counter-reset:list1; }
.contents li.top:before{ content:counter(list) '. '; }
/* уровень 1 */
.contents li.sub_1{ counter-increment:list1; counter-reset:list2; }
.contents li.sub_1:before{ content:counter(list) '.' counter(list1) '. '; }
/* уровень 2 */
.contents li.sub_2{ counter-increment:list2; }
.contents li.sub_2:before{ content:counter(list) '.' counter(list1) '.' counter(list2) '. '; }

Опять переходите в панель управления сайта «Консоль» => « Внешний вид» => «Редактор» => «style.css». В самом конце файла style.css вставляете скопированный код и не забываете нажать кнопку «Обновить файл».

Основная задача выполнена. Далее от вас требуется, при написании постов, грамотно разбивать их на абзацы и проставлять заголовки ( h1-h4). Как правильно использовать теги заголовков h1-h6, можно прочитать здесь.

Для вывода содержания статьи в нужном месте, перед публикацией необходимо, вставить шорткод.

[contents]

Содержание автоматически будет создано, причем в древовидном виде и с учетом иерархии заголовков. Дополнительно, в конце каждого абзаца, появится ссылка “перейти к содержанию”, что также создает дополнительное удобство для посетителя.

Оформление блока вы можете изменить в файле стилей. Я выделил содержание отдельным блоком с синим фоном. Так лучше привлекает внимание и подходит под общий стиль оформления. Вы можете самостоятельно поэксперементировать со стилями для создания индивидуального дизайна.

Кстати, в статье Тимура Камаева, есть дополнения к коду, которые позволяют настроить место отображения блока, кому интересно можете изучить.

Плагины для создания содержания (оглавления) статей.

Для тех кто боится лезть в код, можно воспользоваться специальными плагинами для создания содержания.

  • Table of Contents Plus— простой и удобный плагин, который создает кнопку в редакторе для создания блока содержания.
  • Simple TOC— гибкий плагин с широкими возможностями настроек и созданием кнопки в визуальном редакторе.

Основной недостаток данных плагинов, автоматическое создание оглавления в статьях, когда оно там не требуется. Я стараюсь не использовать в большом количестве плагины на своем сайте и решать вопросы при помощи кода. Как не крути, плагины тормозят скорость загрузки сайта, и не редко конфликтуют между собой и с кодом шаблона блога.

На этом пожалуй все. Улучшайте внешний вид своего ресурса, пишите отзывы и пожелания. Всем удачи!

комментария 4
  1. admin
    14.11.2017 в 18:02

    Пожалуйста, рад был помочь. Удачи!

  2. Елена
    13.11.2017 в 17:32

    Большое спасибо, установила себе на сайт, все работает отлично. Всего доброго.

  3. admin
    24.02.2017 в 16:15

    Андрей, пожалуйста. Применяйте. Удачи!

  4. Андрей
    24.02.2017 в 13:58

    Сергей ваша статья как под заказ! Я совсем недавно мучился с этим вопросом, перелопатив кучу информации я ничего путного кроме как решить проблему с помощью плагинов не нашел! Я не сторонник плагинов и поэтому ваш вариант считаю идеальным!Вариант Тимура Камаева меня пугает! Так что лучше сделаю сам ручками по вашему варианту, как по мне так надежней! Спасибо за очень интересное и простое решение проблемы! С уважением к вам и вашему блогу Андрей!

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.