Parse (string)table with parent tr´s into array - javascript

I have a string with a table inside; my problem is to put the parent tr´s (according their childs) into array (for later using it is not important whether array is one- or multidimensional)
The table looks like this:
<tr><td class="boys" colspan="4"></td></tr>
<tr><td class="indoor" colspan="4"></td></tr>
<tr class="toy">
<td class="article">Ball</td>
<td class="color">Red</td>
<td class="size">big</td>
<td class="price">10</td>
</tr>
<tr class="toy">
<td class="article">Puzzle</td>
<td class="color">colored</td>
<td class="size">medium</td>
<td class="price">5</td>
</tr>
<tr><td class="outdoor" colspan="4"></td></tr>
<tr class="toy">
<td class="article">Inliner</td>
<td class="color">black</td>
<td class="size">5</td>
<td class="price">15</td>
</tr>
<tr class="toy">
<td class="article">Pool</td>
<td class="color">white/blue</td>
<td class="size">big</td>
<td class="price">25</td>
</tr>
<tr><td class="all" colspan="4"></td></tr>
<tr class="toy">
<td class="article">Book</td>
<td class="color">colored</td>
<td class="size">small</td>
<td class="price">2</td>
</tr>
and same tr/td construct for girls.
With
$html = str_get_html($e);
$toys= array();
foreach ( $html->find('tr[class=toy]') as $toy) {
$item['article'] = trim($toys->find('td', 0)->plaintext);
$item['color'] = trim($toys->find('td', 1)->plaintext);
$item['size'] = trim($toys->find('td', 2)->plaintext);
$item['price'] = trim($toys->find('td', 3)->plaintext);
$toys[] = $item;
}
How can i get both parents? So that array looks like
[0] => Array
(
[sex] => boys
[place] => indoor
[article] => Puzzle
[color] => colored
[size] => medium
[price] => 5
)
trying to get it with
$item['place'] = $toys->find('tr.toy', 0)->plaintext;
give wrong results...

You haven't posted the complete table structure (for girls as well), so I assume it looks like this:
<table id="products">
<tr><td class="boys" colspan="4"></td></tr>
<tr><td class="indoor" colspan="4"></td></tr>
<tr class="toy">...</tr>
<tr><td class="outdoor" colspan="4"></td></tr>
<tr class="toy">...</tr>
<tr><td class="girls" colspan="4"></td></tr>
<tr><td class="indoor" colspan="4"></td></tr>
<tr class="toy">...</tr>
<tr><td class="outdoor" colspan="4"></td></tr>
<tr class="toy">...</tr>
</table>
As I wanted to familiarize with the Simple DOM Parser Package, this was a welcome exercise for me. I leave the code as is. I hope it isn't against SO-Rules to omit explanations. So if an angry admin disagrees, or the OP wants some additional info, leave another answer here and I'll provide it in the future. Please regard the table-attribute id.
$ret = $html->find("table[id=products] tr");
$items = array();
$item = NULL;
foreach($ret as $r)
{
echo "got in the loop<br>";
$class = $r->getAttribute("class");
echo "class: $class<br>";
if (strlen(trim($class)) == 0)
{
echo "We have a <tr> without a class<br>";
$td = $r->firstChild();
$class = $td->getAttribute("class");
echo "The <td>-child should either have class boys,girls, indoor or outdoor<br>";
if (strcasecmp($class, "girls") == 0 ||
strcasecmp($class, "boys") == 0)
{
$sex = $class;
echo "It's a $sex<br>";
}
else
{
if (strcasecmp($class, "indoor") == 0 ||
strcasecmp($class, "outdoor") == 0)
{
$place = $class;
echo "Item is meant for $place-usage<br>";
}
}
}
else
{
$childs = $r->childNodes();
$item = array();
$item["place"] = $place;
$item["sex"] = $sex;
echo "Creating new item for $place-$sex<br>";
foreach($childs as $child)
{
$class = $child->getAttribute("class");
$item[$class] = $child->innertext;
echo "Item attribute $class with ".$child->innertext."<br>";
}
array_push($items, $item);
}
}
echo "<pre>";
print_r($items);
echo "</pre>";

Related

How do I toggle <td> 'table data cells' in Javascript?

