1

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?

1
  • Do you want to append the $test node inside an img? Or do you want to add it after / before? Commented Sep 21, 2016 at 9:56

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

8 Comments

Also when you output your node, so you don't need to do replace all the gumpth around it just do: $doc->saveHTML($node); the node is the context of where it will start exporting your html.
We're an the right track here, thanks man! You're right, the element should be appended after the image. Problem: When I use your code, the <a>Hello</a>elements are all printed after all images are printed. But I'm trying to print every single image and the new element right after that. The final thing will be a <noscript> element after each image as a lazyload fallback...
The "insertBefore" works though... So i'll use that!
One last question: Let's say the element I want to append is the original image before it was altered... I tried to add something like $fallback = $node; at the beginning of the loop and than used this in the insertBefore at the end: $node->parentNode->insertBefore($fallback, $node); ... Didn't work since I have no idea what I'm doing here :/
You need to clone the node, use $fallback = $node->cloneNode(true); The true param means clone deep and not just that individual node. The PHP documentation for Dom is actually pretty decent... there are a few gotchas we've faced but they're mostly described on the comments.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.