How to dynamically convert html table rows to columns? - javascript

To modify the database format SQL uses two functions called PIVOT and UNPIVOT. I was wondering, is there a way of achieving the same thing using a script on the client side?
Let's take this table for example:
Rowa. pro_img#1|${product.name}|${product.price}|${product.description}
Rowb. prod_img#2|${product.name}|${product.price}|${product.description}
Rowc. prod_img#3|${product.name}|${product.price}|${product.description}
Rowd. pro_img#4|${product.name}|${product.price}|${product.description}
As you already know, every html table with dynamic data will print rows. What i'm trying to do is having an html table with dynamic data printing columns so I can display a nice product listing like in this example:
Product_img#1| Product_img#2| Product_img#3|
Product_name| Product_name| Product_name|
Product_price| Product_price| Product_price|
Product_description| Product_description| Product_description|
I'm trying to display 3 objects per row. Is there a way of doing this with a simple javascript?
I'm trying to achieve exactly the same thing as Vans (example here)!
Edited New code
<div id="product_container">
<c:forEach var="product" items="${categoryProducts}" varStatus="iter">
<div id="product_image"><a href="viewProduct?${product.id}">
<img class="img" alt="" src="${initParam.productImagePath}${product.id} (1).jpg" /></a></div>
<div id="product_name">${product.name}</div>
<div id="product_price">${product.price}</div>
<div id="add_toList"><form id="wishlistForm" action="addToWishlist" method="post">
<input name="productId" value="${product.id}" type="hidden">
<input class="submit" value="<fmt:message key='AddToWishlist'/>" type="submit">
</form></div>
<div id="add_toCart"><form id="cartForm" action="addToCart" method="post">
<br>
<br>
<input name="productId" value="${product.id}" type="hidden">
<input class="submit" value="<fmt:message key='AddToCart'/>" type="submit">
</form></div>
</c:forEach>
</div>

jsfiddle DEMO
This function takes an HTML table and returns a table which has the given table's rows and columns swapped.
Example input:
----------------
A1 | A2 | A3
----------------
B1 | B2 | B3
----------------
Output:
---------
A1 | B1
---------
A2 | B2
---------
A3 | B3
---------
Javascript:
function convertTable(tbl) {
var rows = tbl.rows.length;
var cols = tbl.rows[0].cells.length;
var tbl2 = document.createElement('table');
for (var i = 0; i < cols; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < rows; j++) {
var td = document.createElement('td');
var tdih = tbl.rows[j].cells[i].innerHTML;
td.innerHTML = tdih;
tr.appendChild(td);
}
tbl2.appendChild(tr);
}
return tbl2;
}

You could create custom divs for each object:
function generateProductDiv(product) {
return
'<div class="product">' +
'<img src="' + product.image + '">' +
'...' +
'</div>';
}
Create a div for each product put them in a parent divs and style them with css (the CSS property display: table; and table-* might be of interest to you if you want to do it this way, another possibility is to use libraries).
The easier solution is just to put those divs inside the cells of a table although you should only use tables if you really want to display tabular data.
You seem to have JSP code, not Javascript, here is how to generate the forms in JSP (btw. JSP is the same as Java Servlets, just another way of writing it, simply put: JSP = html with Java, Servlet = Java with html). Using forms instead of divs because that seems to be what you want:
<c:forEach var="product" items="${categoryProducts}" varStatus="iter">
<form id="wishlistForm" action="addToWishlist" method="post">
...
<a href="viewProduct?${product.id}">
<img class="img" alt="" src="${initParam.productImagePath}${product.id} (1).jpg" />
</a>
...
</form>
</c:forEach>

