3

I have the following HTML.

<div id="container">
    <div id="current">Current Div</div>
</div>

Using DomDocument in PHP, I am trying to add an extra div to the HTML before the div with the id of "current".

<div id="container">
    <div id="new">New Div</div>
    <div id="current">Current Div</div>
</div>

When I use the following code, it appears to add the div inside the div with the id of "current", but before the content of this div.Can anyone tell me why this is and how I can get the results like the HTML above? (See HTML Issue Below)

Current PHP

$doc = new DOMDocument();
$doc->formatOutput = true;

$doc->loadHTMLFile('index.html');

$head = $doc->getElementById('current');
$base = $doc->createElement('div', 'New Div');
$base->setAttribute('id', 'new');
echo $doc->saveHTML();

HTML Issue

<div id="container">
    <div id="current">
        <div id="new">New Div</div>
        Current Div
    </div>
</div>

EDIT:

PHP

$doc = new DOMDocument();
$doc->formatOutput = true;

$doc->loadHTMLFile('index.html');
$container = $doc->getElementById('container');
$current = $doc->getElementById('username');
$new = $doc->createElement('div', 'New Div');

$new->setAttribute('id', 'new');
$container->insertBefore($new, $current);
echo $doc->saveHTML();

HTML

<div id="container">
    <form method="post" action="">
        <input type="text" name="username" id="username" />
    </form>
</div>

Error:

Fatal error: Uncaught exception 'DOMException' with message 'Not Found Error' 
in index.php:55 Stack trace: #0 index.php(55): 
DOMNode->insertBefore(Object(DOMElement), Object(DOMElement))

1 Answer 1

3

You can use DOMNode::insertBefore():

<?php

$doc = new DOMDocument();
$doc->formatOutput = true;
$doc->loadHTMLFile('index.html');

$container = $doc->getElementById('container');
$current = $doc->getElementById('current');
$new = $doc->createElement('div', 'New Div');

$new->setAttribute('id', 'new');
$container->insertBefore($new, $current);

var_dump($doc->saveHTML());

This yields:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
    <body>
        <div id="container">
            <div id="new">New Div</div>
            <div id="current">Current Div</div>
        </div>
    </body>
</html>

Hope this helps :)

EDIT

Referring to your reported issue... I see the problem: input#username is not an immediate child of div#container, it's an immediate child of form. So you need to get the form element as a DOMNode object, not its parent div#container. Easiest way to do this is add an id to the form and do something like this:

$form = $doc->getElementById('form');
$username = $doc->getElementById('username');
$username->setAttribute('value', 'Enter username!');

var_dump($doc->saveHTML());

This is based on the the following HTML. Note the form element has an id:

<div id="container">
    <form id="form" method="post" action="">
        <input type="text" name="username" id="username">
    </form>
</div>

If for some reason you cannot add an id to the form element, you can traverse from div#container like so:

// find div#container element
$container = $doc->getElementBy('id');
// find all form elements that are children of div#container
$forms = $container->getElementsByTagName('form');

if (0 !== $forms->length) {
   $form = $forms->item(0);
}

// etc.

EDIT #2

I almost forgot XPath... If you want to get more succinct/adventurous you can use DOMXPath::query() to find DOM nodes:

$xpath = new DOMXPath($doc);
$form  = $xpath->query('body/div[@id="container"]/form')->item(0);
$input = $xpath->query('body//input[@id="username"]')->item(0);

The XPath query syntax is pretty cryptic but powerful, I cannot say I'm an expert by any means, so I cannot speak for the efficiency of these queries. Also you might want to add error checking - the query method returns a DOMNodeList which has a length property.

Finally it's worth noting that DOMDocument decorates your HTML fragment with a DOCTYPE and html and body tags; this is why the XPath query traverses from body.

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

6 Comments

How come the script breaks when referencing an id of a input textbox rather than the div with id "current"? - What's the difference?
Hi Richy. It breaks? Interesting... Are you getting a specific error? The following works for me: $input = $doc->createElement('input'); $input->setAttribute('type', 'text'); $input->setAttribute('id', 'foo'); $input->setAttribute('value', 'bar'); $container->insertBefore($input, $current);
I have updated my original post to include further details :)
you know... I think I misread your comment - does the input element already exist in index.html? If so, can you post the line of PHP that is causing the issue.
No worries..the html does already contain the input element. I have pasted the php, html and error in my original post. It doesn't appear to like the insertBefore method?
|

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.