哪位狡猾的程序员可以向我展示一个优雅的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代码自动生成)
答案 0 :(得分:7)
我觉得它不优雅,但可能有助于大致了解如何创建一个;)
它使用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();
答案 1 :(得分:6)
以下是使用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();
答案 2 :(得分:4)
我发现了这个方法,由Alex Freeman(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>
答案 3 :(得分:0)
看看TOC class。它允许从嵌套标题生成目录。 h1标签之后可以是任何较低级别的h标签。该类使用递归从文章文本中提取标题
答案 4 :(得分:0)
使用 SimpleHTMLDom 的简短解决方案:
public function getSummary($body)
{
$dom = new Htmldom($body);
$summ = "<ul>";
$prev = 2;
foreach($dom->find("h2,h3,h4") as $x => $htag)
{
$curr = intval(substr($htag->tag, -1));
$prev <= $curr ?: $summ .= "</ul>";
$prev >= $curr ?: $summ .= "<ul>";
$summ .= "<li>$htag->plaintext</li>";
$prev = $curr;
}
$summ .= "</ul>";
return $summ;
}