Brian Bruman
Brian Bruman

Reputation: 913

Append HTML Strings to DOMDocument at start and end of DOMXPath

Here is my block of html

$html = '<div class="importantnoticediv">
            <p class="important-notice-title important-notice-title-1">
                NOTICE:
            </p>
            <p class="important-notice-title important-notice-title-2">
                PROFESSIONAL INSTALLATION IS RECOMMENDED!
            </p>
        </div>';

I'm able to initiate a new DOMDocument and target this specific DIV class using DOMXpath

$dom = new DOMDocument;
$dom->loadHTML($post_content);
$xpath = new DOMXpath($dom);
$importantnotice = $xpath->query('//div[contains(@class, "importantnoticediv")]');
$indiv = $importantnotice->item(0);

I'm having a very hard time figuring out how to append the HTML I want to this class, though.

Here's what I'm trying to transform my block of html into:

<div class="importantnoticediv">
    <div class="note-icon note-icon-important">
        <span><i class="fa fa-exclamation" aria-hidden="true"></i>
        </span>
    </div>
    <div class="note-text note-text-important">
        <p class="important-notice-title important-notice-title-1">
                NOTICE:
        </p>
        <p class="important-notice-title important-notice-title-2">
                PROFESSIONAL INSTALLATION IS RECOMMENDED!
        </p>
    </div>
</div>

So, in other words,

<div class="note-icon note-icon-important">
    <span><i class="fa fa-exclamation" aria-hidden="true"></i>
    </span>
</div>
<div class="note-text note-text-important">

Immediately after my <div class="importantnoticediv"> as well as add a closing </div> at the very end of this div class.

Is this even possible with DOMDocument and DOMXpath? Given that what I am trying to add isn't "valid" html (even though it is, once I do the two steps..) I understand I can create new elements, but how do I insert just a custom html string immediately after the start and end of my chosen DOMDocument/DOMXpath?

Upvotes: 3

Views: 2384

Answers (1)

Decent Dabbler
Decent Dabbler

Reputation: 22783

Here's one way to do it. It makes use of DOMDocumentFragment. Admittedly, it's all a bit convoluted.

Furthermore, one problem with DOMDocumentFragment is that, besides appendXML(), it does not have a counterpart method appendHTML(), which means you can only append HTML to it if it validates as XML.

$html = '<div class="importantnoticediv">
            <p class="important-notice-title important-notice-title-1">
                NOTICE:
            </p>
            <p class="important-notice-title important-notice-title-2">
                PROFESSIONAL INSTALLATION IS RECOMMENDED!
            </p>
        </div>';

$dom = new DOMDocument;
// format our output to make it a tad bit easier on the eyes (doesn't help all that much, though)
$dom->formatOutput = true;
// this implicitly adds <html> and <body> tags
$dom->loadHTML( $html );

$xpath = new DOMXpath( $dom );

// notice I appended /p to your original query, so that we get the list of <p> elements inside the "importantnoticediv"
$nodes = $xpath->query('//div[contains(@class, "importantnoticediv")]/p');
// the first <p>
$p1 = $nodes->item( 0 );
// the second <p>
$p2 = $nodes->item( 1 );

// we create a DOMDocumentFragment that is associated with our DOMDocument
$frag = $dom->createDocumentFragment();

// I append your first HTML fragment (must be valid XML)
$frag->appendXML( '<div class="note-icon note-icon-important">
    <span><i class="fa fa-exclamation" aria-hidden="true"></i>
    </span>
</div>' );
// ... and insert it before our first <p> (parentNode is the "importantnoticediv")
$p1->parentNode->insertBefore( $frag, $p1 );

// frag is now empty again, because we just inserted it(s contents) into the DOMDocument

// we create our second fragment
$frag->appendXML( '<div class="note-text note-text-important"></div>' );

// this time we append it to "importantnoticediv" (i.e. as last child)
// and get the reference to this newly inserted <div>
$insertedNode = $p1->parentNode->appendChild( $frag );

// now we can append (i.e. move) the <p> elements to the newly inserted <div>
$insertedNode->appendChild( $p1 );
$insertedNode->appendChild( $p2 );

// the HTML should now be as desired
echo $dom->saveHTML();

Upvotes: 5

Related Questions