Export to csv in jQuery - javascript

I am dynamically generating a div which is like :
<div id='PrintDiv'>
<table id="mainTable">
<tr>
<td>
Col1
</td>
<td>
Col2
</td>
<td>
Col3
</td>
</tr>
<tr>
<td>
Val1
</td>
<td>
Val2
</td>
<td>
Val3
</td>
</tr>
<tr>
<td>
Val11
</td>
<td>
Val22
</td>
<td>
Val33
</td>
</tr>
<tr>
<td>
Val111
</td>
<td>
Val222
</td>
<td>
Val333
</td>
</tr>
</table>
</div>
And there are lot more elements on the page as well.
Now, how can i get a csv file like this :
Col1,Col2,Col3
Val1,Val2,Val3
Val11,Val22,Val33
Val111,Val222,Val333
using jQuery ?
need a file save dailog box too,like this :
Thanks.

You can do that in the client side only, in browser that accept Data URIs:
data:application/csv;charset=utf-8,content_encoded_as_url
In your example the Data URI must be:
data:application/csv;charset=utf-8,Col1%2CCol2%2CCol3%0AVal1%2CVal2%2CVal3%0AVal11%2CVal22%2CVal33%0AVal111%2CVal222%2CVal333
You can call this URI by:
using window.open
or setting the window.location
or by the href of an anchor
by adding the download attribute it will work in chrome, still have to test in IE.
To test, simply copy the URIs above and paste in your browser address bar. Or test the anchor below in a HTML page:
<a download="somedata.csv" href="data:application/csv;charset=utf-8,Col1%2CCol2%2CCol3%0AVal1%2CVal2%2CVal3%0AVal11%2CVal22%2CVal33%0AVal111%2CVal222%2CVal333">Example</a>
To create the content, getting the values from the table, you can use table2CSV and do:
var data = $table.table2CSV({delivery:'value'});
$('<a></a>')
.attr('id','downloadFile')
.attr('href','data:text/csv;charset=utf8,' + encodeURIComponent(data))
.attr('download','filename.csv')
.appendTo('body');
$('#downloadFile').ready(function() {
$('#downloadFile').get(0).click();
});
Most, if not all, versions of IE don't support navigation to a data link, so a hack must be implemented, often with an iframe. Using an iFrame combined with document.execCommand('SaveAs'..), you can get similar behavior on most currently used versions of IE.