The best (easiest, most maintainable, most semantic) way to do this is pure CSS. You shouldn't have to change the markup at all.
And it's actually pretty easy.
You just have to get the browser to stop treating it like table data. The key is the display: CSS property.
display defaults to table-row for <tr> elements and table-cell for <td> elements, which makes sense. You have to "break" that property.
tr, td {
display: block;
}
In other words, you're telling the browser to "display table row and table data elements just like any other block-level element (like a div, for example)".
This gets you almost all the way there. Now you have to get the table "rows" (which are now being laid out like divs) to stack next to each other, instead of on top of each other.
tr {
float: left;
}
Now, you end up with the possibility of the new "rows" not being the same height. If, for example, your product images are not all the same height, or if your descriptions are different lengths, you'd end up with the table being way out of alignment.
So, you need to give your <td>s all the same height.
td{
height: 150px; /*or whatever height you want*/
}
So, the full CSS code looks like this:
tr {
display: block;
float: left;
}
td {
display: block;
height: 150px;
}
Obviously, this is only the most basic code to get your foot in the door. In the real world, you'd give each type of <td> its own height, something like:
td.product-image {
height: 150px;
}
td.product-description {
height: 70px;
}
td.product-price {
height: 40px;
}
Here's a codepen that puts it all together: table data pivoted
Also, here's some more info on the CSS display property if you're interested.
Best of luck. Feel free to ask in comments if you have any further questions.

Related

Large HTML table and repeated cell creation using Javascript

