I have been researching all afternoon and playing around with various solutions I have found on the internet and Stack OverFlow to try to keep the gridlines turned on when I export data from my HTML webpage but to no avail. I am really trying to avoid using a plug in and something this simple shouldn't require on in my opinion. I'm actually shocked that this is proving as challenging as it is. Anyway...I found this code....
function exportTableToExcel(tableID, filename = ''){
var downloadLink;
var dataType = 'application/vnd.ms-excel';
var tableSelect = document.getElementById(tableID);
var tableHTML = tableSelect.outerHTML.replace(/ /g, '%20');
// Specify file name
filename = filename?filename+'.xls':'excel_data.xls';
// Create download link element
downloadLink = document.createElement("a");
document.body.appendChild(downloadLink);
if(navigator.msSaveOrOpenBlob){
var blob = new Blob(['\ufeff', tableHTML], {
type: dataType
});
navigator.msSaveOrOpenBlob( blob, filename);
}else{
// Create a link to the file
downloadLink.href = 'data:' + dataType + ', ' + tableHTML;
// Setting the file name
downloadLink.download = filename;
//triggering the function
downloadLink.click();
}
}
And coupled with this HTML.....
<table id="tblData" class="table10">
<tr>
<th class="title36">Description</th>
</tr>
</table>
<button onclick="exportTableToExcel('tblData')"</button>
It all works beautifully! Except when I open the file the gridlines are gone and the user would have to go the view tab and turn the gridlines back on every time. Is there a setting I can change somewhere that will allow this?
The second example in this SO works...but I have a problem whereby I need to use a button and not an input button....for styling purposes...and then in doing so because the solution is written as a var and not a function....I had trouble working it out. So I know what I'm trying to do is possible...I just can't quite figure out how to work out doing this as Javascript without a plugin. I'm fairly new at Javascript so thanks in advance for any pointers...perhaps how I can rewrite the second solution as a function?
This is the code that I found that works....
<script type="text/javascript">
var tableToExcel = (function() {
var uri = 'data:application/vnd.ms-excel;base64,'
, template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--><meta http-equiv="content-type" content="text/plain; charset=UTF-8"/></head><body><table>{table}</table></body></html>'
, base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) }
, format = function(s, c) { return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }) }
return function(table, name) {
if (!table.nodeType) table = document.getElementById(table)
var ctx = {worksheet: name || 'Worksheet', table: table.innerHTML}
window.location.href = uri + base64(format(template, ctx))
}
})()
</script>
But I need to rewrite it so that I can call it in like the first function that is referenced at the beginning of my code above. Thanks again for any pointers or thoughts.
After more research I realized the second piece of code was the answer. I simply needed to update my HTML button reference as follows...
<button type="button" onclick="tableToExcel('tblData',)" class="class"><div class="class1"><h3 class="class2">Export To Excel</h3></div></button>
I currently have a Javascript function which exports my AngularJS table of JSON data to a .csv file by div ID as shown:
$scope.exportToExcel = function (tableId) { // ex: '#my-table'
var exportHref = Excel.tableToExcel(tableId, 'WireWorkbenchDataExport');
$timeout(function () { location.href = exportHref; }, 100); // trigger download
}
Which I call like so
<button style="float: right;" class="btn btn-link" ng-click="exportToExcel('#codeProjectsTable')" filename="test.csv">
<span class="glyphicon glyphicon-share"></span>
Export table
</button>
This works well for data sets with ~400 records or less (20 columns per record), but I'm trying to export nearly 1,000 records for further reporting and I am simply being directed to an about:blank page.
I'm suspecting that it is either
Unable to export this large of a file
Timing out because of a long request
Thanks in advance.
I used following logic for to export
var fileName = 'users_export_' + moment().format('YYYY-MM-DD') + '.csv';
var url = URL.createObjectURL(new Blob([response.data]));
var a = document.createElement('a');
a.href = url;
a.download = fileName;
a.target = '_blank';
a.click();
And its working for large for large file/data
Target: write&download a csv file starting with a json string, for example data.csv containing
col1,col2,col3
"324","19-08-2014","13000"
"325","19-08-2014","5010"
What I have done until now:
1) iframe and button to call my conversion function
<iframe id="frame" style="display:none"></iframe>
<form><input type="submit" value="Export CSV" onclick="javascript:Download();"></form>
2) my Download() function which would want to download my csv file
<script type="text/javascript">
function Download(){
var csv=ConvertToCSV(<?php echo $json_string ?>);
var url='data:application/csv,'+csv;
var _iframe_dl = $('<iframe />')
.attr('src', url)
.hide()
.appendTo('body');
};
</script>
3) my json to csv conversion function which tries to create a csv string
<script type="text/javascript">
function ConvertToCSV(json) {
var array = typeof json != 'object' ? JSON.parse(json) : json;
var str = '';
for (var i = 0; i < array.length; i++) {
var line = '';
for (var index in array[i]) {
if (line != '') line += ','
line += '"'+array[i][index]+'"';
}
str += line + "\r\n";
}
return str;
}
</script>
Encountered problems :
i) it seems that it doesn't recognize \r\n, infact output is just one line
"324","19-08-2014","13000""325","19-08-2014","5010"
ii) I cannot set the filename and the extension, infact the downloaded file is "download" without extension containing the single line mentioned above
First of all, you will need to ensure your data is in this format, like the example below.
var array = [["col1","col2","col3"],["324","19-08-2014","13000"],["324","19-08-2014","13000"]]
then you need to create csv variable as shown below
var csv = "data:text/csv;charset=utf-8,";
after this you need to loop through your data array and append each line to the csv variable you just set.
array.forEach(function(arrayItem, index){
arrayAsString = arrayItem.join(",");
csv += index < array.length ? arrayAsString+ "\n" : arrayAsString;
});
now to give this file a name and create a download link you must create a hidden anchor node and set its download attribute.
var encUri = encodeURI(csv);
var link = document.createElement("a");
link.setAttribute("href", encUri);
link.setAttribute("download", "file_name.csv");
//add anchor element to body
document.body.appendChild(link);
link.click();
EDIT:
Tested on Chrome and is working, also on Safari. Does not work on Firefox for some reason which i will take a look at now
I found out that if you add the link into the body of the page only then will Firefox initiate the download, you can use a code like so. I have updated my code above
document.body.appendChild(link);
My html page contain a embedded image (png) that I like to insert into the excel file dynamically. I tried below that created the excel file with the name I like and a worksheet name as well * though the name of worksheet is same as file name, not sure how to set that, with below code
//creating a temporary HTML link element (they support setting file names)
var a = document.createElement('a');
var postfix = 'AKS'
//getting data from our div that contains the HTML table
var data_type = 'data:application/vnd.ms-excel';
var table_div = document.getElementById('myDynamicImage');
var table_html = table_div.outerHTML.replace(/ /g, '%20');
a.href = data_type + ', ' + table_html;
//setting the file name
a.download = 'exported_table_' + postfix + '.xls';
//triggering the function
a.click();
But the content of image in dataURI format is inserted in excel as a text rather then image. Can I setup this for image. can I control the position etc. can i control the worksheet name, I did that with some other but bit complicated way called tableToExcel.
If we were on a nodeJS server, we could write a header, set a mime type, and send it:
res.header("Content-Disposition", "attachment;filename="+name+".csv");
res.type("text/csv");
res.send(200, csvString);
and because of the headers, the browser will create a download for the named csv file.
When useful data is generated in a browser, one solution to getting it in a CSV file is to use ajax, upload it to the server, (perhaps optionally save it there) and get the server to send it back with these headers to become a csv download back at the browser.
However, I would like a 100% browser solution that does not involve ping-pong with the server.
So it occurred to me that one could open a new window and try to set the header with a META tag equivalent.
But this doesn't work for me in recent Chrome.
I do get a new window, and it contains the csvString, but does not act as a download.
I guess I expected to get either a download in a bottom tab or a blank new window with a download in a bottom tab.
I'm wondering if the meta tags are correct or if other tags are also needed.
Is there a way to make this work without punting it to the server?
JsFiddle for Creating a CSV in the Browser (not working - outputs window but no download)
var A = [['n','sqrt(n)']]; // initialize array of rows with header row as 1st item
for(var j=1;j<10;++j){ A.push([j, Math.sqrt(j)]) }
var csvRows = [];
for(var i=0,l=A.length; i<l; ++i){
csvRows.push(A[i].join(',')); // unquoted CSV row
}
var csvString = csvRows.join("\n");
console.log(csvString);
var csvWin = window.open("","","");
csvWin.document.write('<meta name="content-type" content="text/csv">');
csvWin.document.write('<meta name="content-disposition" content="attachment; filename=data.csv"> ');
csvWin.document.write(csvString);
There's always the HTML5 download attribute :
This attribute, if present, indicates that the author intends the
hyperlink to be used for downloading a resource so that when the user
clicks on the link they will be prompted to save it as a local file.
If the attribute has a value, the value will be used as the pre-filled
file name in the Save prompt that opens when the user clicks on the
link.
var A = [['n','sqrt(n)']];
for(var j=1; j<10; ++j){
A.push([j, Math.sqrt(j)]);
}
var csvRows = [];
for(var i=0, l=A.length; i<l; ++i){
csvRows.push(A[i].join(','));
}
var csvString = csvRows.join("%0A");
var a = document.createElement('a');
a.href = 'data:attachment/csv,' + encodeURIComponent(csvString);
a.target = '_blank';
a.download = 'myFile.csv';
document.body.appendChild(a);
a.click();
FIDDLE
Tested in Chrome and Firefox, works fine in the newest versions (as of July 2013).
Works in Opera as well, but does not set the filename (as of July 2013).
Does not seem to work in IE9 (big suprise) (as of July 2013).
An overview over what browsers support the download attribute can be found Here
For non-supporting browsers, one has to set the appropriate headers on the serverside.
Apparently there is a hack for IE10 and IE11, which doesn't support the download attribute (Edge does however).
var A = [['n','sqrt(n)']];
for(var j=1; j<10; ++j){
A.push([j, Math.sqrt(j)]);
}
var csvRows = [];
for(var i=0, l=A.length; i<l; ++i){
csvRows.push(A[i].join(','));
}
var csvString = csvRows.join("%0A");
if (window.navigator.msSaveOrOpenBlob) {
var blob = new Blob([csvString]);
window.navigator.msSaveOrOpenBlob(blob, 'myFile.csv');
} else {
var a = document.createElement('a');
a.href = 'data:attachment/csv,' + encodeURIComponent(csvString);
a.target = '_blank';
a.download = 'myFile.csv';
document.body.appendChild(a);
a.click();
}
#adeneo answer works for Firefox and chrome... For IE the below can be used.
if (window.navigator.msSaveOrOpenBlob) {
var blob = new Blob([decodeURIComponent(encodeURI(result.data))], {
type: "text/csv;charset=utf-8;"
});
navigator.msSaveBlob(blob, 'FileName.csv');
}
See adeneo's answer, but don't forget encodeURIComponent!
a.href = 'data:application/csv;charset=utf-8,' + encodeURIComponent(csvString);
Also, I needed to do "\r\n" not just "\n" for the row delimiter.
var csvString = csvRows.join("\r\n");
Revised fiddle: http://jsfiddle.net/7Q3c6/
Once I packed JS code doing that to a tiny library:
https://github.com/AlexLibs/client-side-csv-generator
The Code, Documentation and Demo/Playground are provided on Github.
Enjoy :)
Pull requests are welcome.
We can easily create and export/download the excel file with any separator (in this answer I am using the comma separator) using javascript. I am not using any external package for creating the excel file.
var Head = [[
'Heading 1',
'Heading 2',
'Heading 3',
'Heading 4'
]];
var row = [
{key1:1,key2:2, key3:3, key4:4},
{key1:2,key2:5, key3:6, key4:7},
{key1:3,key2:2, key3:3, key4:4},
{key1:4,key2:2, key3:3, key4:4},
{key1:5,key2:2, key3:3, key4:4}
];
for (var item = 0; item < row.length; ++item) {
Head.push([
row[item].key1,
row[item].key2,
row[item].key3,
row[item].key4
]);
}
var csvRows = [];
for (var cell = 0; cell < Head.length; ++cell) {
csvRows.push(Head[cell].join(','));
}
var csvString = csvRows.join("\n");
let csvFile = new Blob([csvString], { type: "text/csv" });
let downloadLink = document.createElement("a");
downloadLink.download = 'MYCSVFILE.csv';
downloadLink.href = window.URL.createObjectURL(csvFile);
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
See adeneo's answer, but to make this work in Excel in all countries you should add "SEP=," to the first line of the file. This will set the standard separator in Excel and will not show up in the actual document
var csvString = "SEP=, \n" + csvRows.join("\r\n");