JSON to CSV export works in Chrome, not in IE - javascript

My goal is to simply grab a JSON and export it as a CSV file. Ideally the user gets a prompt within the web browser that asks whether to save or open the CSV file.
I came across many examples that require a call to the server for the JSON to Excel/CSV conversion but I do need a JavaScript-only solution. I found this particular fiddle useful.
This works just fine when it's run with Chrome (v39) but nothing happens in IE11. In fact, I see a couple of errors in IE's console as follows:
I'm still trying to figure out whether these errors are browser specific, which I think they are. And if so, if there's any workaround. As my target browser is IE, "But it works in Chrome so use it" won't do me any good. I've spent hours trying to find a workaround for this to no avail, so any pointer in the right direction will be much appreciated.
Below is verbatim copy and paste of the aforementioned fiddle:
HTML
<div class='mydiv'>
<textarea id="txt" class='txtarea'>
[{"Vehicle":"BMW","Date":"30, Jul 2013 09:24 AM","Location":"Hauz Khas, Enclave, New Delhi, Delhi, India","Speed":42},{"Vehicle":"Honda CBR","Date":"30, Jul 2013 12:00 AM","Location":"Military Road, West Bengal 734013, India","Speed":0},{"Vehicle":"Supra","Date":"30, Jul 2013 07:53 AM","Location":"Sec-45, St. Angel's School, Gurgaon, Haryana, India","Speed":58},{"Vehicle":"Land Cruiser","Date":"30, Jul 2013 09:35 AM","Location":"DLF Phase I, Marble Market, Gurgaon, Haryana, India","Speed":83},{"Vehicle":"Suzuki Swift","Date":"30, Jul 2013 12:02 AM","Location":"Behind Central Bank RO, Ram Krishna Rd by-lane, Siliguri, West Bengal, India","Speed":0},{"Vehicle":"Honda Civic","Date":"30, Jul 2013 12:00 AM","Location":"Behind Central Bank RO, Ram Krishna Rd by-lane, Siliguri, West Bengal, India","Speed":0},{"Vehicle":"Honda Accord","Date":"30, Jul 2013 11:05 AM","Location":"DLF Phase IV, Super Mart 1, Gurgaon, Haryana, India","Speed":71}]
</textarea>
<button class='gen_btn'>Generate File</button>
</div>
Script
$(document).ready(function(){
$('button').click(function(){
var data = $('#txt').val();
if(data == '')
return;
JSONToCSVConvertor(data, "Vehicle Report", true);
});
});
function JSONToCSVConvertor(JSONData, ReportTitle, ShowLabel) {
//If JSONData is not an object then JSON.parse will parse the JSON string in an Object
var arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData;
var CSV = '';
//Set Report title in first row or line
CSV += ReportTitle + '\r\n\n';
//This condition will generate the Label/Header
if (ShowLabel) {
var row = "";
//This loop will extract the label from 1st index of on array
for (var index in arrData[0]) {
//Now convert each value to string and comma-seprated
row += index + ',';
}
row = row.slice(0, -1);
//append Label row with line break
CSV += row + '\r\n';
}
//1st loop is to extract each row
for (var i = 0; i < arrData.length; i++) {
var row = "";
//2nd loop will extract each column and convert it in string comma-seprated
for (var index in arrData[i]) {
row += '"' + arrData[i][index] + '",';
}
row.slice(0, row.length - 1);
//add a line break after each row
CSV += row + '\r\n';
}
if (CSV == '') {
alert("Invalid data");
return;
}
//Generate a file name
var fileName = "MyReport_";
//this will remove the blank-spaces from the title and replace it with an underscore
fileName += ReportTitle.replace(/ /g,"_");
//Initialize file format you want csv or xls
var uri = 'data:text/csv;charset=utf-8,' + escape(CSV);
// Now the little tricky part.
// you can use either>> window.open(uri);
// but this will not work in some browsers
// or you will not get the correct file extension
//this trick will generate a temp <a /> tag
var link = document.createElement("a");
link.href = uri;
//set the visibility hidden so it will not effect on your web-layout
link.style = "visibility:hidden";
link.download = fileName + ".csv";
//this part will append the anchor tag and remove it after automatic click
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}

IE normal doesnt support download tribute. you have to try with blob method.
conver the Json to CSV then usning blob you can download from IE.
nb : this wont work in IE9 or less will work in IE10+
https://stackoverflow.com/a/45641408/8151001

