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))
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With