Peamasii has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I have an easy question but I can't wrap my head around it. :/) Suppose in my HTML I want each <img> tag to be wrapped in an tag. The following code should work but it doesn't? error: Can't replace an item with its parent! at build_lightbox.pl line 11. How can I fix it, thx.
#!/usr/bin/perl -w use strict; use HTML::TreeBuilder; use HTML::Element; my $root = HTML::TreeBuilder->new_from_file("index.section.html"); my @imgs = $root->find_by_tag_name('img'); foreach my $img (@imgs) { my $new_parent = HTML::Element->new('a', 'href' => $img->attr('src'), +'rel' => 'lightbox'); $img->attr('class',"thumbnail"); $new_parent->push_content($img); $img->replace_with($new_parent); } print $root->as_HTML(); $root->delete; # erase this tree because we're done with it
The HTML is like:
<html> <body> <h2> Ajax Requests</h2> <img src="Daily/_Ajax_Requests.svg" alt="Daily Ajax Requests"> <img src="Hourly/_Ajax_Requests.svg" alt="Hourly Ajax Requests"> <img src="Group_by_Min/_Ajax_Requests.svg" alt="Group_by_Min Ajax Req +uests"> </body> </html>

Replies are listed 'Best First'.
Re: HTML Element replace
by smls (Friar) on May 17, 2014 at 10:07 UTC

    Swap the order of the push_content and replace_with lines, then it will work.

    Explanation:

    The error you're getting is because the push_content operation rips the img element out of the original document tree, and adds it to the new stand-alone a element instead.

    So by the time you try to call replace_with (during the first iteration of the foreach loop), your tree data looks like this:

    html <-- $root '-- body +-- h2 | <-- ($img used to be here but not anymore) +-- img '-- img a <-- $new_parent '-- img <-- $img

    In this situation, replacing the $img element with the $new_parent element will obviously not give you what you want (in fact the result would be logically undefined, hence the error message "Can't replace an item with its parent").

    On the other hand if you do the  $img->replace_with($new_parent) operation first, it will work just fine and result in this intermediate state:

    html <-- $root '-- body +-- h2 +-- a <-- $new_parent ($img used to be here but not anymore) +-- img '-- img img <-- $img

    The replaced img element will no longer be part of the document tree, but it continues to exist because we still hold a reference to it in the $img variable. So all that's left to do is to re-add it to the tree using  $new_parent->push_content($img):

    html <-- $root '-- body +-- h2 +-- a <-- $new_parent | '-- img <-- $img +-- img '-- img
      Great, I understand it now. thanks!
Re: HTML Element replace (error: Can't replace an item with its parent!)(cut/detach/clone)
by Anonymous Monk on May 17, 2014 at 10:07 UTC

    but it doesn't? error: Can't replace an item with its parent!

    Clone the image when you push it, then you can replace it

    $new_parent->push_content( $img->clone ); $img->replace_with($ new_parent );

    #!/usr/bin/perl -- use strict; use warnings; use HTML::TreeBuilder; use HTML::Element; my $root = HTML::TreeBuilder->new_from_content( q{ <p> <img src="1"> <p> <img src="2"> } ); my @imgs = $root->find_by_tag_name('img'); foreach my $img (@imgs) { my $new_parent = HTML::Element->new( 'a', 'href' => $img->attr('src'), 'rel' => 'lightbox' ); $img->attr( 'class', "thumbnail" ); $new_parent->push_content( $img->clone ); $img->replace_with($new_parent); } print $root->as_HTML; $root->delete; # erase this tree because we're done with it
      Thanks for your answer, this has helped me understand it!