This is my implementation (based in: https://gist.github.com/3782074):
Usage:
HTML:
<table class="download">...</table>
DOWNLOAD CSV
JS:
$("a[download]").click(function(){
$("table.download").toCSV(this);
});
Code:
jQuery.fn.toCSV = function(link) {
var $link = $(link);
var data = $(this).first(); //Only one table
var csvData = [];
var tmpArr = [];
var tmpStr = '';
data.find("tr").each(function() {
if($(this).find("th").length) {
$(this).find("th").each(function() {
tmpStr = $(this).text().replace(/"/g, '""');
tmpArr.push('"' + tmpStr + '"');
});
csvData.push(tmpArr);
} else {
tmpArr = [];
$(this).find("td").each(function() {
if($(this).text().match(/^-{0,1}\d*\.{0,1}\d+$/)) {
tmpArr.push(parseFloat($(this).text()));
} else {
tmpStr = $(this).text().replace(/"/g, '""');
tmpArr.push('"' + tmpStr + '"');
}
});
csvData.push(tmpArr.join(','));
}
});
var output = csvData.join('\n');
var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(output);
$link.attr("href", uri);
}
Notes:
It uses "th" tags for headings. If they are not present, they are not
added.
This code detects numbers in the format: -####.## (You will need modify the code in order to accept other formats, e.g. using commas).
UPDATE:
My previous implementation worked fine but it didn't set the csv filename. The code was modified to use a filename but it requires an < a > element. It seems that you can't dynamically generate the < a > element and fire the "click" event (perhaps security reasons?).
DEMO
http://jsfiddle.net/nLj74t0f/
(Unfortunately jsfiddle fails to generate the file and instead it throws an error: 'please use POST request', don't let that error stop you from testing this code in your application).

I recently posted a free software library for this: "html5csv.js" -- GitHub
It is intended to help streamline the creation of small simulator apps
in Javascript that might need to import or export csv files, manipulate, display, edit
the data, perform various mathematical procedures like fitting, etc.
After loading "html5csv.js" the problem of scanning a table and creating a CSV is a one-liner:
CSV.begin('#PrintDiv').download('MyData.csv').go();
Here is a JSFiddle demo of your example with this code.
Internally, for Firefox/Chrome this is a data URL oriented solution, similar to that proposed by #italo, #lepe, and #adeneo (on another question). For IE
The CSV.begin() call sets up the system to read the data into an internal array. That fetch then occurs. Then the .download() generates a data URL link internally and clicks it with a link-clicker. This pushes a file to the end user.
According to caniuse IE10 doesn't support <a download=...>. So for IE my library calls navigator.msSaveBlob() internally, as suggested by #Manu Sharma

Here are two WORKAROUNDS to the problem of triggering downloads from the client only. In later browsers you should look at "blob"
1. Drag and drop the table
Did you know you can simply DRAG your table into excel?
Here is how to select the table to either cut and past or drag
Select a complete table with Javascript (to be copied to clipboard)
2. create a popup page from your div
Although it will not produce a save dialog, if the resulting popup is saved with extension .csv, it will be treated correctly by Excel.
The string could be
w.document.write("row1.1\trow1.2\trow1.3\nrow2.1\trow2.2\trow2.3");
e.g. tab-delimited with a linefeed for the lines.
There are plugins that will create the string for you - such as http://plugins.jquery.com/project/table2csv
var w = window.open('','csvWindow'); // popup, may be blocked though
// the following line does not actually do anything interesting with the
// parameter given in current browsers, but really should have.
// Maybe in some browser it will. It does not hurt anyway to give the mime type
w.document.open("text/csv");
w.document.write(csvstring); // the csv string from for example a jquery plugin
w.document.close();
DISCLAIMER: These are workarounds, and does not fully answer the question which currently has the answer for most browser: not possible on the client only

By using just jQuery, you cannot avoid a server call.
However, to achieve this result, I'm using Downloadify, which lets me save files without having to make another server call. Doing this reduces server load and makes a good user experience.
To get a proper CSV you just have to take out all the unnecessary tags and put a ',' between the data.

You can't avoid a server call here, JavaScript simply cannot (for security reasons) save a file to the user's file system. You'll have to submit your data to the server and have it send the .csv as a link or an attachment directly.
HTML5 has some ability to do this (though saving really isn't specified - just a use case, you can read the file if you want), but there's no cross-browser solution in place now.

Hope the following demo can help you out.
$(function() {
$("button").on('click', function() {
var data = "";
var tableData = [];
var rows = $("table tr");
rows.each(function(index, row) {
var rowData = [];
$(row).find("th, td").each(function(index, column) {
rowData.push(column.innerText);
});
tableData.push(rowData.join(","));
});
data += tableData.join("\n");
$(document.body).append('<a id="download-link" download="data.csv" href=' + URL.createObjectURL(new Blob([data], {
type: "text/csv"
})) + '/>');
$('#download-link')[0].click();
$('#download-link').remove();
});
});
table {
border-collapse: collapse;
}
td,
th {
border: 1px solid #aaa;
padding: 0.5rem;
text-align: left;
}
td {
font-size: 0.875rem;
}
.btn-group {
padding: 1rem 0;
}
button {
background-color: #fff;
border: 1px solid #000;
margin-top: 0.5rem;
border-radius: 3px;
padding: 0.5rem 1rem;
font-size: 1rem;
}
button:hover {
cursor: pointer;
background-color: #000;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='PrintDiv'>
<table id="mainTable">
<tr>
<td>Col1</td>
<td>Col2</td>
<td>Col3</td>
</tr>
<tr>
<td>Val1</td>
<td>Val2</td>
<td>Val3</td>
</tr>
<tr>
<td>Val11</td>
<td>Val22</td>
<td>Val33</td>
</tr>
<tr>
<td>Val111</td>
<td>Val222</td>
<td>Val333</td>
</tr>
</table>
</div>
<div class="btn-group">
<button>csv</button>
</div>

Just try the following coding...very simple to generate CSV with the values of HTML Tables. No browser issues will come
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="http://www.csvscript.com/dev/html5csv.js"></script>
<script>
$(document).ready(function() {
$('table').each(function() {
var $table = $(this);
var $button = $("<button type='button'>");
$button.text("Export to CSV");
$button.insertAfter($table);
$button.click(function() {
CSV.begin('table').download('Export.csv').go();
});
});
})
</script>
</head>
<body>
<div id='PrintDiv'>
<table style="width:100%">
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>80</td>
</tr>
</table>
</div>
</body>
</html>

Related

Create a function that follows a moving text on a table

I found a function to move data between table cells but the functions never seem to stick to whatever tag is "attached" to the cell itself. I'm not sure if I was using ids wrong. I need help finding a way to "attach" a function to a tag that moves between cells.
Can you help me create a button to move a tag (unit 1) upwards and downwards through a table such that it stops at the end of the table?
Original code attached here
//Send to the "bottom"
function sendOS() {
var node = document.getElementById("r1c1").lastChild;
document.getElementById("r1c3").appendChild(node);
/*
var node = document.getElementById("r1c3").lastChild;
document.getElementById("r1c2").appendChild(node);
*/
}
//Send to the "top"
function sendTop() {
var node = document.getElementById("r1c2").lastChild;
document.getElementById("r1c1").appendChild(node);
}
table,
th,
td {
border: 1px solid black;
width: 32px;
height: 32px;
}
<table>
<tr id="row1">
<td id="r1c1">Unit1</th>
<td id="r1c2">Unit2</th>
<td id="r1c3">Unit3</th>
</tr>
<tr id="row2">
<td id="r2c1">r1c2</td>
<td id="r2c2">r2c2</td>
<td id="r2c2">r2c3</td>
</tr>
<tr id="row3">
<td id="r2c2">r3c1</td>
<td id="r2c2">r3c2</td>
<td id="r2c2">r3c3</td>
</tr>
</table>
<!--Table ends -->
<!-------------------------------------------------------------->
<button onclick="sendOS()">move to the other side</button>
<button onclick="sendTop()">move to the right</button>
I can't help you with creating all that. You should try it out for yourself, you'll learn a lot more.
But I can help you with the code you provided.
A lot of your id attributes are the same. Every id on the page should be unique.
Change your HTML structure slightly by adding a <span> around the texts inside your cells (<td><span id="target-1">Text</span></td>). That way you can select elements inside your cells and move those around. Using lastChild to get the TextNode is not the right approach.
The functions should check if there is a cell next to it in the direction you want to move the text. If there is, move the text. If not, then do nothing.
Below I've made a small demonstration on how this might work. Here target is the element that we move. It's the <span id="target">Foo</span> element in the HTML.
When clicking either button, the code will go one element up from the target element with parentElement, that will be the <td> our target is in.
It then tries to access the previous or next <td> in the row (depending on the direction). If a neighbouring <td> is found, it will append the element.
The advantage of this approach is that you always have a reference to the element that you are moving.
const target = document.querySelector('#target');
const buttonLeft = document.querySelector('.js-target-move-left');
const buttonRight = document.querySelector('.js-target-move-right');
function targetMoveLeft() {
const previousCell = target.parentElement.previousElementSibling;
if (previousCell) {
previousCell.append(target);
}
}
function targetMoveRight() {
const nextCell = target.parentElement.nextElementSibling;
if (nextCell) {
nextCell.append(target);
}
}
buttonLeft.addEventListener('click', targetMoveLeft);
buttonRight.addEventListener('click', targetMoveRight);
<table>
<tbody>
<tr>
<td><span id="target">Foo</span></td>
<td><span>Bar</span></td>
<td><span>Baz</span></td>
</tbody>
</table>
<button class="js-target-move-left">Left</button>
<button class="js-target-move-right">Right</button>
I hope this will at least push you in the right direction. Good luck with implementing the other features.

Dynamically loading content in c# asp.net mvc 4 using javascript not working properly

I'm working on a website where the owner has exhibitions which he can add, edit and delete. The exhibitions are showed per year, once clicking on the 'Read more' button. I'm running into a weird problem with my pages, since I'm loading the content in a new div once clicking on 'Read more' and I need an unique identifier for this div. I'm using id="#Year here" to identify this div, however I run into this problem:
As you can see, it is not loading properly. I have the following code to load in all exhibitions per group:
#if(Model.Exhibitions.Count() > 0)
{
<h3 style="text-align: left; margin-left: 10px; padding-top: 10px;">#Model.Year</h3>
<table class="extable">
<tr>
<th style="width: 15%">#Translations.EXHIBITIONSTARTDATE</th>
<th style="width: 15%">#Translations.EXHIBITIONENDDATE</th>
<th style="width: 15%">#Translations.EXHIBITIONEVENT</th>
<th style="width: 40%">#Translations.EXHIBITIONDESCRIPTION</th>
<th style="width: 15%">#Translations.GALLERY</th>
</tr>
#foreach (Exhibition item in Model.Exhibitions)
{
#Html.Partial("_Exhibition", item)
}
</table>
}
<div class="more-exhi" id="#Model.Next"></div>
<script type="text/javascript">
(function () {
$('.more-exhi##Model.Year').attr('class', '');
$('.show-more').attr('data-year', '#Model.Next');
}) ();
</script>
#Model.Year is the year being send to the controller (for example, 2014). #Model.Next is the next existing year with exhibitions in descending order (for example, 2010). However the code is above is not working right, and I am not sure why. This is my javascript once someone clicks on the button 'Read more':
<script type="text/javascript">
(function () {
var div = $('#exhibitions');
div.find('.show-more').click(function () {
div.find('.more-exhi').hide().load('#Url.Action("Exhibitions", "Exhibition")', { year: $(this).attr('data-year') } ).fadeIn('1500');
div.find('.show-less').show();
});
})();
</script>
Could someone help me fixing this problem? I think I'm overseeing a problem here, as this shouldn't be too difficult.
Try to change this line of code into:
$('.more-exhi#' + #Model.Year).attr('class', '');
:)

Get select table contents using Greasemonkey

I'm trying to retrieve the contents of a table, from a site, using Greasemonkey script.
Here's how this table looks:
<table class="OCalaisBox">
<tr class="OCalaisHeadRow">
<td colspan="4" class="OCalaisTitleBar">Topics in this article</td>
</tr>
<tr>
<td class="OCalaisList indexNormalText">
<div class="OCalaisHeader">Country</div>
<ul>
<li>Nigeria</li>
<li>Bosnia and Herzegovina</li>
</ul>
</td>
</tr>
</table>
I want to retrieve, say, "Nigeria" and "Bosnia and Herzegovina".
Editor's note: Here's a live page with this structure.
Since you want a list of countries in the article, your easiest bet is to key off of the link hrefs provided. Other answers' approaches will work for your sample HTML, but fetch a whole lot of other stuff on the actual page (sample).
The simplest (readable) code:
//--- Get the country links:
var cntryLinks = document.querySelectorAll ("a[href^='/category/country/']");
//--- Extract the country names:
var nameList = [].map.call (cntryLinks, function (cLink) {
return cLink.textContent;
} );
//--- Convert to text:
var countryStr = nameList.join (", ");
//--- Display:
console.log ("Countries: " + countryStr);
alert ("Countries: " + countryStr);
Keep in mind that that site only puts this information up on (most) news articles, NOT all pages.
You could find all elements you look for by their CSS selectors. In this given case the following oneliner would do the job:
array = ( [].map.call(document.querySelectorAll('.OCalaisList>ul>li>a'), function(item) { return item.textContent; }));
// output the array to console (press ctrl+shift+k in Firefox)
console.log(array);
map() is a method of Array.prototype. The returned result of querySelectorAll() method is iteratable but actually not an array and doesn't have this method on it's own. map() will work as a method of the result, if you put it in context with the call() method.
querySelectorAll() returns a NodeList of all CSS selector matches you specify.
map() calls a callback function on each element of the input array. The callback returns a new value and map() returns a new array of all the new values.
test case:
<!DOCTYPE html>
<html>
<head>
<title>find contents test case</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<table class="OCalaisBox" cellpadding="0" cellspacing="0">
<tr class="OCalaisHeadRow">
<td colspan="4" class="OCalaisTitleBar">Topics in this article</td>
</tr>
<tr>
<td class="OCalaisList indexNormalText" valign=top width="25%" >
<div class="OCalaisHeader">Country</div>
<ul >
<li>Nigeria</li>
<li>Bosnia and Herzegovina</li>
</ul>
</td>
</tr>
</table>
</body>
<script>
array = ( [].map.call(document.querySelectorAll('.OCalaisList>ul>li>a'), function(item) { return item.textContent; }));
console.log(array);
</script>
</html>

Appending a table to another table in the DOM. Targeting with no class or id available

I am currently attempting to append a specific , via jquery, to another table. Here's the HTML, and the two elements involved in the move.
<div id="content_area">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr><td></td></tr>
<tr>
<td></td> <-- TD needing to move -->
</tr>
</tbody>
</table> <-- Needs to move-->
<table width="100%" cellspacing="0" cellpadding="5" border="0">
<tbody>
<tr>
<td width="190" valign="top">
<table width="100%"></table>
<-- Move Above TD Here -->
</td>
</tr>
</tbody>
</table>
</div>
Although I'm hardly experienced with jquery/javascript, I have used the following method in the past to append a div to another div.
$(".target").appendTo($(".destination"));
This method I have used in the past required that the elements have some sort of unique identification. Since this is not possible with the current site I am developing (The software has locked down the HTML), how can I target these two tables in order to make the append?
You can view the issue at the following page:
http://xlevj.jyetp.servertrust.com/Pro-Audio-Equipment-s/1824.htm
It's pretty obvious to see on that page what I'm trying to accomplish with the move. Thanks for any help!
Try this:
//Find the td we want to move.
var tdToMove = $('#divWaitModal + table > tbody > tr:nth-child(2) td');
//Find the td we want to insert into after.
var tdToInsertAfter = $('#divWaitModal + table + table tr:first-child td:first-child');
//Detach the td to move.
tdToMove.detach();
//Insert it at the proper place.
tdToInsertAfter.after(tdToMove);
Just use child number of the node and target trough that :
$('body table:first-child').appendTo( $('table:eq(1) td:eq(0)') );
In words it takes the first table and it's appending it to second table > first cell. You can use :eq( number ) where number starts from 0, or first-child selector in some cases ..
This CSS might accomplish what you're after:
#content_area {
overflow: hidden;
}
#content_area table {
display: inline;
float: left;
}
If you want to target the elements, you can use the #content_area as a selector:
var $tables = $('#content_area>table');
var $table1 = $(tables[0]);
var $table2 = $(tables[1]);

Hide JS element without div or class

I am trying to hide the toolbar of a SSRS report.
There is a specific reason why I need to use JS( The report will be included in the CRM 2011 Dashboard, and I wanted to remove the toolbar from the Report. Since the report parameters did not work, I imported Report Control solution and I am editing the viewer, which uses JS ). The viewer is a Html page that embeds the Report as an IFrame.
The generated Html code is:
<table id="reportViewer_fixedTable" cellspacing="0" cellpadding="0" style="table-layout:fixed;width:100%;height:100%;">
<tbody>
<tr style="background-color:#C4DDFF;"> … </tr>
<tr id="ParametersRowreportViewer" style="display:none;"> … </tr>
<tr style="height:6px;font-size:2pt;display:none;"> … </tr>
<tr>
The toolbar is in the 4th tr, and selecting it directly and trying to hide it did not work.
navCorrectorDiv = report.contentWindow.document.getElementById('reportViewer_Toolbar');
if (navCorrectorDiv != null) {
navCorrectorDiv.style.display = "none";
}
I should select the table reportViewer_fixedTable, that I can do, then select the tbody element and then the fourth tr.
Is there a way to do it? Possibily without jQuery.
Case: No Iframe
Select the element
As jQuery selector:
var selected;
selected = jQuery('#reportViewer_fixedTable');
…
selected = jQuery('#reportViewer_fixedTable tbody');
…
selected = jQuery('#reportViewer_fixedTable tr:nth-child(4)');
Hide selected with:
selected.css('display', 'none');
or with modern browsers without jQuery:
var selected;
selected = document.querySelector('#reportViewer_fixedTable');
…
selected = document.querySelector('#reportViewer_fixedTable tbody');
…
selected = document.querySelector('#reportViewer_fixedTable tr:nth-child(4)');
And hide:
selected.style.display = 'none';
Case: Content in Iframe
The iframe can be problematic, because it might be sandboxed or the content might come from a different domain. This can lead into a XSS-violation which, in your case, might be unfixable.
Anyway, here we go:
//Select the first iframe (which might not be the right one in your case);
var elem = document.querySelector('iframe');
//And put it's body in a variable. We use the querySelector from the body
//of the iframe.
var ibody = elem.contentWindow.document.body;
var table = ibody.querySelector('#reportViewer_fixedTable');
var tbody = ibody.querySelector('#reportViewer_fixedTable tbody');
var fourthtr = ibody.querySelector('#reportViewer_fixedTable tr:nth-child(4)');
table.style.display = 'none';
tbody.style.display = 'none';
fourthtr.style.display = 'none';
I guess you can do it by trying to find the nth Chid
Consider this approach :
HTML :
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
<table id="reportViewer_fixedTable" cellspacing="0" cellpadding="0" style="table-layout:fixed;width:100%;height:100%;">
<tbody>
<tr style="background-color:#C4DDFF;"> <td>1</td> </tr>
<tr id="ParametersRowreportViewer" style="display:none;"><td>2</td> </tr>
<tr style="height:6px;font-size:2pt;display:none;"> <td>3</td> </tr>
<tr><td>FourthTR</td></tr>
</tbody>
</table>
</body>
</html>
JS:
$(function(){
console.log( $('#reportViewer_fixedTable tbody tr:nth-child(4) td:nth-child(1)').text());
$('#reportViewer_fixedTable tbody tr:nth-child(4) td:nth-child(1)').addClass('FourthTR');
$('.FourthTR').hide();
});
So , what we are trying to do is that, we are grabbing the 4th tr of the table and then we are grabbing the 1st child of the 4th tr. Once that is done, we are , on the fly, gonna add a class to it say FourthTR and then hide the class using jQuery.hide(). Voila, you are done.
See the working example here: http://jsbin.com/ACam/1/edit . As always, remember to run with js .
I don't think you need to use JavaScript for this
If you have access to ReportControl solution and server-side code of ReportViewer.aspx.cs, you can set property
reportViewer.ShowToolBar = false
in that code.
Alternatively, if you have access to and can modify viewer page markup (ReportViewer.aspx), you can set it declaratively: by adding ShowToolBar="false" to ReportViewer control declaration:
<rsweb:ReportViewer ID="reportViewer" runat="server" ... ShowToolBar="false">
</rsweb:ReportViewer>
If this is not an option, you can amend URL you're passing to IFrame hosting ReportViewer, by adding rc:Toolbar=false parameter
http://localhost/ReportServer/Pages/ReportViewer.aspx?%2fMyReport%2fBEA&rs:Command=Render&rc:Toolbar=false

Categories