Are you hosting the jquery file or accessing it via a CDN? Try using Google's CDN.
https://developers.google.com/speed/libraries/devguide#jquery
Here is where the code is generating the error.

The problem is due to IE not supporting Data URIs to export CSV files:
https://msdn.microsoft.com/en-us/library/cc848897%28v=vs.85%29.aspx
The other browsers do. I know this is an old question. I hope you have found a work around in the past few months?

Related

Issue in reading self closing tag in javascript

When using javascript to read self close tag the output is coming differently.
Here is my webservice output in xml:
<aryNotice>
<Notice>
<PlanStartDate>
3/20/2017 11:28:01 AM
</PlanStartDate>
<PlanEndDate />
<Location>
UK / City
</Location>
<SevLevel>
YELLOW
</SevLevel>
<SrvLev123>
Web Services
</SrvLev123>
<WebTitle />
<Weblink />
</Notice>
</aryNotice>
Javascript code to read planned start and end dates:
if(document.getElementsByTagName('PlanStartDate').length > 0) {newXML += 'Planned Dates: ' + document.getElementsByTagName('PlanStartDate')[i].textContent; }
if(document.getElementsByTagName('PlanEndDate').length > 0) {newXML+= ' - ' + document.getElementsByTagName('PlanEndDate')[i].textContent;}
else
{newXML+= ' No end date ';}
Output:
Planned Dates: 3/20/2017 11:28:01 AM - UK / CityYELLOWWeb Services]]
I want output to be Planned Dates: 3/20/2017 11:28:01 AM - No end date
Self-terminated tag syntax means nothing to the HTML parser. It just ignores the / in the tag and sees it as an opening tag. Then, even though there is no closing tag in the code, the HTML parser constructs one in memory anyway. You can see that here:
console.log(document.querySelector("testTag"));
<testTag />
As you can see, the browser just sees this as an empty element. So, when you attempt to get the .textContent of an empty element, you get nothing.
Your if conditions weren't testing the .textContent though, they were testing the existence of the elements:
if(document.getElementsByTagName('PlanEndDate').length > 0)
And, since the element does exist, your code incorrectly entered the true branch of the if.
Unfortunately, even if you modified your code to test for the .textContent.length, it still won't work because the HTML parser places the dynamically created end-tag after all the other content, so the .textContent of everything that comes after the opening tag becomes the .textContent of the self-terminated element.
You need to be using an XML parser to properly access this content, otherwise the HTML parser does the job and (as you can tell), it doesn't know how to deal with XML syntax.
var xmlString =
`
<aryNotice><Notice>
<PlanStartDate>3/20/2017 11:28:01 AM</PlanStartDate>
<PlanEndDate />
<Location>UK / City</Location>
<SevLevel>YELLOW</SevLevel>
<SrvLev123>Web Services</SrvLev123>
<WebTitle />
<Weblink /></Notice>
</aryNotice>
`;
var newXML = "";
var parser = null, xmlDoc = null;
// Create XML DOM Document correctly for the browser being used:
if (window.DOMParser) {
parser = new DOMParser();
xmlDoc = parser.parseFromString(xmlString, "text/xml");
} else {
// Internet Explorer
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.loadXML(xmlString);
}
// Now, use the XML DOM API from the XML Document:
var start = xmlDoc.querySelector("PlanStartDate");
var end = xmlDoc.querySelector("PlanEndDate");
// Rest of code is as normal:
if(start.textContent) {
newXML += 'Planned Dates: ' + start.textContent;
}
if(end.textContent) {
newXML += ' - ' + end.textContent;
} else {
newXML += ' - No end date ';
}
console.log(newXML);

How to get javascript function to return selected database entries

