CMTV CMTV - 1 month ago 7
HTML Question

Making a list of H2-H3 elements in php

I have a PHP variable with HTML code of my page which cosists of only paragraph and H2-H3 elements:

<h2>Header 1</h2>

<p>Some text</p>
...
<p>Some text</p>

<h3>Sub-header 1</h3>

<p>Some text</p>
...
<p>Some text</p>

<h2>Header 2</h2>


I am trying to create a function which takes a variable with HTML code and return a string with HTML ul list:

<ul>
<li>Header 1
<ul>
<li>Sub-header 1</li>
</ul>
</li>
<li>Header 2
<ul>
<li>Sub-header 2</li>
<li>Sub-header 3</li>
</ul>
</li>
<li>Header 3</li>
</ul>


Here what I have achieved but I stuck with detecting when to add sub-ul tag and how to do so:

function generate_navigation($HTML) {
$DOM = new DOMDocument();
$DOM->loadHTML($HTML);

$navigation = '<ul>';

// Iterating through all elements
$h2Iterator = 0;
foreach($DOM->getElementsByTagName('*') as $element) {
if($element->tagName == 'h2') {
$h2Iterator++;
$navigation .= '<li>' . $element->textContent . '</li>';
} else if ($element->tagName == 'h3') {
// How to add?
}
}

return $navigation.'</ul>';
}

Answer

You need to keep track of opened h2 tags. No need for the iterator counter, but for an iterator status.

function generate_navigation($HTML) {
    $DOM = new DOMDocument();
    $DOM->loadHTML($HTML);

    $navigation = '<ul>';

    // Iterating through all elements
    $h2IteratorStatus = 0; //0-closed, 1-opened
    foreach($DOM->getElementsByTagName('*') as $element) {
        if($element->tagName == 'h2') {
            if($h2IteratorStatus){
                //it's open, need to close
                $navigation .= '</li>';
                $h2IteratorStatus = 0;
            }
            else $h2IteratorStatus = 1;
            $navigation .= '<li>' . $element->textContent ;
        } else if ($element->tagName == 'h3') {
            $navigation .= '<ul><li>' . $element->textContent .'</li></ul>' ;
        }
    }
    //check for last opened h2
    if($h2IteratorStatus){
        //it's open, need to close
        $navigation .= '</li>';
    }

    return $navigation.'</ul>';
}

Here is a fiddle but the fiddle environment works for me only in Chrome.