3

How do I remove all the li elements except the first in PHP?

<div class="category">
   <ul class="products">
     <li>{nested child elements}</li>
     <li>{nested child elements}</li>
     <li>{nested child elements}</li>
   </ul>
</div>

The code above is generated by another script via a function.

The result should be like this:

<div class="category">
    <ul class="products">
        <li>{nested child elements}</li>
    </ul>
</div>

UPDATE: Sorry guys the "category" is a class not an ID.

In repay to Yoshi, ul.products has siblings but I didn't include them in my post. Would that affect the query?

This is how my code looks like with Yoshi's code added:

class Myclass {
    function prin_html() {
        $content = get_code();
        $dom = new DOMDocument;
        $dom->loadXml($content);

        $xpath = new DOMXPath($dom);
        foreach ($xpath->query('//li[position()>1]') as $liNode) {
            $liNode->parentNode->removeChild($liNode); 
        }

        echo $dom->saveXml($dom->documentElement);    
    }
}

It sill prints the non-filtered html code...

jilseego
  • 1,083
  • 5
  • 13
  • 21

2 Answers2

9

Try:

$dom = new DOMDocument;
$dom->loadHtml('<div id="category">
   <ul class="products">
     <li>{nested child elements}</li>
     <li>{nested child elements}</li>
     <li>{nested child elements}</li>
   </ul>
</div>');

$xpath = new DOMXPath($dom);
foreach ($xpath->query('//li[position()>1]') as $liNode) {
    $liNode->parentNode->removeChild($liNode);
}

$output = '';
foreach ($xpath->query('//body/*') as $child) {
    $output .= $dom->saveXml($child);
}

Output:

<div id="category">
   <ul class="products">
     <li>{nested child elements}</li>


   </ul>
</div>
Yoshi
  • 54,081
  • 14
  • 89
  • 103
  • @jilseego It works in the example I've given. If it doesn't work for you, then you'll probably have malformed html to start with. Please provide more detailed information / examples of your data. – Yoshi Jun 22 '11 at 10:26
  • @Yhoshi: ul has siblings, would that affect the query? – jilseego Jun 22 '11 at 10:42
  • @jilseego No. The xpath query `//li` just simply selects every li-node in the given document. With `[position()>1]` this is reduced to only those which are not the first child under their parent. Coud you update your question to include the html, for which this code doesn't work? – Yoshi Jun 22 '11 at 10:48
  • @Yoshi: I have updated my question. I don't have the code that generates the html code. But that's pretty much what it returns when I examined it using print_r. – jilseego Jun 22 '11 at 10:54
  • @jilseego Try the updated code. I'll just take a guess and say that `$content` does not contain well-formed html (only one root node for example). The changed code uses `loadHtml` to accommodate for that. – Yoshi Jun 22 '11 at 11:00
  • @Yoshi: Still didn't work. If you don't mind you can take a look at my site: http://goo.gl/nKMEU . At the bottom where it says featured products, under ul.xoxo, that's the code get_code() generates. – jilseego Jun 22 '11 at 11:09
  • @jilseego I copied the code from the site you've given (hopefully the correct chunk), and used that as input for the above code (ignoring the many `htmlParseEntityRef` warnings for now), and it stills produces the correct result (removing every li except the first under it's parent). Can you please define: *Still didn't work* – Yoshi Jun 22 '11 at 11:27
  • @Yoshi: Thanks, you've done enough. Your code works when tested with dummy html as shown in your example. There must be something with my implementation. – jilseego Jun 22 '11 at 11:35
2

You could use DOMDocument.

$dom = new DOMDocument;

$dom->loadHTML($html);

$ul = $dom->getElementById('category')->getElementsByTagName('ul')->item(0);

foreach($ul->getElementsByTagName('li') as $index => $li) {
   if ($index == 0) {
      continue;
   }
   $ul->removeChild($li);
}
alex
  • 479,566
  • 201
  • 878
  • 984