我希望在 php 中创建一个非常简单、非常基本的嵌套目录,它可以获取所有 h1-6 并适当地缩进。这意味着如果我有类似的东西:
<h1>content</h1>
<h2>more content</h2>
我应该得到:
content
more content.
我知道创建缩进的是css,这很好,但是如何创建一个目录,其中包含指向页面内容的工作链接?
显然很难掌握我的要求......
我要求一个读取 html 文档并提取所有 h1-6 并制作目录的函数。
我希望在 php 中创建一个非常简单、非常基本的嵌套目录,它可以获取所有 h1-6 并适当地缩进。这意味着如果我有类似的东西:
<h1>content</h1>
<h2>more content</h2>
我应该得到:
content
more content.
我知道创建缩进的是css,这很好,但是如何创建一个目录,其中包含指向页面内容的工作链接?
显然很难掌握我的要求......
我要求一个读取 html 文档并提取所有 h1-6 并制作目录的函数。
为此,您只需在 HTML 代码中搜索标签。
我写了两个函数(PHP 5.4.x)。
第一个返回一个数组,其中包含目录的数据。数据只是它自己的标题、标签的 id(如果你想使用锚点)和内容的子表。
function get_headlines($html, $depth = 1)
{
if($depth > 7)
return [];
$headlines = explode('<h' . $depth, $html);
unset($headlines[0]); // contains only text before the first headline
if(count($headlines) == 0)
return [];
$toc = []; // will contain the (sub-) toc
foreach($headlines as $headline)
{
list($hl_info, $temp) = explode('>', $headline, 2);
// $hl_info contains attributes of <hi ... > like the id.
list($hl_text, $sub_content) = explode('</h' . $depth . '>', $temp, 2);
// $hl contains the headline
// $sub_content contains maybe other <hi>-tags
$id = '';
if(strlen($hl_info) > 0 && ($id_tag_pos = stripos($hl_info,'id')) !== false)
{
$id_start_pos = stripos($hl_info, '"', $id_tag_pos);
$id_end_pos = stripos($hl_info, '"', $id_start_pos);
$id = substr($hl_info, $id_start_pos, $id_end_pos-$id_start_pos);
}
$toc[] = [ 'id' => $id,
'text' => $hl_text,
'sub_toc' => get_headlines($sub_content, $depth + 1)
];
}
return $toc;
}
第二个返回一个字符串,它用 HTML 格式化 toc。
function print_toc($toc, $link_to_htmlpage = '', $depth = 1)
{
if(count($toc) == 0)
return '';
$toc_str = '';
if($depth == 1)
$toc_str .= '<h1>Table of Content</h1>';
foreach($toc as $headline)
{
$toc_str .= '<p class="headline' . $depth . '">';
if($headline['id'] != '')
$toc_str .= '<a href="' . $link_to_htmlpage . '#' . $headline['id'] . '">';
$toc_str .= $headline['text'];
$toc_str .= ($headline['id'] != '') ? '</a>' : '';
$toc_str .= '</p>';
$toc_str .= print_toc($headline['sub_toc'], $link_to_htmlpage, $depth+1);
}
return $toc_str;
}
这两个功能都远非完美,但它们在我的测试中运行良好。随意改进它们。
注意:get_headlines
不是解析器,因此它不适用于损坏的 HTML 代码,只会崩溃。它也只适用于小写<hi>
标签。
我使用了这个包,它使用起来非常简单直接。
https://github.com/caseyamcl/toc
通过 Composer 安装,在您的 composer.json 文件中包含以下内容:
{
"require": {
"caseyamcl/toc": "^3.0",
}
}
或者,将 src 文件夹放入您的应用程序并使用 PSR-4 自动加载器来包含文件。
用法 这个包包含两个主要的类:
TOC\MarkupFixer:将 id 锚属性添加到还没有任何 H1...H6 标记(您可以指定在运行时使用哪些标头标记级别) TOC\TocGenerator:从 HTML 标记生成目录基本示例:
$myHtmlContent = <<<END
<h1>This is a header tag with no anchor id</h1>
<p>Lorum ipsum doler sit amet</p>
<h2 id='foo'>This is a header tag with an anchor id</h2>
<p>Stuff here</p>
<h3 id='bar'>This is a header tag with an anchor id</h3>
END;
$markupFixer = new TOC\MarkupFixer();
$tocGenerator = new TOC\TocGenerator();
// This ensures that all header tags have `id` attributes so they can be used as anchor links
$htmlOut = "<div class='content'>" . $markupFixer->fix($myHtmlContent) . "</div>";
//This generates the Table of Contents in HTML
$htmlOut .= "<div class='toc'>" . $tocGenerator->getHtmlMenu($myHtmlContent) . "</div>";
echo $htmlOut;
这会产生以下输出:
<div class='content'>
<h1 id="this-is-a-header-tag-with-no-anchor-id">This is a header tag with no anchor id</h1>
<p>Lorum ipsum doler sit amet</p>
<h2 id="foo">This is a header tag with an anchor id</h2>
<p>Stuff here</p>
<h3 id="bar">This is a header tag with an anchor id</h3>
</div>
<div class='toc'>
<ul>
<li class="first last">
<span></span>
<ul class="menu_level_1">
<li class="first last">
<a href="#foo">This is a header tag with an anchor id</a>
<ul class="menu_level_2">
<li class="first last">
<a href="#bar">This is a header tag with an anchor id</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
此函数仅针对 h2 标签返回带有附加目录的字符串。100% 测试代码。
函数目录($str){ $html = preg_replace('/]+\>/i', '$0在本文中
', $str, 1); // 内容中第一张图片之后的目录 $doc = 新的 DOMDocument(); $doc->loadHTML($html); // 创建文档片段 $frag = $doc->createDocumentFragment(); // 创建初始列表 $frag->appendChild($doc->createElement('ul')); $head = &$frag->firstChild; $xpath = 新 DOMXPath($doc); $最后 = 1; // 获取所有 H1, H2, ..., H6 元素 $tagChek = 数组(); foreach ($xpath->query('//*[self::h2]') as $headline) { // 获取当前标题的级别 sscanf($headline->tagName, 'h%u', $curr); array_push($tagChek,$headline->tagName); // 必要时移动头部参考 if ($curr parentNode->parentNode; } } elseif ($curr > $last && $head->lastChild) { // 向下移动并创建新列表 for ($i=$last; $ilastChild->appendChild($doc->createElement('ul')); $head = &$head->lastChild->lastChild; } } $last = $curr; // 添加列表项 $li = $doc->createElement('li'); $head->appendChild($li); $a = $doc->createElement('a', $headline->textContent); $head->lastChild->appendChild($a); // 构建标识 $levels = 数组(); $tmp = &$头; // 向上遍历子树到该子树的分片根节点 而 (!is_null($tmp) && $tmp != $frag) { $levels[] = $tmp->childNodes->length; $tmp = &$tmp->parentNode->parentNode; } $id = 'sect'.implode('.', array_reverse($levels)); // 设置目的地 $a->setAttribute('href', '#'.$id); // 给标题添加锚点 $a = $doc->createElement('a'); $a->setAttribute('name', $id); $a->setAttribute('id', $id); $headline->insertBefore($a, $headline->firstChild); } // 回声 $frag; // 将片段附加到文档 if(!empty($tagChek)): $doc->getElementsByTagName('section')->item(0)->appendChild($frag); 返回 $doc->saveHTML(); 别的: 返回 $str; 万一; }