create a downloadable csv from a json string - javascript

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);

Related

Export javascript loop to CSV

Suppose I have this loop code.
for (var i = 0; i < originList.length; i++) {
var results = response.rows[i].elements;
for (var j = 0; j < results.length; j++) {
outputDiv.innerHTML += results[j].distance.text + ',';
}
}
I want to export the outputDiv.innerHTML into CSV with this code, but it doesn't work.
function downloadFile(fileName, urlData) {
var aLink = document.createElement('a');
aLink.download = fileName;
aLink.href = urlData;
var event = new MouseEvent('click');
aLink.dispatchEvent(event);
}
downloadFile('output.csv', 'outputDiv.innerHTML/csv;charset=UTF-8,' + encodeURIComponent(outputDiv.innerHTML));
What should I do? I'm new at this. Thank you.
This solution is in JavaScript. I added an event listener to the button so when it is clicked, it will grab the outerHTML of <table>.
outerHTML essentially includes the opening and closing tags of the element as well as the content whereas innerHTML does not include the opening and closing tags.
From MDN Web Docs
The outerHTML attribute of the Element DOM interface gets the serialized HTML fragment describing the element including its descendants. It can also be set to replace the element with nodes parsed from the given string.
When the innerText is extracted from the all rows and columns. download_csv is called.
You can download the data using a Blob object which is a file-like object of immutable, raw data.
document.querySelector("button").addEventListener("click", function () {
let html = document.querySelector("table").outerHTML;
exportToCSV(html, "table.csv");
});
function exportToCSV(html, filename) {
let csv = [];
// grab all rows inside table
let rows = document.querySelectorAll("table tr");
let row, cols;
for (let i = 0; i < rows.length; i++) {
row = []; // will hold innerText of all columns
// retrieve all columns of row
cols = rows[i].querySelectorAll("td, th");
for (let j = 0; j < cols.length; j++){
// push column innerText
row.push(cols[j].innerText);
}
// push all innerText into CSV
csv.push(row.join(","));
}
console.log("Extracted content from html:",csv);
// Download CSV
download_csv(csv.join("\n"), filename);
}
function download_csv(csv, filename) {
let csvFile;
let downloadLink;
// CSV FILE
csvFile = new Blob([csv], {type: "text/csv"});
// create an element and set the file name.
downloadLink = document.createElement("a");
downloadLink.download = filename;
// We have to create a link to the file
downloadLink.href = window.URL.createObjectURL(csvFile);
// prevent link from being shown
downloadLink.style.display = "none";
// Add the link to your DOM
document.body.appendChild(downloadLink);
// start the download
downloadLink.click();
}
<table>
<tr><th>Name</th><th>Age</th><th>Country</th></tr>
<tr><td>Tony</td><td>26</td><td>USA</td></tr>
<tr><td>Levi</td><td>19</td><td>Spain</td></tr>
<tr><td>Calvin</td><td>32</td><td>Russia</td></tr>
</table>
<button>Export HTML table to CSV file</button>
I do not know what are you trying to achieve in your last line, but that does not look like a dataURL, a dataURL looks like:
data:[][;base64],
Now that being said, the idea is to create an object url through a combination of Blob and window.URL.createObjectURL:
function dL(input,fileName){
var blob = new Blob(input,{type:"text/csv"}),
url = window.URL.createObjectURL(blob),
aElem = document.createElement("a"),
fileName = "deogenResults.txt";
aElem.setAttribute("href",url);
aElem.setAttribute("download",fileName);
if (window.navigator.constructor.prototype.hasOwnProperty("msSaveBlob")) {
window.navigator.msSaveBlob(blob,fileName);
} else if ("download" in aElem) {
aElem.click();
} else {
window.open(url,"_blank");
}
setTimeout(function(){window.URL.revokeObjectURL(url)},2000);
}
Use it like this: dL(outputDiv.innerHTML,"someName")
It is important to remind you that some browsers might not allow click to trigger on an element that is NOT in the DOM yet, in that case you might want to append the a element to the body, set it invisible and then remove it inside setTimeout.
I wrote it in plain ES5, you can adapt with const,Promise instead of setTimeout etc declarations accordingly.
A good answer here by dandavis:
It uses a library by http://danml.com/js/download.js you make sure your div contents contains comma seperated content.
var csv = jQuery(".list").map(function(a, i) {
return $.trim($(this).text()).split(/\s*\n\s*/).join(",");
}).toArray().join("\r\n");
alert(csv); // Contents
// Download
// download(csv, "tabledata.csv", "text/csv");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://danml.com/js/download.js"></script> <!-- CSV -->
<div class="list">
1, 2, 3
</div>

exporting html table with links to csv

