DOM selected all <tr>, how to traverse the <td> - php

I have a table with trs that look like this.
<tr>
<td>Link</td>
<td>value 2</td>
<td>value 3</td>
<td>value 4</td>
<td>value 5</td>
</tr>
I'm using xPath to select all <tr> and looping through them.
foreach($xpathResult as $item){
//print the href of the first td
//print the nodeValue of the second td
//print the nodeValue of the 3rd td
//print the nodeValue of the 4th td
//print the nodeValue of the 5th td
}
How do I print the href of the first td and the nodeValue of each td thereafter? There's firstChild and lastChild, but how do I get to the middle s? I could use firstChild->nextSibling and firstChild->nextSibling->nextSibling but isn't there an easier way?

Just use a loop on childNodes, like so:
foreach( $trs as $tr) {
echo $tr->firstChild->firstChild->attributes->getNamedItem( 'href')->value;
foreach( $tr->childNodes as $k => $td) {
if( $k == 0) continue; // Skip the first one
echo $td->nodeValue . "\n";
}
}

Why not just select the td's directly?
$results = $dom->xpath('//tr/td');
foreach($results as $td) {
echo $td->nodeValue;
}

Related

PhP table assignment

I am working on an homework assignment, and I am having a hard time with it. Any help is appreciated!
For the Module 2 Exercise, students will write a PHP program named "moduletwo.php" that generates an HTML table with the following requirements:
A function called "makeTable($headerArray, $numRows)" that creates a table with headers as they appear in the array and however many rows are specified.
If a number of rows is not specified, use a default of 5
Even number rows should use the class "dark"
Odd number rows should use the class "light"
There should be a column that has only the row number
Create an array of table headers (these can be whatever you want, but be creative here, and do not just use "Heading 1" or generic term)
This function will be used in the body of the HTML document to create an empty table with the array of headers
The HTML document should be complete, including DOCTYPE and all necessary elements.
Write a CSS file named "moduletwo.css" to go along with this that creates the "dark" and "light" classes with background colors of your choosing, but that must be readable and reflect the "dark" and "light" names. Link to this in the head section of the page."
this is what i have so far:
<?php
function makeTable($headerarray, $numrows)
{
echo "<table>"; # echo the table begining
foreach ($headerarray as $header) { # loop through the erray to print the th tag
echo "<th>" . $header . "</th>";
}
foreach ($numRows as $row) { #loop $numrows times to create that number of empty rows
echo "<tr>";
echo "<td>";
echo "</td>";
echo "</tr>";
}
echo "</table>"; # echo table ending;
}
$headers = array(
"Book Title",
"Book Author",
"Publication Date",
"Book Pages",
"Finished Book"
);
makeTable($headers, 5); #create a table with 5 empty rows under the header
?>
I'm struggling with creating the rows.
First, I want to say that I do not recommend copying this exactly and using it for your homework. You should read the comments and attempt to understand why it does what it does. I've done my best to comment the code so you can learn it.
Newbie Friendly
//define default `$numrows` in function declaration
function makeTable($headerarray, $numrows = 5) {
//create table beginning
echo "<table>";
//create first row, which is your header
//this needs to be done OUTSIDE of the header loop, as there is only 1 set of headers.
echo "<tr>";
//loop through headers using FOREACH
foreach($headerarray as $header) {
//add <th> element for each header
echo "<th>" . $header . "</th>";
}
//end table row (header)
echo "</tr>";
//define ODD class. The first loop is ALWAYS ODD.
$class = 'light';
//get a count of columns
$numcolumns = count($headerarray);
//loop a specific number of times (number of rows), using a FOR loop
for ($row = 1; $row <= $numrows; $row++) {
//create table row inside FIRST FOR LOOP. This creates a row each time.
//also add `$class` to row.
echo "<tr class=\"" . $class . "\">";
//loop a specific number of times (number of columns). This creates a column each time.
for ($column = 1; $column <= $numcolumns; $column++) {
//create a <td> element (cell)
echo "<td>";
if($column == 1) { //if column is 1
//use row number as cell content
echo "Row " . $row;
} else { //if column IS NOT 1
//use column number as content
echo "Column " . $column; #show column number as content
}
//end <td> element (cell)
echo "</td>"; #end row
}
//end table row
echo "</tr>";
//AT THE END OF ROW LOOP, switch `$class` to 'dark' if it is currently 'light'
//or switch it to 'light' if it is currently 'dark'
//since this is at the end of the row loop, it takes effect on the next loop. That means it should alternate between the 2
/***
This solution has several benefits. First, you don't need to know if the current row is even or odd.
second, you could use this to check if the row is even/odd IF YOU NEED TO
(if `$class` is 'light', you know the row is ODD)
**/
if($class == 'light') {
$class = 'dark';
} else {
$class = 'light';
}
}
//end table
echo "</table>";
}
Which results in this HTML using your example data.
<table>
<tr>
<th>Book Title</th>
<th>Book Author</th>
<th>Publication Date</th>
<th>Book Pages</th>
<th>Finished Book</th>
</tr>
<tr class="light"><td>Row 1</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
<tr class="dark"><td>Row 2</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
<tr class="light"><td>Row 3</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
<tr class="dark"><td>Row 4</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
<tr class="light"><td>Row 5</td>
<td>Column 2</td>
<td>Column 3</td>
<td>Column 4</td>
<td>Column 5</td>
</tr>
</table>
The CSS part is pretty simple, you just need to define a dark and light rule.
tr.light {
background-color: LIGHT_COLOR;
}
tr.dark {
background-color: DARK_COLOR;
}
A more advanced approach
If I had to do this, I'd probably do something like this:
function makeTable($headerarray, $numrows = 5) {
$headers = "";
foreach($headerarray as $head) {
$headers .= "<th>{$head}</th>";
}
$body = "";
$numcolumns = count($headerarray);
for ($row = 1; $row <= $numrows; $row++) {
$tmp = "<tr>";
for ($col = 1; $col <= $numcolumns; $col++) {
$content = $col == 1 ? "Row {$row}" : "Column {$col}";
$tmp .= "<td>{$content}</td>";
}
$body .= $tmp . "</tr>";
}
$html = "
<table>
<thead>
<tr>
{$headers}
</tr>
</thead>
<tbody>
{$body}
</tbody>
</table>
";
return $html;
}
This function returns the table instead of directly echoing it. This allows you to do more stuff with the HTML at the very end if you need to. You would use this function like this:
$headers = array(
"Book Title",
"Book Author",
"Publication Date",
"Book Pages",
"Finished Book"
);
echo makeTable($headers, 5);
I also completely removed the classes from the rows, because it's easier to do with CSS alone, which will react better to rows being removed by javascript or whatever. It can be applied like this:
tbody > tr:nth-child(even) {
background-color: DARK_COLOR;
}
tbody > tr:nth-child(odd) {
background-color: LIGHT_COLOR;
}