I'm sure this has to be documented somewhere, but I'm so new at html that I can't figure out where to look, so feel free to just direct me to the correct reading if it's something obvious. As a note, I code in several languages, but mostly things like python or LaTeX, nothing like html. I have included my attempt at the bottom, but tried to include what I could in terms of my design setup and what problems I am running into. Any solutions to any of the listed problems would be appreciated :)
The setup;
I need to form gigantic table of information that will be populated based on a JSON string given on another webpage. I am currently using javascript to parse the JSON string and form a single global object of my own that has all the relevant information (the JSON string that the user will plug in is huge and I only need a relatively small portion of it). The created object has the following form;
cruData = {hero:[{"cruID":0,"EP":0,"Owned":"No","GearName1":"Empty","GearRarity1":"Empty","GearName2":"Empty","GearRarity2":"Empty","GearName3":"Empty","GearRarity3":"Empty"}]}
With the cruID iterating from 0 to 103 (plans to make it at least a couple hundred higher at some point). All the strings will be populated with strings, and EP/ID are true numbers.
Proposed Setup:
My original plan was to pre-make the (blank) table structure on the separate web page, and then after grabbing the JSON file and parsing it into the cruData object, to populate that table with relevant info (technically ID 1 to 103 for now, no 0 ID in this table) upon page-load when the user goes to it.
The Problems:
1) My concern with this method of forming the table upon page load is that the table is sizable and I'd really rather not have the table get formed on every page load. Is there a way to save this locally so that it gets formed once and then it's in cache?
2) Assuming pre-formatting the table really is a good idea (feel free to tell me I'm stupid for thinking so heh), I will have some 350+ cells (all the 'rarity' cells) that are all identical dropdown boxes. Thus I'd like to make a class type for cells that are a drop-down selection list, but can't figure out how to do that in the style section. I can get a specific td to work, but not a class of td to work. Again, I'm sure this is because I just don't really understand how defining classes or the style section works (I haven't used a class-inheritance system coding before).
To be specific, I would like there to be a drop-down box in 3 different cells of every row of this 100+ row table. They are all identical drop-down selections. To do this, would a form be better? Is there a way to define the drop-down selections one time in a class, and then just point to the class in each cell so that I don't have to re-add the selection options to 3 cells of every single row?
3) I would like each of the rarity cells and the EP cell for each row to be editable by the human, having the new value saved for calculations elsewhere. My idea for this would be to copy the cruData object to a cruComp object that is used for computations, then have that get overwritten by player input; that way I can add a "reset" button on the table page to fix everything back after user remorse kicks in :)
4) I want to color row blocks in the table that correspond to something in the game I'm recording data for. Right now I need every 5 rows to be blocked together in a color after the heading, but that 5 may universally change to another number. Is there a way to set a dynamic variable for this so that I can change one number instead of a bunch? Also in my attempt below it works for the first like 50-70 rows and then starts having all kinds of weird behavior, but I can't figure out why?
[ Solved, found the thead tbody tags which was the problem ]
5) Not a real problem but, is there a way to just have x rows get created without each one being created by hand? The only wrinkle is that the ID numbers in the example given below will have all the numbers 1-103+ but not in that order. I figured I could do some sort of vector that has the numbers in the correct order and then pull the consecutive numbers from that vector in order to number them in order, but I don't know if you can create rows using a FOR loop.
Thanks for the help!
My Attempt: Warning, I'm sure this will make anyone that knows what they are doing die inside, as I keep saying I really have little to no idea what I'm doing in this language. Hence me starting with a silly project to learn by doing :)
<!DOCTYPE html>
<html>
<head>
<title>COTLI Calculator and Ref</title>
<!-- <link rel="stylesheet" href="StyleRef.css"> -->
<script>
function Load_Data() {
var i = 0;
/*
<td>The Bush Whacker</td>
<td id="CrEP_1">Blank</td>
<td id="CrGN1_1">Blank</td>
<td id="CrGR1_1">Blank</td>
<td id="CrGN2_1">Blank</td>
<td id="CrGR2_1">Blank</td>
<td id="CrGN3_1">Blank</td>
<td id="CrGR3_1">Blank</td>
<td id="CrUnLock_1">Blank</td>
*/
for (i = 1; i < lootTable.hero.length; i++) {
"crEP_"+i = cruData.hero[i].EP;
};
}
window.onload = Load_Data;
</script>
</head>
<body style="background-color:lightgrey; text-align:center;">
<style>
<!-- Below is the color coding for the rows broken into batches of 4 bench slots at a time. The offset value is because of how the rows are counted and the "header" row. -->
<!-- tr:nth-child(35n+1) {background-color: #4682b4;} -->
<!-- tr:nth-child(21n+1) {background-color: #e36f8a;} -->
tr:nth-child(20n+2) {background-color: #4682b4;}
tr:nth-child(20n+3) {background-color: #4682b4;}
tr:nth-child(20n+4) {background-color: #4682b4;}
tr:nth-child(20n+5) {background-color: #4682b4;}
tr:nth-child(20n+6) {background-color: #4682b4;}
tr:nth-child(20n+7) {background-color: #3abda0;}
tr:nth-child(20n+8) {background-color: #3abda0;}
tr:nth-child(20n+9) {background-color: #3abda0;}
tr:nth-child(20n+10) {background-color: #3abda0;}
tr:nth-child(20n+11) {background-color: #3abda0;}
tr:nth-child(20n+12) {background-color: #e09e87;}
tr:nth-child(20n+13) {background-color: #e09e87;}
tr:nth-child(20n+14) {background-color: #e09e87;}
tr:nth-child(20n+15) {background-color: #e09e87;}
tr:nth-child(20n+16) {background-color: #e09e87;}
tr:nth-child(20n+17) {background-color: #93b881;}
tr:nth-child(20n+18) {background-color: #93b881;}
tr:nth-child(20n+19) {background-color: #93b881;}
tr:nth-child(20n) {background-color: #93b881;}
tr:nth-child(20n+1) {background-color: #93b881;}
table {
border-collapse: collapse;
border: 5px solid black;
tex-align:center;
}
th {
padding: 20px;
border: solid black 3px;
}
td {
padding: 20px;
border: solid black 1px;
}
td.rarity {
<select>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
}
</style>
<h1>
Crusader Table
</h1>
<table align="center">
<tr style="background-color:#e36f8a; text-align:center;">
<th>Crusader Name</th>
<th>EP</th>
<th>Gear 1 Name</th>
<th>Gear 1 Rarity</th>
<th>Gear 2 Name</th>
<th>Gear 2 Rarity</th>
<th>Gear 3 Name</th>
<th>Gear 3 Rarity</th>
<th>Unlocked</th>
</tr>
<!-- Below is the master table for Crusaders. Tags are: CrEP_# for EP, CrGN1_# for Gear 1 name, CrGR1_# for Gear 1 Rarity (similarly gear 2 and 3) and CrUnlock_# for unlock, where # is Crusader ID. -->
<!-- Bench One
The Bush Whacker - 1
RoboRabbit - 38
Graham The Driver - 66
Warwick the Warlock - 75
Slisiblyp, the Alien Scientist - 89
-->
<tr>
<td>The Bush Whacker</td>
<td id="CrEP_1" value=CruData.hero[1].EP> </td>
<td id="CrGN1_1">Blank</td>
<td class="rarity" id="CrGR1_1"></td>
<td id="CrGN2_1">Blank</td>
<td id="CrGR2_1">Blank</td>
<td id="CrGN3_1">Blank</td>
<td id="CrGR3_1">Blank</td>
<td id="CrUnLock_1">Blank</td>
</tr>
Without doing it for you - here are the angles that I would solve these problems
You can cache it locally to a cookie as a string of the literal HTML, but the process really isn't that expensive. If the length is a concern - maybe think about breaking it down into rendering the first X amount of elements.
Create a class for the cell. <td class='dropdown-cell'> [your data] </td> and then in your css: .dropdown-cell{[your css rules} will format only the table cells that have the class dropdown-cell. edit: CSS classes work the same way they would if it was a <td class="custom-class"> or <select class="custom-class"> or <option class="custom-class> whatever element you put it on will inherit that styling as specified by .custom-class in the CSS.
.dropdown{
height: 50px;
width: 200px;
background-color: gray;
}
.dropdown-option{
background-color: lightgray;
}
<select class="dropdown">
<option class="dropdown-option">A</option>
<option class="dropdown-option">B</option>
<option class="dropdown-option">C</option>
</select>
If I'm understanding it right, if you want these edits to be bound to some object. You should create a class for each row, and then run a function onChange or onClick or onKeyup of these editable events and modify the related Object.
class SomeObject{
constructor(id, name, someVal){
this.id = id;
this.name = name;
this.someVal = someVal;
}
}
var numberOfObjects = 5;
var yourObjectsArray = [];
//Create i number of objects and add them to an array of your row objects
//This would be done by looping through your JSON
for(var i = 0; i < numberOfObjects; i++){
yourObjectsArray.push(new SomeObject(i, "Object " + i, "Some Value"));
}
//Build your table
for(var i = 0; i < yourObjectsArray.length; i++){
//The syntax below is Jquery - I suggest using it for event bindings and DOM manipulation
$('#output-table').append('<tr><td class="id">'+ yourObjectsArray[i].id +'</td><td>'+ yourObjectsArray[i].name +'</td><td class="change-val">'+ yourObjectsArray[i].someVal +'</td></tr>');
}
//Bind an event to a click or any jquery event handler
$(document).on('click', '.change-val', function(){
//Get the ID of the row that you clicked
var id = $(this).closest('tr').find('.id').text(); //use value or something else
//Modify the text in the table
var newVal = "New Value";
$(this).text(newVal);
//Parse the array of objects to find the one you need to modify
for(var i = 0; i < yourObjectsArray.length; i++){
if(yourObjectsArray[i].id == id){
yourObjectsArray[i].someVal = newVal;
}
}
//Prove that your object value changed not just the text
$('#output').html("<br>");//clear the output
for(var i = 0; i < yourObjectsArray.length; i++){
$('#output').append('ID: ' + yourObjectsArray[i].id + " Value: " + yourObjectsArray[i].someVal + "<br>");
}
});
table {
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click on a value cell below to change it
<table id="output-table">
<thead>
<tr>
<td>ID</td>
<td>Name</td>
<td>Value</td>
</tr>
</thead>
</table>
<div id="output"></div>
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName add a class to each row and then in a for loop, add conditional formatting for row[i] modulus 5.
You can create rows using a for loop, you just have to give the Javascript the HTML formatting and then use an append to add it to the end https://www.w3schools.com/jsref/met_node_appendchild.asp
I'd recommend Jquery for your DOM manipulation as it makes drilling down to specific ID's, classes, and elements very easy.

Very slow reflow/layout in Internet Explorer

I have to build a table with +450 row / +90 columns dynamically and show it in Internet Explorer. I've been heavily optimizing for the last two days, and ended up creating a collection of table rows as a very long string and assigning it to the innerHTML of the tBody of the table.
it works just fine in Chrome/Mozilla, a reflow takes about .2 seconds, but it's very slow in Internet Explorer. it takes about 4 seconds to render (i say "about" because if the console is open (for time measurement) it takes 19 seconds to render). Another problem is that innerHTML is not even supported in IE9 and below. So the question is: What's the fastest way to render a whole lot of HTML as fast as possible in IE9?
HTML Sample:
<tr class="data-row" ><td class="hidden" style="width: -21px; padding:
10px;">"1"</td><tdclass="structureCatagory" style="width: 119px; padding:
10px;">"0381"</td><td class="structureCatagory" style="width: 139px;
padding: 10px;">"Some text"</td><td class="structureCatagory"
style="width: 139px; padding: 0px;"><img src="/Content/Images/info.png"
onclick="Interface.OnImageClicked($(this))" ></td>...
And so forth for a total of 4178521 characters.
Javascript:
function Update() {
var displayData = Model.GetData();
if (displayData == undefined || displayData.length == 0)
return false;
var rows = "", len = displayData.length;
for (var i = 0; i < len; i++) rows += GetRow(displayData[i]);
//until here it's very fast
GlobalQueries.dataTableBody[0].innerHTML = rows;
//^ this line takes alot of time
return true;
}
Thanks in advance!
Edit: The table itself:
<div class="grid">
<table class="fixed">
<tbody></tbody>
</table>
</div>
<style>
.grid { margin-top: 240px; margin-left: 10px; }
.grid td, .header-row td { border: 1px solid black; }
table.fixed { table-layout: fixed; }
</style>
I think only lazy rendering will help you here. This way you will reduce the amount of HTML nodes on the page and make it lighter. You don't need to render rows that are not visible on the screen...
There are examples with jQuery, React, Polymer ... and so on.
To make the paint look faster you can batch the inserts and not insert the entire table at once but in chunks with methods like requestAnimationFrame.

Implementing dynamic selectors and showing up corresponding image

I'm trying to implement a functionality:
Show list of items on the left
Dynamic selector loop each item (dashed rectangle in the image below)
On the right it should show an image for an item that has selector on it
Items' names and images will be pulled out from a storage
Question: Is this something that can be implemented with jQuery or something else?
It'd helpful if could recommend any related resources.
You can make use of this code. Replace the id's and class names with your ones.
var list = ["cat","dog","elephant","lion"];
var listImg = ["cat","dog","elephant","lion"];
createListPanel();
function createListPanel()
{
var parent = $("#displayPanel").find("td").eq(0);
for(var i=0;i<list.length;i++)
{
parent.append("<div class='list' id="+i+">"+list[i]+"</div>");
}
$(".list").click(function(){
$("#detailDisplay").html(listImg[this.id]);
});
}
.list:hover{
background-color: #ffcccc;
}
.list{
background-color: #ffe6e6;
width:150px;
}
#displayPanel
{
width: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id = "displayPanel">
<table width="100%">
<tr><td ></td><td><div id="detailDisplay"></div></td></tr>
</table>
</div>
You can replace the text in the ListImg with your image url

Trouble with Symfony/Twig: trying to dynamically add rows via an Add Row button, using Javascript

G'day all, I've been trying to do this for a while, with no joy yet.
In a nutshell: our db has up to 10 rows for a table, with each row having 4 columns. I can display them nicely using a simple HTML table. That's all good.
If I display them all, then users can enter date for none/some/all of them, the db gets updates, and it's all good.
But many users will only need a few rows, so I want to hide all but the first, and offer a simple Add Row button. This row should change each hidden row from Display: none; to Display: inline.
Except I can't figure out how to do it! I've tried all sorts of combinations.
e.g. having an onload() function that renders each later row invisible via Display: none.
Also: setting initial CSS the same way: Display: none.
Then having the Add Row button set the ID for those rows to be Display: inline.
I'm not sure if it's worth posting code, as I suspect there's a really simple solution I'm not remotely aware of.
Anybody got any clues?
Many thanks, much appreciated!
With javascript:
$("#showNext").click(function() {
var hiddenElement = $("#rows").children(".row:hidden").first();
hiddenElement.show();
});
.row {
width: 100%;
height: 50px;
background-color: #29abe1;
margin: 5px;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button id="showNext">
Show next row.
</button>
<div id="rows">
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
</div>
Note: .children() only travels a single level down the DOM tree.
By using javascript and an ajax request
function AddRow()
{
//The current count of the rows (stored in html as hidden)
var rowCount = parseInt(document.getElementById("rowCount").value);
var NextRowIndex= rowCount+1;
//updating the rowcount value in html page
document.getElementById('rowCount').value = NextRowIndex;
//Ajax function which retuns a the required row
$.ajax({
//requesting to get the required row as response
url:urlprefix+"add_row/"+NextRowIndex,
beforeSend: function(){ enableLoader(); },
success: function(data)
{
$("#addFileRow"+rowCount).after("<div id='addFileRow"+NextRowIndex+"' class='sec-container' style='padding-bottom: 18px'>"+
"</div>");
disableLoader();
$("#addFileRow"+NextRowIndex).append(data);
},
error: function(data)
{
alert(data);
}
});
};

How can I use enter/br/ or \n in getElementById("ErrorTable").innerHTML?

Hello guys I might need you help with something I can not figure it out.
I have a registeration form and recently I added a red table for the errors in the filling inputs, but now I have a new problem and all the errors are in one row and I don't know how can I do "enter" to it and get it down so it will be in a list and not one by one right to left.
document.getElementById("ErrorTable").innerHTML += '•Agree with the terms';
and this is the table HTML:
<center>
<div class="RegisterTextArea">
<table id="ErrorTable">
</table>
</div>
</center>
I have a new problem Here: http://prntscr.com/7bxe6k.
How to clear the UL so it doesn't submit again like the picture and how to fit it in the box so it will split left when there is no space.
HTML:
<center>
<div class="errors">
<ul id="ErrorTable"></ul>
</div>
</center>
style:
.errors
{
background-color: #FFB6B6;
border: groove 1px #A81B1B;
width: 500px;
height: 50px;
text-align: right;
font-size: 10px;
color: #A81B1B;
}
.errors ul li
{
}
I think you can use Unordered Html List instead of Table. I've updated your code as below
HTML
<center>
<div class="RegisterTextArea">
<ul id="ErrorTable">
</ul>
</div>
</center>
Java Script
document.getElementById("ErrorTable").innerHTML = '';
document.getElementById("ErrorTable").innerHTML += '<li>Agree with the terms</li>';
Hope you are expected such a result.
If you want a list, then you need to use a list and not a table.
<ul id="errors">
</ul>
You can't put text directly in a list (or in a table); you have to wrap it in appropriate markup. In the case of a list, that is a list item.
function add_error( error_message ) {
var list_item = document.createElement('li');
var text = document.createTextNode(error_message);
list_item.appendChild(text);
document.getElementByid('errors').appendChild(list_item);
}
add_error("Agree with the terms");
The default rendering of an unordered list puts each list item on a new line and gives it a bullet point.

Categories