So I need to be able to click to show/hide table data cells, however, after trying multiple functions for toggling their display I've been unable to progress forward in my attempts.
Although I've tried several different functions I keep getting the same error:
Uncaught ReferenceError: toggle_visibilityOn is not defined
at HTMLTableCellElement.onclick
Here is the latest iteration of my code in PHP.
if($job_type != 'Maintenance'){ ?>
<tr>
<!--<td><a href="edit-job-2.php?job=<?php echo $job_id; ?>">-->
<td onclick = "toggle_visibilityOn(<?php echo $job_id; ?>)"><?php echo $job_id; ?>
<td id = "billingticket<?php echo $job_id; ?>"><?php echo $billing_ticket; ?></td>
<td id = "billing_status<?php echo $billing_status; ?>"><?php echo $billing_status; ?></td>
<td id = "job_date<?php echo $job_date; ?>"></td>
<td id = "drilling_contractor<?php echo $drilling_contractor; ?>"><?php echo $drilling_contractor; ?></td>
<td id = "job_type<?php echo $job_type; ?>"><?php echo $job_type; ?></td>
<td id = "lease_name<?php echo $lease_name; ?>"><?php echo $lease_name; ?></td>
<td id = "job_status<?php echo $job_status; ?>"><?php echo $job_status; ?></td>
</td>
<tr>
<?php }
I have commented out the previous attempts.
Here is the Javascript portion of the code:
function toggle_visibilityOn()
{
document.getElementById("billing_status<?php echo $billing_status; ?>").style.display = "block";
document.getElementById("job_date<?php echo $job_date; ?>").style.display = "block";
document.getElementById("drilling_contractor><?php echo $drilling_contractor; ?>").style.display = "block";
document.getElementById("job_type<?php echo $job_type; ?>").style.display = "block";
document.getElementById("lease_name<?php echo $lease_name; ?>").style.display = "block";
document.getElementById("job_status<?php echo $job_status; ?>").style.display = "block";
document.getElementById("billingticket<?php echo $job_id; ?>").setAttribute('onclick','toggle_visibilityOff()');
document.getElementById("billingticket<?php echo $job_id; ?>");
}
function toggle_visibilityOff()
{
document.getElementById("billing_status<?php echo $billing_status; ?>").style.display = "none";
document.getElementById("job_date<?php echo $job_date; ?>").style.display = "block";
document.getElementById("drilling_contractor<?php echo $drilling_contractor; ?>").style.display = "block";
document.getElementById("job_type<?php echo $job_type; ?>").style.display = "none";
document.getElementById("lease_name<?php echo $lease_name; ?>").style.display = "none";
document.getElementById("job_status<?php echo $job_status; ?>").style.display = "none";
document.getElementById("billingticket<?php echo $job_id; ?>").setAttribute('onclick','toggle_visibilityOn()');
document.getElementById("billingticket<?php echo $job_id; ?>");
}
I know I'm doing something wrong, and I'm almost sure it has to do with the Id tags, but I'm no closer to figuring it out than I was two days ago.
Note: I'm fixing and editing preexisting code so I'm not familiar with everything that's going on. I'm aware the answer may lie elsewhere.
To anyone who reads this: Thank you for your time.
There is a lot we can do to make this better. First, you shouldn't echo the jobID in the javascript -- you should pass the value via the onClick method. Next, we can combine both of your toggle methods into one simplified version. Lastly, unless you want your table columns to be stacked, you won't want to use display: block for the toggleOn -- use display: inline-block or display: inline.
Here's an example with your PHP removed so that it can demonstrate the functionality of the javascript.
function toggle_visibility(jobID) {
const billingTicket = document.getElementById("billingticket" + jobID);
const style = (billingTicket.style.display === "none") ? "inline-block" : "none";
billingTicket.style.display = style;
document.getElementById("billing_status" + jobID).style.display = style;
document.getElementById("job_date" + jobID).style.display = style;
document.getElementById("drilling_contractor" + jobID).style.display = style;
document.getElementById("job_type" + jobID).style.display = style;
document.getElementById("lease_name" + jobID).style.display = style;
document.getElementById("job_status" + jobID).style.display = style;
}
<table>
<tr>
<td onclick="toggle_visibility(5)">JOBID 5</td>
<td id="billingticket5">Billing Ticket</td>
<td id="billing_status5">Billing Status</td>
<td id="job_date5">Job Date</td>
<td id="drilling_contractor5">Drilling Contractor</td>
<td id="job_type5">Job Type</td>
<td id="lease_name5">Lease Name</td>
<td id="job_status5">Job Status</td>
</tr>
</table>
Just put your PHP back into the table structure here and you should be good to go.
If this is exactly how you want the data in your table to be laid out, we can simplify this even more:
function toggle_visibility(element) {
[...element.parentNode.children].splice(1).forEach(column => {
column.style.display = column.style.display === 'none' ? "inline-block" : "none";
});
}
<table>
<tr>
<td onclick="toggle_visibility(this)">JOBID 5</td>
<td>Billing Ticket</td>
<td>Billing Status</td>
<td>Job Date</td>
<td>Drilling Contractor</td>
<td>Job Type</td>
<td>Lease Name</td>
<td>Job Status</td>
</tr>
</table>
This is even better because it doesn't require the elementID's, and so you also won't need to clutter your PHP file with all those echos.
What I'm doing here is using the context of this to pass in the element that was clicked. Then we get an array of the element's siblings by using spread syntax to create an array from the children object of the parentNode, and splice the array to remove the first child from the array, leaving us with an array that contains all of the siblings of the element that was clicked. Finally we loop through each member of the array to toggle the display style of each sibling.
If you need a button that can toggle all of these types of rows, try this example:
function toggle_visibility(element) {
[...element.parentNode.children].splice(1).forEach(column => {
column.style.display = column.style.display === 'none' ? "inline-block" : "none";
});
//check if all rows are hidden/shown to update button label
let comparison,
shouldUpdateButtonLabel = true,
rows = document.getElementsByTagName("tr");
[...rows].forEach(row => {
if (!comparison) comparison = row.children[1].style.display;
else if (row.children[1].style.display !== comparison) shouldUpdateButtonLabel = false;
});
if (shouldUpdateButtonLabel) {
let button = document.getElementById("toggleAll");
button.innerHTML = button.innerHTML === "Hide All" ? "Show All" : "Hide All";
}
}
function toggleAll(button) {
const style = button.innerHTML === "Hide All" ? "none" : "inline-block";
button.innerHTML = button.innerHTML === "Hide All" ? "Show All" : "Hide All";
let rows = document.getElementsByTagName("tr");
[...rows].forEach(row => {
[...row.children].splice(1).forEach(column => {
column.style.display = style;
});
});
}
td {
display: inline-block;
}
<table>
<tr>
<td onclick="toggle_visibility(this)">JOBID 5</td>
<td>Billing Ticket</td>
<td>Billing Status</td>
<td>Job Date</td>
<td>Drilling Contractor</td>
<td>Job Type</td>
<td>Lease Name</td>
<td>Job Status</td>
</tr>
<tr>
<td onclick="toggle_visibility(this)">JOBID 6</td>
<td>Billing Ticket</td>
<td>Billing Status</td>
<td>Job Date</td>
<td>Drilling Contractor</td>
<td>Job Type</td>
<td>Lease Name</td>
<td>Job Status</td>
</tr>
<tr>
<td onclick="toggle_visibility(this)">JOBID 7</td>
<td>Billing Ticket</td>
<td>Billing Status</td>
<td>Job Date</td>
<td>Drilling Contractor</td>
<td>Job Type</td>
<td>Lease Name</td>
<td>Job Status</td>
</tr>
</table>
<button id="toggleAll" onclick="toggleAll(this)">Hide All</button>

