1

I've got a bunch of HTML containing divs with a certain class (among other divs). What I'm trying to do is wrap the contents of that div in another div.

This HTML:

<h1>Hello</h1>

<p>Lorem ipsum dolor</p>

<div class="section">
    <h2>Helllllooo</h2>

    <div class="left">Left</div>
    <div class="right">Right</div>
</div>

<p>Lorem ipsum dolor</p>

<div class="section">
    <h2>Bubye</h2>

    <div class="right">Right</div>
    <div class="left">Left</div>
</div>

Should look like this when done:

<h1>Hello</h1>

<p>Lorem ipsum dolor</p>

<div class="section"><div class="inner">
    <h2>Helllllooo</h2>

    <div class="left">Left</div>
    <div class="right">Right</div>
</div></div>

<p>Lorem ipsum dolor</p>

<div class="section"><div class="inner">
    <h2>Bubye</h2>

    <div class="right">Right</div>
    <div class="left">Left</div>
</div></div>

Ideally I'd like something like jQuery's wrapInner; $('div.section').wrapInner('<div class="inner"></div>');

Here's what I have right now (which is not working :P):

<?php
$doc = new DOMDocument();
$doc->loadHTML('<?xml encoding="utf-8" ?>' . $theContent); # XML Encoding trick to prevent bug (http://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly?rq=1)
$divs = $doc->getElementsByTagName('div');

foreach ($divs as $div) {
    # Make sure we're on a div.section
    if (strpos($div->getAttribute('class'), 'section') != -1) {
        # Create wrapping div
        $inner = $doc->createElement('div');
        $inner->setAttribute('class', 'inner');

        # Move all children to new div
        while ($div->childNodes->length) {
            $inner->appendChild($div->childNodes->item(0));
        }

        # This doesn't work (item() is not a function)
    #   $div->appendChild($inner->item(0));

        # This freezes the page
    #   $div->appendChild($inner);

        # This looks perfect, all the elements from div.section inside a div.inner
        echo '<pre>' . htmlspecialchars($inner->ownerDocument->saveHTML($inner)) . '</pre>';
    }
}

# This looks as expected, a bunch of empty div.section elements
echo '<pre>' . htmlspecialchars($doc->saveHTML()) . '</pre>';
powerbuoy
  • 12,460
  • 7
  • 48
  • 78

1 Answers1

1

The probleme is that you're modifying the NodeList you're iterating over.

Try this :

<?php
$doc = new DOMDocument();
$doc->loadHTML('<?xml encoding="utf-8" ?>' . $theContent); # XML Encoding trick to prevent bug (http://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly?rq=1)
$divs = $doc->getElementsByTagName('div');
$section_div = array();

foreach ($divs as $div) {
    # Make sure we're on a div.section
    if (strpos($div->getAttribute('class'), 'section') !== FALSE) {
        $section_div[] = $div;
    }
}
foreach ($section_div as $div) {
    # Create wrapping div
    $inner = $doc->createElement('div');
    $inner->setAttribute('class', 'inner');

    # Move all children to new div
    while ($div->childNodes->length) {
        $inner->appendChild($div->childNodes->item(0));
    }

    # Append div.inner to div.section
    $div->appendChild($inner);
}

# This looks as expected, a bunch of empty div.section elements
echo '<pre>' . htmlspecialchars($doc->saveHTML()) . '</pre>';

Note: strpos returns FALSE if the string is not found.

Rémi Svahn
  • 123
  • 5
  • Thanks for the `strpos()` correction as well, did they change that recently or something? Was pretty sure it was -1 (I know it's not, now, though) – powerbuoy Dec 12 '13 at 17:48
  • 1
    @powerbuoy I'm not aware of such a change. Maybe you were mistaken with some other language (`indexOf` in JavaScript does return -1). – Rémi Svahn Dec 13 '13 at 09:09