how to show last row of SQL query result in (php) <tfoot> and exclude in <tbody>?

My database colleague built a couple of views and SP's for me so I can focus on html/php.
One is a query that shows a "total" row at the bottom of the table.
I want to:
Exclude this last row on my <tbody>
Show this single row in my <tfoot>
Why don't I just show everything in <tbody> since the totals row will always show at the bottom anyway? Because I want my <tfoot>'s <td>'s colspan to be different, for presentation purposes.
How can I do this using php alone?
I'm using basic PDO:
foreach($table as $row) {
echo '<tr>
<td>'.$row['Item'].'</td>
<td>'.$row['Amount'].'</td>
</tr>';
}
This should work as long as the last row is not exactly the same as any other row.
$last_row = end($table);
echo '<tbody>';
foreach ($table as $row) {
if ($row === $last_row) continue;
echo '<tr>
<td>'.$row['Item'].'</td>
<td>'.$row['Amount'].'</td>
</tr>';
}
echo '</tbody>';
echo '<tfoot>
<tr>
<td>'.$last_row['Item'].'</td>
<td>'.$last_row['Amount'].'</td>
</tr>
</tfoot>';
If it's possible that the last row is not unique you can use the key instead:
$last_key = count($table) - 1;
echo '<tbody>';
foreach ($table as $key => $row) {
if ($key === $last_key) continue;
echo '<tr>
<td>'.$row['Item'].'</td>
<td>'.$row['Amount'].'</td>
</tr>';
}
echo '</tbody>';
echo '<tfoot>
<tr>
<td>'.$table[$last_key]['Item'].'</td>
<td>'.$table[$last_key]['Amount'].'</td>
</tr>
</tfoot>';
$total = 0;
foreach($table as $row) {
$total += $row['Amount'];
echo '<tr>
<td>'.$row['Item'].'</td>
<td>'.$row['Amount'].'</td>
</tr>';
}
echo '<tr>
<td>Total:</td>
<td>'.$total.'</td>
</tr>';
You don't need to use thead or tfoot if you do not intend to use them to style your content or in some cases SEO purposes.

