Какой из ваших коварных программистов может показать мне элегантное php-кодированное решение для автоматического создания вложенной оглавления на основе тегов заголовков на странице?
Поэтому у меня есть html-документ:
<h1> Animals </h1> Some content goes here. Some content goes here. <h2> Mammals </h2> Some content goes here. Some content goes here. <h3> Terrestrial Mammals </h3> Some content goes here. Some content goes here. <h3> Marine Mammals </h3> Some content goes here. Some content goes here. <h4> Whales </h4> Some content goes here. Some content goes here.
Более конкретно, я хочу связанное оглавление в виде вложенного списка ссылок на заголовки на одной странице:
Содержание (автоматически генерируемое PHP-кодом)
Я не считаю его элегантным, но может помочь в получении общей идеи о том, как его создать;)
Он использует simple_html_dom для поиска и управления элементами в исходном html
$htmlcode = <<< EOHTML <h1> Animals </h1> Some content goes here. Some content goes here. <h2> Mammals </h2> Some content goes here. Some content goes here. <h3> Terrestrial Mammals </h3> Some content goes here. Some content goes here. <h3> Marine Mammals </h3> Some content goes here. Some content goes here. <h4> Whales </h4> Some content goes here. Some content goes here. EOHTML; // simpehtmldom or other dom manipulating library require_once 'simple_html_dom.php'; $html = str_get_html($htmlcode); $toc = ''; $last_level = 0; foreach($html->find('h1,h2,h3,h4,h5,h6') as $h){ $innerTEXT = trim($h->innertext); $id = str_replace(' ','_',$innerTEXT); $h->id= $id; // add id attribute so we can jump to this element $level = intval($h->tag[1]); if($level > $last_level) $toc .= "<ol>"; else{ $toc .= str_repeat('</li></ol>', $last_level - $level); $toc .= '</li>'; } $toc .= "<li><a href='#{$id}'>{$innerTEXT}</a>"; $last_level = $level; } $toc .= str_repeat('</li></ol>', $last_level); $html_with_toc = $toc . "<hr>" . $html->save();
Вот пример использования DOMDocument :
$doc = new DOMDocument(); $doc->loadHTML($code); // create document fragment $frag = $doc->createDocumentFragment(); // create initial list $frag->appendChild($doc->createElement('ol')); $head = &$frag->firstChild; $xpath = new DOMXPath($doc); $last = 1; // get all H1, H2, …, H6 elements foreach ($xpath->query('//*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6]') as $headline) { // get level of current headline sscanf($headline->tagName, 'h%u', $curr); // move head reference if necessary if ($curr < $last) { // move upwards for ($i=$curr; $i<$last; $i++) { $head = &$head->parentNode->parentNode; } } else if ($curr > $last && $head->lastChild) { // move downwards and create new lists for ($i=$last; $i<$curr; $i++) { $head->lastChild->appendChild($doc->createElement('ol')); $head = &$head->lastChild->lastChild; } } $last = $curr; // add list item $li = $doc->createElement('li'); $head->appendChild($li); $a = $doc->createElement('a', $headline->textContent); $head->lastChild->appendChild($a); // build ID $levels = array(); $tmp = &$head; // walk subtree up to fragment root node of this subtree while (!is_null($tmp) && $tmp != $frag) { $levels[] = $tmp->childNodes->length; $tmp = &$tmp->parentNode->parentNode; } $id = 'sect'.implode('.', array_reverse($levels)); // set destination $a->setAttribute('href', '#'.$id); // add anchor to headline $a = $doc->createElement('a'); $a->setAttribute('name', $id); $a->setAttribute('id', $id); $headline->insertBefore($a, $headline->firstChild); } // append fragment to document $doc->getElementsByTagName('body')->item(0)->appendChild($frag); // echo markup echo $doc->saveHTML();
Я нашел этот метод, Алекс Фриман ( http://www.10stripe.com/articles/automatically-generate-table-of-contents-php.php ):
preg_match_all('#<h[4-6]*[^>]*>.*?<\/h[4-6]>#',$html_string,$resultats); //reformat the results to be more usable $toc = implode("\n",$resultats[0]); $toc = str_replace('<a name="','<a href="#',$toc); $toc = str_replace('</a>','',$toc); $toc = preg_replace('#<h([4-6])>#','<li class="toc$1">',$toc); $toc = preg_replace('#<\/h[4-6]>#','</a></li>',$toc); //plug the results into appropriate HTML tags $toc = '<div id="toc"> <p id="toc-header">Table des matières</p> <hr /> <ul> '.$toc.' </ul> </div><br /><br />'; return $toc;
В HTML заголовки должны быть написаны как:
<h2><a name="target"></a>Text</h2>