In a nutshell, this is what I'm trying to do:
Get all <img> tags from a document
Set a data-src attribute (for lazy loading)
Empty their sources (for lazy loading)
Inject a <noscript> tag after this image
1-3 are fine. I just can't get the created <noscript> tag to be beside the image correctly.
I'm trying with insertBefore but I'm open for suggestions:
// Create a DOMDocument instance
$dom = new DOMDocument;
$dom->formatOutput = true;
$dom->preserveWhiteSpace = false;
// Loads our content as HTML
$dom->loadHTML($content);
// Get all of our img tags
$images = $dom->getElementsByTagName('img');
// How many of them
$len = count($images);
// Loop through all the images in this content
for ($i = 0; $i < $len; $i++) {
// Reference this current image
$image = $images->item($i);
// Create our fallback image before changing this node
$fallback_image = $image->cloneNode();
// Add the src as a data-src attribute instead
$image->setAttribute('data-src', $src);
// Empty the src of this img
$image->setAttribute('src', '');
// Now prepare our <noscript> markup
// E.g <noscript><img src="foobar.jpg" /></noscript>
$noscript = $dom->createElement("noscript");
$noscript->appendChild( $fallback_image );
$image->parentNode->insertBefore( $noscript, $image );
}
return $dom->saveHTML();
Having two images in the page, this is the result (abbreviated for clarity's sake):
Before:
<div>
<img />
<p />
</div>
<p>
<img />
</p>
After:
<div>
<img /> <!-- this should be the fallback wrapped in <noscript> that is missing -->
<p>
<img />
</p>
</div>
<p>
<img /> <!-- nothing happened here -->
</p>
Using $dom->appendChild works but the <noscript> tag should be beside the image and not at the end of the document.
My PHP skills are very rusty so I'd appreciate any clarification or suggestions.
UPDATE
Just realised saveHTML() was also adding <DOCTYPE><html><body> tags, so I've added a preg_replace (until I find a better solution) to take care of removing that.
Also, the output I have pasted before was based on the inspector of Chrome's Developer Tools.
I checked the viewsoure to see what was really going on (and thus found out about the tag).
This is what's really happening:
https://eval.in/114620
<div>
<img /> </noscript> <!-- wha? just a closing noscript tag -->
<p />
</div>
<p>
<img /> <!-- nothing happened here -->
</p>
SOLVED
So this is how I fixed it:
https://eval.in/117959
I think it's a good idea to work with new nodes after they have being inserted into the DOM:
$noscript = $dom->createElement("noscript");
$noscriptnode = $image->parentNode->insertBefore( $noscript, $image );
// Only now work with noscript by adding it's contents etc...
Also when it's inserted with "insertBefore" - it's a good idea to save it's reference.
$noscriptnode = $image->parentNode->insertBefore( $noscript, $image );
And another thing: I wasrunning this code within Wordpress. Some hooks were being run afterwards which was messing up my markup.
Related
I have a string containing different types of html tags and stuff, including some <img> elements. I am trying to wrap those <img> elements inside a <figure> tag. So far so good using a preg_replace like this:
preg_replace( '/(<img.*?>)/s','<figure>$1</figure>',$content);
However, if the <img>tag has a neighboring <figcaption> tag, the result is rather ugly, and produces a stray end tag for the figure-element:
<figure id="attachment_9615">
<img class="size-full" src="http://www.example.com/pic.png" alt="name" width="1699" height="354" />
<figcaption class="caption-text"></figure>Caption title here</figcaption>
</figure>
I've tried a whole bunch of preg_replace regex variations to wrap both the img-tag and figcaption-tag inside figure, but can't seem to make it work.
My latest try:
preg_replace( '/(<img.*?>)(<figcaption .*>*.<\/figcaption>)?/s',
'<figure">$1$2</figure>',
$content);
As others pointed out, better use a parser, i.e. DOMDocument instead. The following code wraps a <figure> tag around each img where the next sibling is a <figcaption>:
<?php
$html = <<<EOF
<html>
<img class="size-full" src="http://www.example.com/pic.png" alt="name" width="1699" height="354" />
<figcaption class="caption-text">Caption title here</figcaption>
<img class="size-full" src="http://www.example.com/pic.png" alt="name" width="1699" height="354" />
<img class="size-full" src="http://www.example.com/pic.png" alt="name" width="1699" height="354" />
<figcaption class="caption-text">Caption title here</figcaption>
</html>
EOF;
$dom = new DOMdocument();
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
# get all images
$imgs = $xpath->query("//img");
foreach ($imgs as $img) {
if ($img->nextSibling->tagName == 'figcaption') {
# create a new figure tag and append the cloned elements
$figure = $dom->createElement('figure');
$figure->appendChild($img->cloneNode(true));
$figure->appendChild($img->nextSibling->cloneNode(true));
# insert the newly generated elements right before $img
$img->parentNode->insertBefore($figure, $img);
# and remove both the figcaption and the image from the DOM
$img->nextSibling->parentNode->removeChild($img->nextSibling);
$img->parentNode->removeChild($img);
}
}
$dom->formatOutput=true;
echo $dom->saveHTML();
See a demo on ideone.com.
To have a <figure> tag around all your images, you might want to add an else branch:
} else {
$figure = $dom->createElement('figure');
$figure->appendChild($img->cloneNode(true));
$img->parentNode->insertBefore($figure, $img);
$img->parentNode->removeChild($img);
}
i want replace all images on my html but the code replace one and escaping one and so on
i use DOMDocument to replace images on my content and i use the next code the problem is the code escaping image
for example
1 2 3 4 images the code replace one and three and escaping tow and four and so on
$dom = new \DOMDocument();
$dom->loadHTML("data"));
$dom->preserveWhiteSpace = true;
$count = 1;
$images = $dom->getElementsByTagName('img');
foreach ($images as $img) {
$src = $img->getAttribute('src');
$newsrc = $dom->createElement("newimg");
$newsrc->nodeValue = $src;
$newsrc->setAttribute("id","qw".$count);
$img->parentNode->replaceChild($newsrc, $img);
$count++;
}
$html = $dom->saveHTML();
return $html;
the html code is
<p><img class="img-responsive" src="http://www.jarofquotes.com/img/quotes/86444b28aa86d706e33246b823045270.jpg" alt="" width="600" height="455" /></p>
<p> </p>
<p>some text</p>
<p> </p>
<p><img class="img-responsive" src="http://40.media.tumblr.com/c0bc20fd255cc18dca150640a25e13ef/tumblr_nammr75ACv1taqt2oo1_500.jpg" alt="" width="480" height="477" /></p>
<p> </p>
<p><span class="marker"><img class="img-responsive" src="http://wiselygreen.com/wp-content/uploads/green-living-coach-icon.png" alt="" width="250" height="250" /><br /><br /></span></p>
i want output html replace all images with
<newimg>Src </newimg>
Ok, I couldn't find a dupe suitable for PHP, so I am answering this one.
The issue you are facing is that NodeLists returned by getElementsByTagName() are live list. That means, when you do the call to replaceChild(), you are altering the NodeList you are currently iterating.
Let's assume we have this HTML:
$html = <<< HTML
<html>
<body>
<img src="1.jpg"/>
<img src="2.jpg"/>
<img src="3.jpg"/>
</body>
</html>
HTML;
Now let's load it into a DOMDocument and get the img elements:
$dom = new DOMDocument;
$dom->loadHTML($html);
$allImages = $dom->getElementsByTagName('img');
echo $allImages->length, PHP_EOL;
This will print 3 because there is 3 img elements in the DOM right now.
Let's replace the first img element with a p element:
$allImages->item(0)->parentNode->replaceChild(
$dom->createElement("p"),
$allImages->item(0)
);
echo $allImages->length, PHP_EOL;
This now gives 2 because there is now only 2 img elements left, essentially
item 0: img will be removed from the list
item 1: img will become item 0
item 2: img will become item 1
You are using foreach, so you are first replacing item 0, then move on to item 1, but item 1 is now item 2 and the item 0 is item 1 you would expect next. But because the list is live, you are skipping it.
To get around this, use a while loop and always replace the first element:
while ($allImages->length > 0) {
$allImages->item(0)->parentNode->replaceChild(
$dom->createElement("p"),
$allImages->item(0)
);
}
This will then catch all the img elements.
Hi I am trying to wrap images containing a specific class (pinthis is this example) in a span to which I will add info for schema. This is a basic example and I will need to inject other schema info also. To get me started though can anyone help me get from my existing code to my example output. I need to update multiple pages dynamically and some of the content will come via PHP from Wordpress taxonomies and other data so would prefer to do it in PHP if possible.
<p>
<a class="fancybox" rel="gallery1" href="image.jpg">
<img src="img.jpg" alt="alt text" width="1000" height="1000" class="various classes including ... pinthis">
</a>
</p>
Which I would like to become...
<p>
<a class="fancybox" rel="gallery1" href="image.jpg">
<span itemscope itemtype="http://schema.org/ImageObject">
<img src="img.jpg" alt="alt text" width="1000" height="1000" class="various classes including ... pinthis">
</span>
</a>
</p>
I think if someone could point me in the right direction and give me a push start that would give me enough to carry on from there
Many thanks.
Using PHP DOMDocument, you could do something like this:
$html = '<p><a class="fancybox" rel="gallery1" href="image.jpg"><img src="img.jpg" alt="alt text" width="1000" height="1000" class="various classes pinthis"></a></p>';
// Create a DOMDocument and load the HTML.
$dom = new DOMDocument();
$dom->loadHTML($html);
// Create the span wrapper.
$span = $dom->createElement('span');
$span->setAttribute('itemscope', '');
$span->setAttribute('itemtype', 'http://schema.org/ImageObject');
// Get all the images.
$images = $dom->getElementsByTagName('img');
// Loop the images.
foreach ($images as $image) {
// Only affect those with the pinthis class.
if (strpos($image->getAttribute('class'), 'pinthis') !== false) {
// Clone the span if we need to use it often.
$span_clone = $span->cloneNode();
// Replace the image tag with the span tag.
$image->parentNode->replaceChild($span_clone, $image);
// Add the image tag as a child of the new span tag.
$span_clone->appendChild($image);
}
}
// Get your HTML with saveHTML()
$html = $dom->saveHTML();
echo $html;
Just modify the code to suit your specific needs. For example, if you need to change your span tag attributes, if you are changing your class for searching, etc... You might even want to make a function where you can specify your class and span attributes.
Documentation to DOMDocument: http://php.net/manual/en/class.domdocument.php
use warpAll
check if the image has required class
if image has class, then wrap it with the desired <span></span>
Try it this way :
if ($('img.classes').hasClass('pinthis')){
$('img.classes').wrapAll('<span itemscope itemtype="http://schema.org/ImageObject">></span>');
}
Fiddle Demo
helpful thread : jquery, wrap elements inside a div
Basically what I'm trying to achieve is replacing the content of the src-attributes of a bunch of img-nodes by the content of the corresponding data-src-nodes in a page like the following one.
<html>
<body>
<div id="a">
<img src="" data-src="myValue" />
<img src="" data-src="myValue2" />
</div>
<img src="" data-src="myValue" />
</body>
</html>
I want to do this by finding a common base node (in this case the img nodes in the div with id a) and based on that node
the node containing the value to copy and#
the node retrieving the value
Script
<?PHP
$html = '<html><body><div id="a"><img src="" data-src="myValue"/><img src="" data-src="myValue2"/></div><img src="" data-src="myValue"/></body></html>';
$doc = new DOMDocument();
#$doc->loadHTML($html);
$basenode = false;
$xpath = new DOMXPath($doc);
$entries = $xpath->query('(//div[#id="a"])');
if ($entries->length > 0) $basenode = $entries->item(0);
if ($basenode) {
$img = $xpath->query('//img', $basenode);
foreach ($img as $curImg) {
$from = $xpath->query('//#data-src', $curImg);
$to = $xpath->query('//#src', $curImg);
$to->item(0)->value = $from->item(0)->value;
}
echo $doc->saveXML();
}
?>
Expected output
<html>
<body>
<div id="a">
<img src="myValue" data-src="myValue" />
<img src="myValue2" data-src="myValue2" />
</div>
<img src="" data-src="myValue" />
</body>
</html>
Actual output
<html>
<body>
<div id="a">
<img src="myValue" data-src="myValue" />
<img src="" data-src="myValue2" />
</div>
<img src="" data-src="myValue" />
</body>
</html>
So, the line
$from = $xpath->query('//#data-src', $curImg);
seems to actually base its search on the root node and not the img-node selected before. How can I solve this?
(I know that a possible workaround would be to omit selecting the img-nodes explicitly and doing something like from='//div[#id="a"]/img/#data-src' and to='//div[#id="a"]/img/#src' but I'm a bit concerned, that I might end up copying values between attributes of different nodes)
/ at the beginning specifies an absolute location path (i.e, from the document root). Instead, you want to use a relative one (relative to the context node).
For example; .//#data-src, or descendant::img/#data-src, and so on.
I need your assistence related php. In php, i want to allow html <img> tags only, i tried php's built-in function strip_tags() but it's not giving me the output i need. For instance, in the following code strip_tags() allows img tags but along with text.
$img = "<img src='/img/fawaz.jpg' alt= ''> <br /> <p> This is a detailed paragraph about Fawaz and his mates.</p>";
echo strip_tags($img , "<img>");
What would be the proper way to just allow <img> or any tag only from the function or variable.
Any help 'd be appreciated.
Thanks
This might be due to non closing img tag in your code. Try this
$img = "<img src='/img/fawaz.jpg' alt= '' /> <br /> <p> This is a detailed paragraph about Fawaz and his mates.</p>";
echo strip_tags($img , "<img>");
strip_tags() doesn't work that way you want it to behave. If supplied with a second argument, the tags listed are allowed to be part of the resulting string - except those which are not listed. And it will not filter out inner text.
If you want to extract <img/> elements only, don't even think about using a regex. Use a DOM parser for that:
libxml_use_internal_errors(true);
$doc=new DOMDocument;
$html=$doc->loadHTML('<img src="/img/fawaz.jpg" alt= ""> <br /> <p> This is a
detailed paragraph about Fawaz and his mates.</p>');
$path=new DOMXPath($doc);
foreach ($path->query('//img') as $found)
var_dump($doc->saveXML($found));
delete HTML Tags Without <img> and <a> and <br/> and <hr/> and ...
$img = "
<img src='/img/fawaz.jpg' alt= '' />
<br /><br/>
<hr/>
<p> This is a detailed paragraph about Fawaz and his mates.</p>
<a href='cft'>123</a>
";
$img = strip_tags($img , "<img>|<a>|<br>|<hr>");
echo $img;