xPath retrieve onclick value

I'm trying to retrieve the onclick value on a td element. This is what I have so far.
$xpath = new DOMXPath($dom);
$trs = $xpath->query("/html/body//table/tr");
foreach ($trs as $tr){
$tds = $xpath->query("td", $tr);
foreach ($tds as $td) {
$a = $xpath->query("#onclick", $td);
echo $a->nodeValue;
echo $td->nodeValue;
}
}
This doesn't seem to be working though.
Here's the structure
<table>
<tr>
<td>Name</td>
<td onclick="blahblah">Author</td>
<td>Title</td>
</tr>
</table>
$a is a NodeList, you must select an item:
#print($a->item(0)->nodeValue);

PHP textContent removing HTML?

I have the following script which loops through a HTML table and gets the values from it then returns the value of the table in a td.
$tds = $dom->getElementsByTagName('td');
// New dom
$dom2 = new DOMDocument;
$x = 1;
// Loop through all the tds printing the value with a new class
foreach($tds as $t) {
if($x%2 == 1)
print "</tr><tr>";
$class = ($x%2 == 1) ? "odd" : "even";
var_dump($t->textContent);
print "<td class='$class'>".$t->textContent."</td>";
$x++;
}
But the textContent seems to be stripping the HTML tags (for example it is a <p></p> wrapper tag). How can I get it to just give me the value?
Or is there another way of doing this? I have the following html
<table>
<tr>
<td>q1</td>
<td>a1</td>
</tr>
<tr>
<td>q2</td>
<td>a2</td>
</tr>
</table>
and I need to make it look like
<table>
<tr>
<td class="odd">q1</td>
<td class="even">a1</td>
</tr>
<tr>
<td class="odd">q2</td>
<td class="even">a2</td>
</tr>
</table>
It will always look the exact same way (minus extra element rows and the values which change).
Any help?
According to MDN this is the expected behaviour of textContent.
You can just add the class to the tds in the DomDocument
$tds = $dom->getElementsByTagName('td');
$x = 1;
foreach($tds as $td) {
if($x%2 == 1){
$td->setAttribute('class', 'odd');
}
else{
$td->setAttribute('class', 'even');
}
$x++;
}

Alternating row colors in html table from xml datasource with php

I would like to alternate the row color from odd and even from the following xml with php.
<?php
// load SimpleXML
$books = new SimpleXMLElement('books.xml', null, true);
echo <<<EOF
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th>Publisher</th>
<th>Price at Amazon.com</th>
<th>ISBN</th>
</tr>
EOF;
foreach($books as $book) // loop through our books
{
echo <<<EOF
<tr>
<td>{$book->title}</td>
<td>{$book->author}</td>
<td>{$book->publisher}</td>
<td>\${$book->amazon_price}</td>
<td>{$book['isbn']}</td>
</tr>
EOF;
}
echo '</table>';
?>
How would I do this with php considering my source is XML?
Add a counter, initialize it to zero, increment on each iteration and put different classes in tr depending on the value of $counter%2 (zero or not). (like ($counter%2)?'odd':'even').
Something like this:
for($i=0;$i<6;$i++)
{
if($i % 2)
{
// even
}else{
// odd
}
}
Here's a simple way.
<?php
// load SimpleXML
$books = new SimpleXMLElement('books.xml', null, true);
echo <<<EOF
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th>Publisher</th>
<th>Price at Amazon.com</th>
<th>ISBN</th>
</tr>
EOF;
$even = true;
foreach($books as $book) // loop through our books
{
$class = $even ? 'even' : 'odd';
$even = $even ? false : true;
echo <<<EOF
<tr class="$class">
<td>{$book->title}</td>
<td>{$book->author}</td>
<td>{$book->publisher}</td>
<td>\${$book->amazon_price}</td>
<td>{$book['isbn']}</td>
</tr>
EOF;
}
echo '</table>';
?>

Categories