I am creating a web directory of organisations which displays the organisations within the selected county using an xml database.
For testing purposes I have only set it up to display the organisation names and counties at the moment but when I have a county name in the javascript marked with // <---------- I get only that county's organisations. How do I get a cross-browser solution for the option list in the html to select the county?
There will be more than one organisation per county in the fully working version and the web addresses in the xml data will need to be outputted as hyperlinks which is why I am extracting each child node one at a time instead of all the children in one go. The idea is still work in progress and the output will eventually be in separate divs for each organisation.
My html
<form>
Select your choice of county:
<select onchange="DisplayData(this)">
<option>---</option>
<option>UK</option>
<option>Hampshire</option>
<option>Surrey</option>
<option>Berkshire</option>
</select>
</form>
My javascript
<script>
function loadXMLDoc(dname)
{
if (window.XMLHttpRequest)
{
xhttp=new XMLHttpRequest();
}
else
{
xhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xhttp.open("GET",dname,false);
try {xhttp.responseType="msxml-document"} catch(err) {} // Helping IE
xhttp.send("");
return xhttp;
}
var x=loadXMLDoc("data.xml");
var xml=x.responseXML;
function DisplayData(option)
{
name="/data/record[county[contains(., 'Hampshire')]]/name"; //<---------
// name="/data/record[county[contains(., option)]]/name"; DISPLAYS ALL COUNTIES
// name="/data/record[county[contains(., $option)]]/name"; DISPLAYS ALL COUNTIES
county="/data/record[county[contains(., 'Hampshire')]]/county"; //<---------
// code for IE
if (window.ActiveXObject || xhttp.responseType=="msxml-document")
{
xml.setProperty("SelectionLanguage","XPath");
nodes1=xml.selectNodes(name);
nodes2=xml.selectNodes(county);
for (i=0;i<nodes1.length;i++)
{
document.write("<b>Name:</b> " + nodes1[i].childNodes[0].nodeValue);
document.write("<br>");
document.write("<b>County:</b> " + nodes2[i].childNodes[0].nodeValue);
document.write("<br>");
document.write("<br>");
}
}
// code for Chrome, Firefox, Opera, etc.
else if (document.implementation && document.implementation.createDocument)
{
var nodes1=xml.evaluate(name, xml, null, XPathResult.ANY_TYPE, null);
var nodes2=xml.evaluate(county, xml, null, XPathResult.ANY_TYPE, null);
var result1=nodes1.iterateNext();
var result2=nodes2.iterateNext();
while (result1)
{
document.write("<b>Name:</b> " + result1.childNodes[0].nodeValue);
document.write("<br>");
document.write("<b>County:</b> " + result2.childNodes[0].nodeValue);
document.write("<br>");
document.write("<br>");
result1=nodes1.iterateNext();
result2=nodes2.iterateNext();
}
}
}
</script>
My test data.xml file
<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
<record>
<name>Hampshire Organisation</name>
<town>Basingstoke</town>
<county>Hampshire</county>
<tele>01234 567890</tele>
<web>www.hampshireorg.com</web>
</record>
<record>
<name>Surrey Organisation</name>
<town>Woking</town>
<county>Surrey</county>
<tele>01234 567890</tele>
<web>www.surreyorg.com</web>
</record>
<record>
<name>Berkshire Organisation</name>
<town>Reading</town>
<county>Berkshire</county>
<tele>01234 567890</tele>
<web>www.berkshireorg.com</web>
</record>
<record>
<name>Nationwide Organisation</name>
<town>London</town>
<county>UK</county>
<tele>01234 567890</tele>
<web>www.nationwideorg.com</web>
</record>
</data>
The main issue here is that <select onchange="DisplayData(this)"> is returning the select element, not the option selected.
The selected option can be obtained like so: option.selectedOptions[0] and option.selectedOptions[0].innerText can be used to get the text.
Another serious issue is the XPath query that contains the option string. The string is parsed as a literal, so it's searching for "option", not the data contained in the option variable. It should look like this: name="/data/record[county[contains(., " + option + ")]]/name";
Assuming each data/record entry is unique, you don't need to use a while loop on those nodes because you will only have one result. I don't use XPath but I assume you could use a while loop to iterate through the childNodes if you needed to.
Below is my version of DisplayData without the browser checking.
Since the data exists together as children of the one node, I only use one query. This is ideal because if you need to pass the record data around, it's self contained.
And here's a plunker.
function DisplayData(option) {
var selectedOption = option.selectedOptions[0].innerText;
var xpath = '/data//record[contains(./county, "' + selectedOption + '")]';
var result = xml.evaluate(xpath, xml.documentElement, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (result.singleNodeValue !== null && result.singleNodeValue.children.length > 0){
var el = document.getElementById('results');
el.innerHTML = '';
var data = result.singleNodeValue.children;
for (var i = 0, length = data.length; i < length; i++){
el.innerHTML += '<li>' + data[i].textContent + '</li>';
}
}
}
I have found the answer to my problem
<select onchange="DisplayData(this)">needed to be changed to <select onchange="DisplayData(this.options[this.selectedIndex].value)">
This allows the option variable within the javascript to work when altering
name="/data/record[county[contains(., 'Hampshire')]]/name"; to name='/data//record[contains(./county, "' + option + '")]/name'; for example as suggested by slamborne within his answer
and it works cross browser.

detecting multiple html tags with javascript and regex

I am building a chrome extension which would read the current page and detect specific html/xml tags out of it :
For example if my current page contains the following tags or data :
some random text here and there
<investmentAccount acctType="individual" uniqueId="1629529524">
<accountName>state bank of america</accountName>
<accountHolder>rahul raina</accountHolder>
<balance balType="totalBalance">
<curAmt curCode="USD">516545.84</curAmt>
</balance>
<asOf localFormat="MMM dd, yyyy">2013-08-31T00:00:00</asOf>
<holdingList>
<holding holdingType="mutualFund" uniqueId="-2044388005">
<description>Active Global Equities</description>
<value curCode="USD">159436.01</value>
</holding>
<holding holdingType="mutualFund" uniqueId="-556870249">
<description>Passive Non-US Equities</description>
<value curCode="USD">72469.76</value>
</holding>
</holdingList>
<transactionList/>
</investmentAccount>
</site>
some data 123
<site name="McKinsey401k">
<investmentAccount acctType="individual" uniqueId="1629529524">
<accountName>rahuk</accountName>
<accountHolder>rahuk</accountHolder>
<balance balType="totalBalance">
<curAmt curCode="USD">516545.84</curAmt>
</balance>
<asOf localFormat="MMM dd, yyyy">2013-08-31T00:00:00</asOf>
<holdingList>
<holding holdingType="mutualFund" uniqueId="1285447255">
<description>Special Sits. Aggr. Long-Term</description>
<value curCode="USD">101944.69</value>
</holding>
<holding holdingType="mutualFund" uniqueId="1721876694">
<description>Special Situations Moderate $</description>
<value curCode="USD">49444.98</value>
</holding>
</holdingList>
<transactionList/>
</investmentAccount>
</site>
So I need to identify say tag and print the text between the starting and ending tag i.e : "State bank of america" and "rahukk"
So this is what I have done till now:
function countString(document_r,a,b) {
var test = document_r.body;
var text = typeof test.textContent == 'string'? test.textContent : test.innerText;
var testRE = text.match(a+"(.*)"+b);
return testRE[1];
}
chrome.extension.sendMessage({
action: "getSource",
source: "XML DETAILS>>>>>"+"\nAccount name is: " +countString(document,'<accountName>','</accountName>')
});
But this only prints the innertext of only the first tag it encounters in the page i.e "State bank of america".
What if I want to print only "rahukk" which is the innertext of last tag in the page or both.
How do I print the innertext of last tag it encounters in the page or how does it print all the tags ?
Thanks in advance.
EDIT : The document above itself is an HTML page i have just put the contents of the page
UPDATE : So I did some here and there from the suggestions below and the best I could reach by this code :
function countString(document_r) {
var test = document_r.body;
var text = test.innerText;
var tag = "accountName";
var regex = "<" + tag + ">(.*?)<\/" + tag + ">";
var regexg = new RegExp(regex,"g");
var testRE = text.match(regexg);
return testRE;
}
chrome.extension.sendMessage({
action: "getSource",
source: "XML DETAILS>>>>>"+"\nAccount name is: " +countString(document)
});
But this gave me :
XML DETAILS>>>>> Retirement Program (Profit-Sharing
Retirement Plan (PSRP) and Money Purchase Pension Plan
(MPPP)),Retirement Program (Profit-Sharing
Retirement Plan (PSRP) and Money Purchase Pension Plan
(MPPP)),Retirement Program (Profit-Sharing
Retirement Plan (PSRP) and Money Purchase Pension Plan
(MPPP))
This again because the same XML was present in the page 3 times and What I want is that regex to match only from the last XML and I don't want the tag names too.
So my desired output would be:
XML DETAILS>>>>> Retirement Program (Profit-Sharing
Retirement Plan (PSRP) and Money Purchase Pension Plan
(MPPP))
you match method is not global.
var regex = new RegExp(a+"(.*)"+b, "g");
text.match(regex);
If the full XML string is valid, you can parse it into an XML document using the DOMParser.parseFromString method:
var xmlString = '<root>[Valid XML string]</root>';
var parser = new DOMParser();
var doc = parser.parseFromString(xmlString, 'text/xml');
Then you can get a list of tags with a specified name directly:
var found = doc.getElementsByTagName('tagName');
Here's a jsFiddle example using the XML you provided, with two minor tweaks—I had to add a root element and an opening tag for the first site.
Regex pattern like this: <accountName>(.*?)<\/accountName>
var tag = "accountName";
var regex = "<" + tag + ">(.*?)<\/" + tag + ">";
var testRE = text.match(regex);
=> testRE contains all your matches, in case of tag=accountName it contains "state bank of america" and "rahukk"
UPDATE
According to this page to receive all matches, instead of only the first one, you smust add a "g" flag to the match pattern.
"g: The global search flag makes the RegExp search for a pattern
throughout the string, creating an array of all occurrences it can
find matching the given pattern." found here
Hope this helps you!
You don't need regular expressions for your task (besides, read RegEx match open tags except XHTML self-contained tags for why it's not a good idea!). You can do this completely via javascript:
var tag = "section";
var targets = document.getElementsByTagName(tag);
for (var i = targets.length; i > 0; i--) {
console.log(targets[i].innerText);
}

Load events into calendar

I have a XML-file, which contains the data of a calendar. I want to load the events dynamically into a calendar using AJAX. I want to get the XML-elements called "afspraak" (four of them). The XML-file can be viewed here: http://dimitrisnijder.nl/files/agenda.xml
I'm trying to do that using the following code:
function agendaLoad(){
$(".title").html(monthLabels[thisMonth]+" "+thisYear);
$.get(
"agendaSaver.php",
{month: thisMonth,
year: thisYear},
processXML,
"xml"
);
}
function processXML(xml) {
$("agenda afspraak", xml).each(function() {
var dag = $(this).find("dag").text();
var maand = $(this).find("maand").text();
var jaar = $(this).find("jaar").text();
var onderwerp = $(this).find("onderwerp").text();
var tijd = $(this).find("tijd").text();
var beschrijving = $(this).find("beschrijving").text();
if((maand == today.getMonth()+1) && (jaar == today.getFullYear())) {
$("#day"+dag).append('<div class="afspraak">'+onderwerp+'</div> <p class="beschrijving"><strong>'+tijd+' '+onderwerp+'</strong><br/>'+beschrijving+'</p>');
}
});
}
This partially works. The script will load the fourth event ("afspraak") into the agenda, and displays it on the 25th of March 2012. But that event is loaded on the 25th day of every month. The first three events arent loaded at all..
Any help? Thanks in advance.
Your processXML() function seems to be fine at first glance. So, could it be that the xml returned by agendaSaver.php only includes the fourth event? Test for this by putting alert(xml) in the first line of processXML().

jQuery, Assign td text to a variable and post it

I am relatively new to JavaScript and have a problem I hope your can help
What I am trying to do is this
$("tr:not(:first-child)").each(function(){
var sitename = $("this" + "td:nth-child(1)").text();
var type= $("this" + "td:nth-child(2)").text();
var address= $("this" + "td:nth-child(3)").text();
var city= $("this" + "td:nth-child(4)").text();
var lat= $("this" + "td:nth-child(5)").text();
var long= $("this" + "td:nth-child(6)").text();
var ftime= $("this" + "td:nth-child(7)").text();
$.post('./watchdog.php',{ oadata: sitename});
}).end().remove();
I am selecting the "tr" in a a table except the first one which contains "th". Then I am just stripping the td and assigning the text value to variable. In the end I am posting this data to a PHP script that basically inserts this in a MySQL DB. the problem is i keep getting NULL value in the database.
Here is my Table as generated in the HTML
Site Name Type Address City Lat Long Fault Time
ISB056 Spur Opposite Plot No. 435, Sector I-10/4 Islamabad 73.04393 33.64677 12:02 PM
ISB078 Spur SEECS NUST H-12 Islamabad Islamabad 72.990044 33.64246 12:02 PM
This
$("this" + "td:nth-child(1)").text()
generates the selector thistd:nth-child(1) which does not make sense at all. I think you want:
$(this).find("td:nth-child(1)").text()
But easier would be to just take an array with e.g.
var data = $(this).children('td').map(function() {
return $(this).text();
}).get();
and then send the data like
$.post('./watchdog.php',{ sitename: data[0], type: data[1],...});
You musn't use 'this' but this so remove your quotes.
You also should use classes.
<tr class="site">
<td class="sitename">...</td>
...
</tr>
That will make your code easier to read and to debug:
$("tr.site").each(function(){
var sitename = $(".sitename",this)
})

Categories

Resources