Unable to store particular target values in jQuery

$(document).ready(e => {
$(".test").click(e => {
textvalue = displayData(e);
console.log(textvalue); //prints the array
});
});
function displayData(e) {
let i = 0;
const td = $("#tbody tr td");
let textvalues = [];
for (const value of td) {
if (value.dataset.name == e.target.dataset.name) {
textvalues[i++] = value.textContent;
}
}
return textvalues;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Gender</th>
<th>Email</th>
<th>Contact</th>
<th>Department</th>
<th>Edit</th>
</tr>
</thead>
<tbody id="tbody">
<tr>
<td>DummyName</td>
<td>20</td>
<td>Female</td>
<td>DummyEmail</td>
<td>DummyContact</td>
<td>DummyDepartment</td>
<td class="test">Click</td>
</tr>
<tr>
<td>DummyName2</td>
<td>22</td>
<td>Female</td>
<td>DummyEmail2</td>
<td>DummyContact2</td>
<td>DummyDepartment2</td>
<td class="test">Click</td>
</tr>
</tbody>
</table>
</body>
</html>
I'm using jQuery to update onscreen values in a form. Complete beginner at this.
$(document).ready(e =>{
$(".btnedit").click(e =>{
textvalues = displayData(e);
let sname = $("input[name*='name_type");
let sage = $("input[name*='age_type");
let sgender = $("input[name*='gender_type");
let semail = $("input[name*='email_type");
let scontact = $("input[name*='contact_type");
let sdept = $("input[name*='dept_type");
sname.val(textvalues[0]);
sage.val(textvalues[1]);
sgender.val(textvalues[2]);
semail.val(textvalues[3]);
scontact.val(textvalues[4]);
sdept.val(textvalues[5]);
});
});
function displayData(e){
let i = 0;
const td = $("#tbody tr td");
let textvalues = [];
for(const value of td){
if(value.dataset.name == e.target.dataset.name)
{
//console.log(value);
textvalues[i++] = value.textContent;
}
}
return textvalues;
}
I need to get the data stored in a table onto the inputs of the form, in order for the user to update it further. The user clicks on a record to edit it(which is displayed on the page).
The record values are stored in the array textvalues. Problem is the entire table values get stored in the array instead of just the single record.
In value.dataset.name, name is a column from the table which I'm using as the primary key (I know it's wrong, but just going with it for now).
Edit: Original table code:
while($row = mysqli_fetch_assoc($result))
{
?>
<tr>
<td data-name = "<?php echo $row['name']; ?>"><?php echo $row['name'];?></td>
<td data-name = "<?php echo $row['name']; ?>"><?php echo $row['age'];?></td>
<td data-name = "<?php echo $row['name']; ?>"><?php echo $row['gender'];?></td>
<td data-name = "<?php echo $row['name']; ?>"><?php echo $row['email'];?></td>
<td data-name = "<?php echo $row['name']; ?>"><?php echo $row['contact'];?></td>
<td data-name = "<?php echo $row['name']; ?>"><?php echo $row['department'];?></td>
<td data-name = "<?php echo $row['name']; ?>"><i class="fas fa-edit btnedit"></i></td>
</tr>
<?php
}
Your code works fine except one little thing: all your input selectors lack closing quote and bracket, e.g. this is wrong (jQuery 3.3.1 throws error):
let sname = $("input[name*='name_type");
But this is right:
let sname = $("input[name*='name_type']");
Otherwise, it works just fine (well, certain optimizations can be done, that's true, but still it works as is) -- if I have guessed your HTML structure correctly, see example below. (Disclaimer: this is by no means a good piece of code with best pracrices etc. This is just a reproduction of original task with minimal fix to make it work.)
$(document).ready(e => {
$(".btnedit").click(e => {
textvalues = displayData(e);
let sname = $("input[name*='name_type']");
let sage = $("input[name*='age_type']");
let sgender = $("input[name*='gender_type']");
sname.val(textvalues[0]);
sage.val(textvalues[1]);
sgender.val(textvalues[2]);
});
});
function displayData(e) {
let i = 0;
const td = $("#tbody tr td");
let textvalues = [];
for (const value of td) {
if (value.dataset.name == e.target.dataset.name) {
textvalues[i++] = value.textContent;
}
}
return textvalues;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tbody id="tbody">
<tr>
<td data-name="1">a1</td>
<td data-name="1">b1</td>
<td data-name="1">c1</td>
<td><button class="btnedit" data-name="1">edit</button></td>
</tr>
<tr>
<td data-name="2">a2</td>
<td data-name="2">b2</td>
<td data-name="2">c2</td>
<td><button class="btnedit" data-name="2">edit</button></td>
</tr>
</tbody>
</table>
Type: <input type="text" name="name_type" /><br />
Age: <input type="text" name="age_type" /><br />
Gender: <input type="text" name="gender_type" />
Possible reason of could be this: data-name is invalid everywhere in your table. If that's not the case, please share some more code. Minimal yet complete example would be great.
Update: in your example HTML I see no data-name attributes at all, and clickable element also does not have it. So, your selector $('#tbody th td') matches all TDs, and that's why you see whole table in output.
So, look at the example above and do the same with data-name: every <td> and the button on one row have the same value in that attribute.

JQuery tablesorting code not working (no plugin)

I'm displaying a table with AJAX in my website. I wrote a JQuery code for sorting my table when it's send via AJAX and a <th>-tag is clicked. (I don't want to use a plugin. No, really, I don't want to use a plugin!)
This is my code:
PHP (index.php):
<form action="query.php" method="get">
<input type="search" name="query" autofocus="true" autocomplete="off" list="products">
<datalist id="products">
<?php
$sql = "SELECT * FROM products;";
$result = mysqli_query($con, $sql);
while ($product = mysqli_fetch_array($result)) {
echo "<option value=\"" . $product["productname"] . "\">" . $product["price"] . " $</option>";
}
?>
</datalist>
<button type="submit">Search</button>
</form>
<div class="result" align="center"></div>
PHP (query.php):
<?php
include_once "connection.php";
$query = trim($_GET["query"]);
$query = mysqli_real_escape_string($con, $query);
$sql = "SELECT * FROM products WHERE productname LIKE '%$query%' ORDER BY productname;";
$result = mysqli_query($con, $sql);
$result_no = mysqli_num_rows($result);
if ($result_no > 0) {
echo "<table>";
echo "<thead>";
echo "<tr>";
echo "<th>Product</th>";
echo "<th>Price</th>";
echo "<th>Quantity</th>";
echo "</tr>";
echo "</thead>";
echo "<tbody>";
while ($product = mysqli_fetch_array($result)) {
echo "<tr class=\"table\"><td align=\"left\">" . $product["productname"] . "</td><td align=\"right\">" . $product["price"] . " $</td><td align=\"right\">" . $product["quantity"] . "</td></tr>";
}
echo "</tbody>";
echo "<tfoot>";
if ($result_no == 1) {
echo "<tr><td colspan=\"3\" align=\"center\">" . $result_no . " product found." . "</td></tr>";
} else {
echo "<tr><td colspan=\"3\" align=\"center\">" . $result_no . " product found." . "</td></tr>";
}
echo "</tfoot>";
echo "</table>";
} elseif ($result_no <= 0) {
echo "<p>No products found.</p>";
}
mysqli_close($con);
?>
JQuery:
$(document).ready(function() {
$("form").on("submit", function(event) {
event.preventDefault();
var form = $(this);
$.ajax({
type: this.method,
url: this.action,
data: form.serialize(),
cache: false,
success: function(data) {
$("div.result").html(data);
$("th").on("click", function() {
var column = $(this).index();
var tbody = $("tbody");
var rows = tbody.find("tr");
var dir = $(this).data("dir") || -1;
dir *= -1;
rows.sort(function(a, b) {
var aVal = $($(a).find("td")[column]).text().toLowerCase();
var bVal = $($(b).find("td")[column]).text().toLowerCase();
return aVal > bVal ? 1 * dir : aVal < bVal ? -1 * dir : 0;
});
$(this).data("dir", dir);
tbody.empty();
$(rows).appendTo(tbody);
});
}
});
});
});
The connection.php is for connecting to my database. I use MySQL and PHPMyAdmin. My tables are 'users' for login data and 'products' for the shop products.
My Problem: The first line of the table is alway sorted at the wrong place.
Use the built in javascript sort function.
I extracted the relevant sort code from your example
I grabbed a sample table from w3cschools
I modified the js to store the sorted direction in the header cell.
I implemented a compare function (see linked sort documentation).
I replaced the tbody when the sort was complete.
EDIT: changed out HTML, added functionality to function to enable numeric sorting and not just alphabetically. Note the number class and the new if in the sort function
$("th").on("click", function() {
var column = $(this).index();
var numeric = $(this).hasClass("number"); //this class has been sprinkled to identify numeric sort.
var bdy = $(this).closest("table").find("tbody");
var rows = bdy.find("tr");
var dir = $(this).data("dir") || -1; //default direction is desc
dir *= -1; //reverse the stored direction
rows.sort(function(a, b) {
var aVal = $($(a).find("td")[column]).text().toLowerCase(); //get the text from one row
var bVal = $($(b).find("td")[column]).text().toLowerCase(); //get the text from row 2
if (numeric) { //added to handle numeric columns
aVal = parseFloat(aVal);
bVal = parseFloat(bVal);
}
return aVal > bVal ? 1 * dir : aVal < bVal ? -1 * dir : 0; // note the dir value to change direction
}); //sort the rows by the column content
bdy.empty(); //empty the body
$(rows).appendTo(bdy); //put the rows back
$(this).data("dir", dir); //log the direction
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table">
<thead>
<tr class="table">
<th class="table">Product</th>
<th class="table number">Price</th>
<th class="table number">Quantity</th>
</tr>
</thead>
<tbody>
<tr class="table">
<td align="left" class="table">Chainsaw</td>
<td align="right" class="table">60.00 $</td>
<td align="right" class="table">1</td>
</tr>
<tr class="table">
<td align="left" class="table">Hammer</td>
<td align="right" class="table">24.99 $</td>
<td align="right" class="table">2</td>
</tr>
<tr class="table">
<td align="left" class="table">Nails (25 per Box)</td>
<td align="right" class="table">9.99 $</td>
<td align="right" class="table">21</td>
</tr>
<tr class="table">
<td align="left" class="table">Screwdriver</td>
<td align="right" class="table">29.99 $</td>
<td align="right" class="table">2</td>
</tr>
<tr class="table">
<td align="left" class="table">Screws (25 per Box)</td>
<td align="right" class="table">15.00 $</td>
<td align="right" class="table">26</td>
</tr>
</tbody>
<tfoot>
<tr class="table">
<td colspan="3" align="center" class="table">5 products found.</td>
</tr>
</tfoot>
</table>
#FelixRewer, please include a sample rendered table, your question was updated with your PHP. I believe that the PHP is not your problem but the HTML that comes out the other end.
Yes, maybe you're right. So here's a code snippet with the output of query.php and the JQuery tablesorter (I also added my styles, I don't think it's relevant, but if yes, here it is.):
$("th").on("click", function() {
var column = $(this).index();
var table = $("table");
var tbody = table.find("tbody");
var rows = tbody.find("tr");
var dir = $(this).data("dir") || -1;
dir *= -1;
rows.sort(function(a, b) {
var aVal = $($(a).find("td")[column]).text().toLowerCase().trim();
var bVal = $($(b).find("td")[column]).text().toLowerCase().trim();
return aVal > bVal ? 1 * dir : aVal < bVal ? -1 * dir : 0;
});
$(this).data("dir", dir);
tbody.empty();
$(rows).appendTo(table);
});
.table {
margin: 3vmax;
border: 1px solid #000000;
border-collapse: collapse;
color: #000000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="table">
<thead>
<tr class="table">
<th class="table">Product</th>
<th class="table">Price</th>
<th class="table">Quantity</th>
</tr>
</thead>
<tbody>
<tr class="table">
<td align="left" class="table">Chainsaw</td>
<td align="right" class="table">60.00 $</td>
<td align="right" class="table">1</td>
</tr>
<tr class="table">
<td align="left" class="table">Hammer</td>
<td align="right" class="table">24.99 $</td>
<td align="right" class="table">2</td>
</tr>
<tr class="table">
<td align="left" class="table">Nails (25 per Box)</td>
<td align="right" class="table">9.99 $</td>
<td align="right" class="table">21</td>
</tr>
<tr class="table">
<td align="left" class="table">Screwdriver</td>
<td align="right" class="table">29.99 $</td>
<td align="right" class="table">2</td>
</tr>
<tr class="table">
<td align="left" class="table">Screws (25 per Box)</td>
<td align="right" class="table">15.00 $</td>
<td align="right" class="table">26</td>
</tr>
</tbody>
<tfoot>
<tr class="table">
<td colspan="3" align="center" class="table">5 products found.</td>
</tr>
</tfoot>
</table>
And yes I have the same problem here. I've tested a lot with my code and I think maybe the first line gets sorted at the wrong place, because of the <tfoot>, but that's just an assumption.
This topic is closed. Here's the code I was looking for:
JavaScript: https://jsfiddle.net/tf4e97w6/#&togetherjs=TGIj8qdzUO
jQuery: https://jsfiddle.net/15ke8Lqv/#&togetherjs=DACQV5mE9F
Code snippet:
$(document).ready(function() {
$("th").on("click", function() {
var column = $(this).index();
var table = $("table");
var tbody = table.find("tbody");
var rows = tbody.find("tr");
var dir = $(this).data("dir") || -1;
dir *= -1;
$(this).siblings().data("dir", -1);
rows.sort(function(a, b) {
var aVal = $($(a).find("td")[column]).html().toLowerCase().trim();
var bVal = $($(b).find("td")[column]).html().toLowerCase().trim();
if ($.isNumeric(aVal.charAt()) && $.isNumeric(bVal.charAt())) {
aVal = parseFloat(aVal);
bVal = parseFloat(bVal);
}
return aVal > bVal ? 1 * dir : aVal < bVal ? -1 * dir : 0;
});
$(this).data("dir", dir);
tbody.empty();
$(rows).appendTo(table);
});
});
h1 {
color: #cc1100;
}
table {
width: 100%;
}
table,
tr,
td {
border: 1px solid #000000;
border-collapse: collapse;
}
tfoot,
thead {
text-align: center;
background-color: #cccccc;
}
th:hover {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<caption>
<h1>Tablesorter</h1>
</caption>
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<tbody>
<tr>
<td>January</td>
<td>$150</td>
</tr>
<tr>
<td>February</td>
<td>$160</td>
</tr>
<tr>
<td>March</td>
<td>$240</td>
</tr>
<tr>
<td>April</td>
<td>$160</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">Sum: $710</td>
</tr>
</tfoot>
</table>
With kind regards,
Felix Rewer.

Keep Hidden Table Column/Cells Visible Using Session Storage

I have a table where I can check a checkbox and it will log all of the contents in the row. Once you check a checkbox, another column (Quantity #) appears with a textbox/spinner inside the cell. If it is unchecked, then the corresponding textbox/spinner disappears.
I want to use session storage to be able to keep any cells in the Quantity # column visible if the row is checked throughout refreshes until the page is closed. I was able to use session storage to keep the checkboxes checked, but am having a problem with getting the Quantity # column to remain visible so long as the checkbox is checked for that row.
How can I do this?
I have included the HTML for the checkbox row and the JavaScript that keeps the checkboxes checked so it can be used to base an answer off of if needed.
HTML for the checkbox and the appearing table cell:
<td class="ui-widget-content"><input type="checkbox" class="check" name="check" id="checkid-<?php echo intval ($row['ID'])?>"></td>
<td class="quantity_num ui-widget-content" name="rows[0][0][quant]" style="display: none;"><input type="textbox" style="width: 100px;" class="spinner" id="spin-<?php echo intval ($row['ID'])?>"></td>
JavaScript for the session storage for the checkbox and for the spinner in the appearing table cell:
$(function(){
$(':checkbox').each(function() {
// Iterate over the checkboxes and set their "check" values based on the session data
var $el = $(this);
$el.prop('checked', sessionStorage[$el.prop('id')] === 'true');
});
$('input:checkbox').on('change', function() {
// save the individual checkbox in the session inside the `change` event,
// using the checkbox "id" attribute
var $el = $(this);
sessionStorage[$el.prop('id')] = $el.is(':checked');
});
});
$(function () {
$(".check").change(function(){
$(this).closest('tr').find('td.quantity_num').toggle(this.checked);
console.log($('input.check').is(':checked'));
var quantity = $(this).closest('tr').find('td.quantity').data('quantity');
console.log(quantity);
if($('input.check').is(':checked'))
$(this).closest('table').find('th.num').toggle(true);
else
$(this).closest('table').find('th.num').toggle(false);
$(this).closest("tr").find(".spinner" ).spinner({
spin: function( event, ui ) {
if ( ui.value > quantity ) {
$( this ).spinner( "value", quantity );
return false;
} else if ( ui.value <= 1 ) {
$( this ).spinner( "value", 1 );
return false;
}
}
});
});
});
EDIT:
HTML/PHP for table:
<table id="merchTable" cellspacing="5" class="sortable">
<thead>
<tr class="ui-widget-header">
<th class="sorttable_nosort"></th>
<th class="sorttable_nosort">Loc</th>
<th class="merchRow">Report Code</th>
<th class="merchRow">SKU</th>
<th class="merchRow">Special ID</th>
<th class="merchRow">Description</th>
<th class="merchRow">Quantity</th>
<th class="sorttable_nosort">Unit</th>
<th style="display: none;" class="num">Quantity #</th>
</tr>
</thead>
<tbody>
<?php foreach ($dbh->query($query) as $row) {?>
<tr>
<td class="ui-widget-content"><input type="checkbox" class="check" name="check" id="checkid-<?php echo intval ($row['ID'])?>"></td>
<td class="loc ui-widget-content"><input type="hidden"><?php echo $row['Loc'];?></td>
<td name="rows[0][0][rp-code]" class="rp-code ui-widget-content" align="center" id="rp-code-<?php echo intval ($row['Rp-Code'])?>"><?php echo $row['Rp-Code'];?></td>
<td name="rows[0][0][sku]" class="sku ui-widget-content" id="sku-<?php echo intval ($row['SKU'])?>"><?php echo $row['SKU'];?></td>
<td name="rows[0][0][special-id]" class="special-id ui-widget-content" align="center" id="special-id-<?php echo intval ($row['Special-ID'])?>"><?php echo $row['Special-ID'];?></td>
<td name="rows[0][0][description]" class="description ui-widget-content" id="description-<?php echo intval ($row['Description'])?>"><?php echo $row['Description'];?></td>
<td name="rows[0][0][quantity]" class="quantity ui-widget-content" data-quantity="<?php echo $row['Quantity'] ?>" align="center" id="quantity-<?php echo intval ($row['Quantity'])?>"><?php echo $row['Quantity'];?></td>
<td name="rows[0][0][unit]" class="unit ui-widget-content" id="unit-<?php echo intval ($row['Unit'])?>"><?php echo $row['Unit'];?></td>
<td name="rows[0][0][quant]" style="display: none;" class="quantity_num ui-widget-content"><input type="textbox" style="width: 100px;" class="spinner" id="spin-<?php echo intval ($row['ID'])?>"></td>
</tr>
<?php } ?>
</tbody>
</table>

HTML Table to JSON file via AJAX

Is there any possibility to convert an HTML table to JSON with PHP?
I have this JavaScript:
<script>
(function() {
var jsonArr = [];
var obj = {};
var rowIx = 0;
var jsonObj = {};
var thNum = document.getElementsByTagName('th').length;
var arrLength = document.getElementsByTagName('td').length;
for(i = 0; i < arrLength; i++){
if(i%thNum === 0){
obj = {};
}
var head = document.getElementsByTagName('th')[i%thNum].innerHTML;
var content = document.getElementsByTagName('td')[i].innerHTML;
obj[head] = content;
if(i%thNum === 0){
jsonObj[++rowIx] = obj
}
}
var result = "<br>"+JSON.stringify({"Values": jsonObj});
document.write(result);
})();
</script>
which uses the below HTML code:
<TABLE border="3" rules="all" bgcolor="#E7E7E7" cellpadding="1" cellspacing="1">
<TR>
<TH align=center><font size="3" face="Arial">Date</font></TH>
<TH align=center><font size="3" face="Arial"><B>Teacher</B></font></TH>
<TH align=center><font size="3" face="Arial">?</font></TH>
<TH align=center><font size="3" face="Arial">Hour</font></TH>
<TH align=center><font size="3" face="Arial">Subject</font></TH>
<TH align=center><font size="3" face="Arial">Class</font></TH>
<TH align=center><font size="3" face="Arial">Room</font></TH>
<TH align=center><font size="3" face="Arial">(Teacher)</font></TH>
<TH align=center><font size="3" face="Arial">(Room)</font></TH>
<TH align=center><font size="3" face="Arial">XYY</font></TH>
<TH align=center><font size="3" face="Arial"><B>Information</B></font></TH>
<TH align=center><font size="3" face="Arial">(Le.) nach</font></TH>
</TR>
<TR><TD align=center><font size="3" face="Arial">24.9.</font></TD>
<TD align=center><font size="3" face="Arial"><B><strike>Dohe</strike></B></font></TD>
<TD align=center><font size="3" face="Arial">Free</font></TD>
<TD align=center><font size="3" face="Arial">1</font></TD>
<TD align=center><font size="3" face="Arial"><strike>Math</strike></font> </TD>
<TD align=center><font size="3" face="Arial">(9)</font> </TD>
<TD align=center><font size="3" face="Arial">---</font> </TD>
<TD align=center><font size="3" face="Arial"><strike>Dohe</strike></font></TD>
<TD align=center><font size="3" face="Arial">A001</font></TD>
<TD align=center> </TD>
<TD align=center> </TD>
<TD align=center><font size="3" face="Arial">Free.</font></TD>
</TR>
</TABLE>
to generate this JSON code:
{"Values":{"1":{"Date":"24.9.","Teacher":"Dohe","?":"Free","Hour":"1","Subject":"Math ","Class":"(9) ","Room":"--- ","(Teacher)":"Dohe","(Room)":"A001","XYY":" ","Information":" ","(Le.) nach":"Free."},"2":{"Date":"26.9.","Teacher":"John","?":"Free","Hour":"8","Subject":"Bio ","Class":"(9) ","Room":"--- ","(Teacher)":"John","(Room)":"A021","XYY":" ","Information":" ","(Le.) nach":"Free."}}}
The script is perfect but I need a script, which saves the JSON data to a file on the server automatically, without any user interaction.
If you say your JS logic is perfect, here is a PHP (ver 5.3+) conversion that uses DOM like your code.
This function loads a html file (you may use curl if it is an url) then convert it and save to a file.
function save_table_to_json ( $in_file, $out_file ) {
$html = file_get_contents( $in_file );
file_put_contents( $out_file, convert_table_to_json( $html ) );
}
function convert_table_to_json ( $html ) {
$document = new DOMDocument();
$document->loadHTML( $html );
$obj = [];
$jsonObj = [];
$th = $document->getElementsByTagName('th');
$td = $document->getElementsByTagName('td');
$thNum = $th->length;
$arrLength = $td->length;
$rowIx = 0;
for ( $i = 0 ; $i < $arrLength ; $i++){
$head = $th->item( $i%$thNum )->textContent;
$content = $td->item( $i )->textContent;
$obj[ $head ] = $content;
if( ($i+1) % $thNum === 0){ // New row. Slightly modified to keep it correct.
$jsonObj[++$rowIx] = $obj;
$obj = [];
}
}
return json_encode([ "Values" => $jsonObj ]);
}
// Example
save_table_to_json( 'table.html', 'data.json' );
Personally, I like using JQuery, yet if you don't know or can't insert it as a SRC, then we should assume to use JavaScript. So we will post the JSON data to our PHP via AJAX when the page is done loading. The PHP will then write this JSON into a new file on the server called subs.json and will be overwritten each time the script runs.
We will start with the JavaScript:
<script>
function collectData() {
var jsonArr = [];
var obj = {};
var rowIx = 0;
var jsonObj = {};
var thNum = document.getElementsByTagName('th').length;
var arrLength = document.getElementsByTagName('td').length;
for(i = 0; i < arrLength; i++){
if(i%thNum === 0){
obj = {};
}
var head = document.getElementsByTagName('th')[i%thNum].innerHTML;
var content = document.getElementsByTagName('td')[i].innerHTML;
obj[head] = content;
if(i%thNum === 0){
jsonObj[++rowIx] = obj;
}
}
return jsonObj;
}
function postJSONData(json){
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "/subPost.php");
xmlhttp.setRequestHeader("Content-Type", "application/application/x-www-form-urlencoded");
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var return_data = xmlhttp.responseText;
alert(return_data);
}
}
xmlhttp.send("values="+JSON.stringify(json));
}
postJSONData(collectData());
</script>
At this point, the page should post your JSON to a PHP page called subPost.php located at the same level as the page that is executing this JS. This PHP Will look like:
<?php
if(isset($_POST['values'])){
$values = $_POST['values'];
$fp = fopen('subs.json', 'w');
fwrite($fp, $values);
fclose($fp);
echo "Values written to subs.json.\r\n";
} else {
echo "No Post data received.";
}
?>
I made a working example you can see here: http://www.yrmailfrom.me/projects/testPost/ and the content of http://www.yrmailfrom.me/projects/testPost/subs.json is:
{"1":{"<font face=\"Arial\" size=\"3\">Date</font>":"<font face=\"Arial\" size=\"3\">24.9.</font>","<font face=\"Arial\" size=\"3\"><b>Teacher</b></font>":"<font face=\"Arial\" size=\"3\"><b><strike>Dohe</strike></b></font>","<font face=\"Arial\" size=\"3\">?</font>":"<font face=\"Arial\" size=\"3\">Free</font>","<font face=\"Arial\" size=\"3\">Hour</font>":"<font face=\"Arial\" size=\"3\">1</font>","<font face=\"Arial\" size=\"3\">Subject</font>":"<font face=\"Arial\" size=\"3\"><strike>Math</strike></font> ","<font face=\"Arial\" size=\"3\">Class</font>":"<font face=\"Arial\" size=\"3\">(9)</font> ","<font face=\"Arial\" size=\"3\">Room</font>":"<font face=\"Arial\" size=\"3\">---</font> ","<font face=\"Arial\" size=\"3\">(Teacher)</font>":"<font face=\"Arial\" size=\"3\"><strike>Dohe</strike></font>","<font face=\"Arial\" size=\"3\">(Room)</font>":"<font face=\"Arial\" size=\"3\">A001</font>","<font face=\"Arial\" size=\"3\">XYY</font>":"
This is not valid JSON. It seems that some data is being misunderstood. I suspect that this is due to characters in the values, like . I see this in the posted data:
"<font face=\"Arial\" size=\"3\">XYY</font>":"
[nbsp;","<font_face] => \"Arial\" size=\"3\">(Le.) nach</font>":"<font face=\"Arial\" size=\"3\"
>Free.</font>"}}
I was able to overcome this with another small JS function:
function nbsp2space(str) {
return String(str).replace(/ /g, ' ');
}
Then use this function in collectData() like so:
obj[head] = nbsp2space(content);
Now when the page executes, we post the data to the PHP and it's written to the file subs.json.
You can try some thing like this:
HTML
<table>
<tr>
<th>No</th>
<th>Name</th>
<th>Email</th>
</tr>
<tr>
<td>1</td>
<td>Test</td>
<td>test#example.com</td>
</tr>
<tr>
<td>2</td>
<td>Test 2</td>
<td>test2#example.com</td>
</tr>
<tr>
<td>3</td>
<td>Test 3</td>
<td>test3#example.com</td>
</tr>
</table>
Javascript
<script type="text/javascript">
jQuery(document).ready(function(){
data = new Array();
columns = [];
var row = new Array();
$('table tr').each(function(index,tr){
var index = index;
if(index == 0){ // First we get column names from th.
$(tr).find('th').each(function(thIndex,thValue){
columns.push($(thValue).text());
});
} else {
$(tr).find('td').each(function(tdIndex,tdValue){
row[tdIndex] = $(tdValue).text(); // Put each td value in row
});
data.push(row); // now push each row in data.
row = new Array(); // reset row after push
}
});
// Send it to PHP for further work:
$.post('json.php', { data: data, columns: columns }, function(response){
// TODO with response
});
})
</script>
json.php
$data = $_POST['data']; // Each rows values
$columns = $_POST['columns']; // Columns names
for($i = 0; $i < count($data); $i++) {
$json[] = array(($i+1) => array_combine($columns, $data[$i]));
}
$json = json_encode($json);
// TODO with $json eg: file_put_contents();
the output you will get after json_encode() is:
{"values":[{"1":{"No":"1","Name":"Test","Email":"test#example.com"}},{"2":{"No":"2","Name":"Test 2","Email":"test2#example.com"}},{"3":{"No":"3","Name":"Test 3","Email":"test3#example.com"}}]}
Note jQuery must be included before running this.

Categories