I am using the following function to export a table to a csv file for our users to export a copy of their purchases to a format they can use in quickbooks, excel, etc.
The function works great, except that the first column of our table contains urls and these columns appear blank in the export.
I am wondering how i could modify this so when a link is encountered as a field the text of that link would be added to the csv.
Example: an example would add an example to the csv for that field.
function exportTableToCSV($table, filename) {
var $rows = $table.find('tr:has(td),tr:has(th)'),
tmpColDelim = String.fromCharCode(11), // vertical tab character
tmpRowDelim = String.fromCharCode(0), // null character
colDelim = '","',
rowDelim = '"\r\n"',
csv = '"' + $rows.map(function (i, row) {
var $row = $(row), $cols = $row.find('td,th');
return $cols.map(function (j, col) {
var $col = $(col), text = $col.text();
return text.replace(/"/g, '""'); // escape double quotes
}).get().join(tmpColDelim);
}).get().join(tmpRowDelim).split(tmpRowDelim).join(rowDelim).split(tmpColDelim).join(colDelim) + '"',
csvData = 'data:application/csv;charset=utf-8,' + encodeURIComponent(csv);
console.log(csv);
if (window.navigator.msSaveBlob) { // IE 10+
window.navigator.msSaveOrOpenBlob(new Blob([csv],
{type: "text/plain;charset=utf-8;"}),
"csvname.csv")
} else {
$(this).attr({ 'download': filename, 'href': csvData, 'target': '_blank' });
}
}
As far as I can see, your snippet should already support this. It iterates over all rows of the table and for each row over all of its column fields, where jQuery's text() function is applied in order to extract the text. This should return the text between a tags as well.
Example:
console.log($('<td>Test page</td>').text());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
will return the string Test page.
This fiddle contains the function you provided and a dummy table with some anchor elements. In my opinion it already meets your requirements.
var a = document.createElement('a');
a.href = "http://example.php";
a.append("an example");
document.body.appendChild(a);
console.log(a.textContent);

Implement save.As() dialog for csv in JavaScript

I am trying to write a code that will convert my php array into csv and then open a download dialog (save.As()) so that the website user can download the file after clicking on an html href link.
After searching a lot of posts, I could already come up with a solution in JavaScript that enables me to convert the php array to csv and display the result on the webpage to check if this is functioning and indeed it is.
I tried hard to also get the download dialog working, but had no success so far. I was trying FileSaver.js but this one just won't work for me. I would prefer a pure JavaScript solution that would work for IE, Safari and Firefox without additional libraries.
Here is my script so far to convert json object to csv and to display result on screen (this is mostly based on another script found at SO, but I cannot find the source again - if someone knows, I would be happy to include a link here):
<script type="text/javascript">
function ConvertToCSV(objArray) {
var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
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;
}
function download_csv_function() {
// Read in JSON data and transform to CSV
$(document).ready(function () {
// Read in json data
var items = <?php echo $jsonout; ?>;
// Convert JSON object into JSON string
var jsonObject = JSON.stringify(items);
// Convert JSON to CSV
var csvstring = ConvertToCSV(jsonObject);
$('#csv').text(csvstring);
})
};
</script>
Include a link in html:
</form>
<!-- Define "Download .csv link here" -->
<a href="javascript:void(0)" onclick="download_csv_function();" >Get data as csv file</a>
</form>
<body>
<pre id="csv"></pre>
</body>
Thanks!
Thanks to the helpful comments, I could get this running and updated the code to the final version that uses FileSaver.js. The following syntax "saveTextAs(data, filename)" solved the problem. It works for both Firefox and Safari (IE I haven't checked yet). In addition to FileSaver.js, jQuery needs to be loaded for proper functioning of the href link.
First load js libraries:
</script>
<!-- Load jQuery and FileSaver.js here -->
<script src="/path-to-jquery.js"></script>
<script src="/path-to-FileSaver.js"></script>
<script>
Script to convert json object to csv and to open download dialog (this is mostly based on another script found at SO, but I cannot find the source again - if someone knows, I would be happy to include a link here):
<script type="text/javascript">
function ConvertToCSV(objArray) {
var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
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;
}
function download_csv_function() {
// Read in JSON data and transform to CSV
$(document).ready(function () {
// Read in json data
var items = <?php echo $jsonout; ?>;
// Convert JSON object into JSON string
var jsonObject = JSON.stringify(items);
// Convert JSON to CSV
var csvstring = ConvertToCSV(jsonObject);
// FileSaver.js: open download dialog and save file as csv
saveTextAs(csvstring, "data.csv");
})
};
</script>
Include a link in html:
</form>
<!-- Define "Download .csv link here" -->
<a href="javascript:void(0)" onclick="download_csv_function();" >Get data as csv file</a>
</form>
Thanks everyone for the help!

Javascript : how inject a variable with html content to a data-attribute appended?

I wish to insert a variable that contains an HTML code in a DATA attribute (a href ... data-content= ...) it not work very well because the code inserted deletes some characters and suddenly it does not display properly.
Here is the code used
function uploadProgress(file)
{
var ext = file.split('.').pop();
var fileUrl = '/playerFiles/'+file;
if(ext == 'mp4')
{
var preview = '<video autoplay loop muted width="250"><source src="'+fileUrl+'" type="video/mp4">Your browser does not support the video tag.</video>';
}
else
{
var preview = '<img src="'+fileUrl+'" width="250">';
}
var showtime = $('#'+id).find('td.showtime');
showtime.html('<i class="fa fa-file-o"></i> Aperçu');
}
AND my HTML output return this :
"><i class="fa fa-file-o"></i> Aperçu
Why it doesn't work ? What should I do?
Thank You
The problem with your code is there is special characters in the preview value . If you use code given below then you can override the problem and this is not the proper way and avoid this kind of coding style.Use data attributes for integers,small string values etc.. contents like html or long string values etc either use public properties or hidden controls.
function uploadProgress(file)
{
var ext = file.split('.').pop();
var fileUrl = '/playerFiles/'+file;
if(ext == 'mp4')
{
var preview = '<video autoplay loop muted width="250"><source src="'+fileUrl+'" type="video/mp4">Your browser does not support the video tag.</video>';
}
else
{
var preview = '<img src="'+fileUrl+'" width="250">';
}
var showtime = $('#'+id).find('td.showtime');
showtime.html('</i> Aperçu');
$(".preview").data("content",preview);
}
well, lets fix this for the first: you have double quotes in the preview var you should escape them with '\' e.g.:
var preview = '<img src=\"' + url + '\" width=\"250\">';
or better use single quotes inside the var
var preview = "<img src='" + url + "' width='250'>";
but I think it's not good approach to store html in this attr - would be better to store here url only and html in the separate template. or render the hidden element on page load

Use jQuery to get the file input's selected filename without the path

I used this:
$('input[type=file]').val()
to get the file name selected, but it returned the full path, as in "C:\fakepath\filename.doc". The "fakepath" part was actually there - not sure if it's supposed to be, but this is my first time working with the filename of file uploads.
How can I just get the file name (filename.doc)?
var filename = $('input[type=file]').val().split('\\').pop();
or you could just do (because it's always C:\fakepath that is added for security reasons):
var filename = $('input[type=file]').val().replace(/C:\\fakepath\\/i, '')
You just need to do the code below. The first [0] is to access the HTML element and second [0] is to access the first file of the file upload (I included a validation in case that there is no file):
var filename = $('input[type=file]')[0].files.length ? ('input[type=file]')[0].files[0].name : "";
Get path work with all OS
var filename = $('input[type=file]').val().replace(/.*(\/|\\)/, '');
Example
C:\fakepath\filename.doc
/var/fakepath/filename.doc
Both return
filename.doc
filename.doc
Chrome returns C:\fakepath\... for security reasons - a website should not be able to obtain information about your computer such as the path to a file on your computer.
To get just the filename portion of a string, you can use split()...
var file = path.split('\\').pop();
jsFiddle.
...or a regular expression...
var file = path.match(/\\([^\\]+)$/)[1];
jsFiddle.
...or lastIndexOf()...
var file = path.substr(path.lastIndexOf('\\') + 1);
jsFiddle.
Here is how I do it, it works pretty well.
In your HTML do:
<input type="file" name="Att_AttributeID" onchange="fileSelect(event)" class="inputField" />
Then in your js file create a simple function:
function fileSelect(id, e){
console.log(e.target.files[0].name);
}
If you're doing multiple files, you should also be able to get the list by looping over this:
e.target.files[0].name
maybe some addition for avoid fakepath:
var fileName = $('input[type=file]').val();
var clean=fileName.split('\\').pop(); // clean from C:\fakepath OR C:\fake_path
alert('clean file name : '+ fileName);
How about something like this?
var pathArray = $('input[type=file]').val().split('\\');
alert(pathArray[pathArray.length - 1]);
This alternative seems the most appropriate.
$('input[type="file"]').change(function(e){
var fileName = e.target.files[0].name;
alert('The file "' + fileName + '" has been selected.');
});
Does it have to be jquery? Or can you just use JavaScript's native yourpath.split("\\") to split the string to an array?
<script type="text/javascript">
$('#upload').on('change',function(){
// output raw value of file input
$('#filename').html($(this).val().replace(/.*(\/|\\)/, ''));
// or, manipulate it further with regex etc.
var filename = $(this).val().replace(/.*(\/|\\)/, '');
// .. do your magic
$('#filename').html(filename);
});
</script>
Get the first file from the control and then get the name of the file, it will ignore the file path on Chrome, and also will make correction of path for IE browsers. On saving the file, you have to use System.io.Path.GetFileName method to get the file name only for IE browsers
var fileUpload = $("#ContentPlaceHolder1_FileUpload_mediaFile").get(0);
var files = fileUpload.files;
var mediafilename = "";
for (var i = 0; i < files.length; i++) {
mediafilename = files[i].name;
}
Here you can call like this
Let this is my Input File control
<input type="file" title="search image" id="file" name="file" onchange="show(this)" />
Now here is my Jquery which get called once you select the file
<script type="text/javascript">
function show(input) {
var fileName = input.files[0].name;
alert('The file "' + fileName + '" has been selected.');
}
</script>
var filename=location.href.substr(location.href.lastIndexOf("/")+1);
alert(filename);
We can also remove it using match
var fileName = $('input:file').val().match(/[^\\/]*$/)[0];
$('#file-name').val(fileName);

Categories