Automatically generate nested table of contents based on heading tags

前端 未结 4 1591
广开言路
广开言路 2020-12-10 09:16

Which one of you crafty programmers can show me an elegant php coded solution for automatically generating a nested table of contents based on heading tags on the page?

相关标签:
4条回答
  • 2020-12-10 09:26

    Here’s an example using 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();
    
    0 讨论(0)
  • 2020-12-10 09:36

    Have a look at the TOC class. It allows generating table of contents from nested headings. h1 tag can be followed by any lower level h tag. The class uses recursion to extract the headings from article text

    0 讨论(0)
  • 2020-12-10 09:39

    I found this method, by 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;
    

    In the HTML, the headers have to be written as:

    <h2><a name="target"></a>Text</h2>
    
    0 讨论(0)
  • 2020-12-10 09:49

    I don't find it elegant, but might help in getting general idea how to create one ;)

    It uses simple_html_dom to find and manipulate elements in original 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();
    
    0 讨论(0)
提交回复
热议问题