Search code examples
phpwordpressdomappendappendchild

PHP DOM append HTML in Wordpress filter


I created a Wordpress filter using the DOM API method that was suggested here https://wordpress.stackexchange.com/a/61036/103233 to change some attributes of my images:

function add_lazyload($content) {

    $content = mb_convert_encoding($content, 'HTML-ENTITIES', "UTF-8");
    $dom = new DOMDocument();
    @$dom->loadHTML($content);

    foreach ($dom->getElementsByTagName('img') as $node) {  
        $oldsrc = $node->getAttribute('src');
        $node->setAttribute('data-src', $oldsrc );
        $newsrc = get_template_directory_uri() . '/images/placeholder.gif';
        $node->setAttribute('src', $newsrc);
    }

    $newHtml = preg_replace('/^<!DOCTYPE.+?>/', '', str_replace( array('<html>', '</html>', '<body>', '</body>'), array('', '', '', ''), $dom->saveHTML()));
    return $newHtml;
}
add_filter('the_content', 'add_lazyload');
add_filter('post_thumbnail_html', 'add_lazyload');

How can I add a HTML element within the foreach loop?

I tried adding a new DOMDocument and displaying it within the loop:

$test = new DOMDocument();
$test->loadHTML('<a>Hello</a>');
$test->saveHTML();

But this first prints all new $test elements (one for each loop) and then my altered $newHTML is printed. I want to append the new $test element to each img of the foreach loop. I never used DOM before... any suggestions? appendChild? But how?


Solution

  • http://php.net/manual/en/domdocument.createelement.php is the way to go. So you'd do something like:

    foreach ($dom->getElementsByTagName('img') as $node) {  
        $oldsrc = $node->getAttribute('src');
        $node->setAttribute('data-src', $oldsrc );
        $newsrc = get_template_directory_uri() . '/images/placeholder.gif';
        $node->setAttribute('src', $newsrc);
    
        // Create new anchor
        $testNode = $dom->createElement('a', 'Hello');
        $node->appendChild($testNode); // Insert new node inside (bad idea for images though)
        $node->parentNode->appendChild($testNode); // Insert new node after image
        $node->parentNode->insertBefore($testNode, $node); // Insert new node before image
    }
    

    You need to be careful with appending text with createElement as HTML entities such as & ampersands need to be escaped.