Related
I am making a website that grabs data from an API. The API essentially consists of a script normally ran as such
<script type="text/javascript" src="https://www.fxblue.com/users/dynascalp_demo/overviewscript"></script>
it will simply create an array and push the data I need from it, like this:
if (!document.MTIntelligenceAccounts) document.MTIntelligenceAccounts = new Array(); document.MTIntelligenceAccounts.push({ "userid": "dynascalp_demo","balance": 9275.95,"equity": 9275.95,"closedProfit": -724.05,"floatingProfit": 0,"freeMargin": 9275.95,"totalDeposits": 10000,"totalWithdrawals": 0,"totalBankedGrowth": -7.24,"monthlyBankedGrowth": -0.67,"weeklyBankedGrowth": -0.16,"dailyBankedGrowth": -0.03,"bankedProfitFactor": 0.66,"deepestValleyCash": -819.04,"deepestValleyPercent": -8.19,"troughInBalance": 9175.79,"peakInBalance": 10020.11,"historyLengthDays": 331,"averageTradeDurationHours": 2.17,"worstDayPercentage": -1.44,"worstWeekPercentage": -2.32,"worstMonthPercentage": -4.31,"tradesPerDay": 2.5,"totalClosedPositions": 589,"totalOpenPositions": 0,"bankedWinningTrades": 382,"bankedLosingTrades": 207,"bankedBreakEvenTrades": 0,"bankedWinPips": 1486.3,"bankedLossPips": -1604.6,"initialDeposit": 10000,"totalBankedPips":-118.3,"totalOpenPips":0,"peakPercentageLossFromOutset": -8.24,"riskReturnRatio": -1.21,"openAndPendingOrders": []});
My idea is to run this code conditionally, in another, bigger script. I will query my database and check whether the data is already in the database. If it is, then skip the request altogether and send the data from the database through an ajax request handled by the server, which will return a JSON. If it isn't or the data has expired, meaning it has not been updated for at least a day, it should grab the data from the API and update the database. This is done by the front-end as there is no Node.js support in the back-end.
The only thing I'm missing is how I should execute this script from mine, instead of calling it directly in the HTML.
For example, Fetch() does not work. I believe the request is malformed, or it is not the type of request it expects. Unexpected end of input is thrown and the request does not succeed.
This code should result in a number being shown
function fxBlue_retrieveAPI() {
document.MTIntelligenceAccounts = new Array();
const url = "https://www.fxblue.com/users/dynascalp_demo/overviewscript";
//var fxblue_API_Names = ["dynascalp_demo", "fxprogoldrobot", "fxprosilverrobot", "forex_gump_ea"];
var varNames = ["totalDeposits", "balance", "totalBankedGrowth", "monthlyBankedGrowth", "deepestValleyPercent", "historyLengthDays"];
var experts = [];
var s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", url);
document.body.appendChild(s);
for (var i = 0; i < document.MTIntelligenceAccounts.length; i++) {
experts.push({ name: document.MTIntelligenceAccounts[i].userid, id: i });
if (document.getElementById(experts[i].name + varNames[0])) { document.getElementById(experts[i].name + varNames[0]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].totalDeposits; }
if (document.getElementById(experts[i].name + varNames[1])) { document.getElementById(experts[i].name + varNames[1]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].balance; }
if (document.getElementById(experts[i].name + varNames[2])) { document.getElementById(experts[i].name + varNames[2]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].totalBankedGrowth + "%" };
if (document.getElementById(experts[i].name + varNames[3])) { document.getElementById(experts[i].name + varNames[3]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].monthlyBankedGrowth };
if (document.getElementById(experts[i].name + varNames[4])) { document.getElementById(experts[i].name + varNames[4]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].deepestValleyPercent + "%" };
if (document.getElementById(experts[i].name + varNames[5])) { document.getElementById(experts[i].name + varNames[5]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].historyLengthDays };
}
}
<script type="text/javascript" src="/cdn-cgi/scripts/API/jquery-3.1.1.min.js" async></script>
<script type="text/javascript" src="/cdn-cgi/scripts/API/test.js"></script>
<body onload="fxBlue_retrieveAPI()">
<h3>Total banked growth data example</h3>
<p id="dynascalp_demototalBankedGrowth"></p>
</body>
Assuming (or hoping) that you won't experience CORS problems with your data source (here at SO it is not possible to reach the source), you could do something like this to get to the actual contents of the script file:
const actualText=`if (!document.MTIntelligenceAccounts) document.MTIntelligenceAccounts = new Array();\ndocument.MTIntelligenceAccounts.push({\n"userid": "dynascalp_demo","balance": 9275.95,"equity": 9275.95,"closedProfit": -724.05,"floatingProfit": 0,"freeMargin": 9275.95,"totalDeposits": 10000,"totalWithdrawals": 0,"totalBankedGrowth": -7.24,"monthlyBankedGrowth": -0.67,"weeklyBankedGrowth": -0.16,"dailyBankedGrowth": -0.03,"bankedProfitFactor": 0.66,"deepestValleyCash": -819.04,"deepestValleyPercent": -8.19,"troughInBalance": 9175.79,"peakInBalance": 10020.11,"historyLengthDays": 331,"averageTradeDurationHours": 2.17,"worstDayPercentage": -1.44,"worstWeekPercentage": -2.32,"worstMonthPercentage": -4.31,"tradesPerDay": 2.5,"totalClosedPositions": 589,"totalOpenPositions": 0,"bankedWinningTrades": 382,"bankedLosingTrades": 207,"bankedBreakEvenTrades": 0,"bankedWinPips": 1486.3,"bankedLossPips": -1604.6,"initialDeposit": 10000,"totalBankedPips":-118.3,"totalOpenPips":0,"peakPercentageLossFromOutset": -8.24,"riskReturnRatio": -1.21,"openAndPendingOrders": []});`;
// fetch("https://www.fxblue.com/users/dynascalp_demo/overviewscript")
fetch("https://jsonplaceholder.typicode.com/users/3") // (some dummy data source for demo purposes only)
.then(r=>r.text())
.then(text=>{ text=actualText; // mimmic the text received from www.fxblue.com ...
obj=JSON.parse(text.replace(/(?:.|\n)*push\(/,"").replace(/\);$/,""))
console.log(obj)
})
It is then up to you to decide whether you want to use the data or not.
Whatever you do, it is important that the action happens in the callback function of the last . then() call. Alternatively you can of course also work with an async function and use await inside.
My idea is to run this javascript code conditionally, by querying my database
For this you could do an jquery ajax call, and act based on the response you get. I recommend using jquery for the ajax call. Here is the jquery ajax call where you pass whatever data is necessary to the controller.
$.ajax({
type: "GET",
url: "/ControllerName/ActionName",
data: { data: UserIdOrSomething },
success: function(response) {
// you can check the response with an if, and implement your logic here.
}
});
You can place this ajax call in the document.ready function to automatically call it whenever the page loads.
$( document ).ready(function() {
// place the above code here.
});
As an example, here is how an asp.net core controller action would look like that would handle this ajax call;
[HttpGet("ActionName/{data:string}")]
public IActionResult ActionName(string data) // parameter name should match the one in jquery ajax call
{
// do stuff. query your database etc.
var response = "the response";
return Json(response);
}
finally, keep in mind handling sensitive data in javascript is generally not a good idea, as the javascript code is open to everyone. For example, in this case, you will be giving out a way for people to check if a user exists in your database, which could be problematic. I would suggest using guid as id for the users table if possible, which may or may not be useful to mitigate the dangers depending on how you query the database.
Attempting to create a script that will pull information from an API requested XML document and put it into a 2D array.
Upon making the Get request
https://api.example.com/v1.svc/users?apikey=MY-KEY&source=MY-APP&limit=1000
An XML is produced for each user looking like
<User>
<Id>Rdh9Rsi3k4U1</Id>
<UserName>firstlast#email.com</UserName>
<FirstName>First</FirstName>
<LastName>Last</LastName>
<Active>true</Active>
<Email>firstlast#email.com</Email>
<AccessLevel>Learner</AccessLevel>
</User>
Each user has a similar looking output stacked on top of each other. How could this be scrubbed into an array? Example, the first array would have 7 "columns" with all shown information with each user having a row.
b
So I figured it out for anyone looking for an answer to this type of question in the future. Basically, I found out that the API I was trying to reach (not actually "citrowske.com" as shown in the example) did not allow for CORS or jsonp which left me with the only option of using a Proxy.
Shown is an example of code similar to what I ended up using (below), along with the test XML file shown here
A basic explanation of how this works, it uses the proxy to get the XML file and stores it as "xml" found as "function(xml)". Then the XML doc is searched and each section that starts with "User" gets the "FirstName" and "LastName" data pulled from it and appended to dropdown in the HTML section named "yourdropdownbox".
$.ajaxPrefilter( function (options) {
if (options.crossDomain && jQuery.support.cors) {
var http = (window.location.protocol === 'http:' ? 'http:' : 'https:');
options.url = http + '//cors-anywhere.herokuapp.com/' + options.url;
//options.url = "http://cors.corsproxy.io/url=" + options.url;
}
});
$.get(
'http://citrowske.com/xml.xml',
function (xml) {
//console.log("> ", xml);
//$("#viewer").html(xml);
////////////////////////////////////
var select = $('#yourdropdownbox');
select.append('<option value="">Select a User</option>');
$(xml).find('User').each(function(){
var FirstNames = $(this).find('FirstName').text();
var LastNames = $(this).find('LastName').text();
select.append("<option value='"+ FirstNames +"'>"+FirstNames+" "+LastNames+"</option>");
});
}
////////////////////////////////////
);
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<select id="yourdropdownbox">
</select>
As a note, Proxy's are not known for being extremely secure, so watch out what you use this for.
Also, if I wanted to turn the data into an array instead of appending it each time I could have added
var firstnamesarray = ["0"];
var lastnamesarry = ["0"];
var i = 0;
Above the top row of forward-slashes and then replaced:
var FirstNames = $(this).find('FirstName').text();
var LastNames = $(this).find('LastName').text();
with
firstnamesarry[i] = $(this).find('FirstName').text();
lastnamesarry[i] = $(this).find('LastName').text();
i = i+1;
and replaced the "select.append" First & Last Names with
firstnamearry[i] & lastnamearry[i]
To view a working example, check out the jsfiddle here
I'm trying to read from xml file text and remove nodes where that text was.
For example I read the text from <en> or <es> nodes and I need to remove those both nodes and save text to <translation>
xml file before editing:
<?xml version="1.0" encoding="UTF-8" ?>
<books>
<name>
<translation>
<en>
Text 1
</en>
<es>
Text 2
</es>
</translation>
</name>
</books>
So it should looks like this:
<books>
<translation>
Text 1
</translation>
</books>
this is part of my function where after reading text from <en> I'm trying to remove all child nodes from <translation> but I'm stuck and getting an error when I'm using removeChild method
var lang = 'en';
$.ajax({
type: "GET",
url: 'my.xml',
dataType: "xml",
success: function (xml) {
$(xml).find('translation').each(function () {
var text = $(this).find(lang).text();
});
$(xml).getElementsByTagName('translation').parentNode.removeChild();
// xml convert to json
var books = [];
var data = $.xml2json(xml)['#document'];
that.books = data.books;
}
});
I'll appreciate any help. Thank you!
var value = [];
$.get("your.xml", function(d){
values.push($(d).find("en")[0].childNodes[0]); //push "Text 1" into array
$(d).find("en").remove(); //remove node
$(d).find("es").remove(); //remove node
$(d).find("translation").append(values[0]); //add "Text 1" to node
});
The problem you're running into is that you're attempting to use a jQuery object as a DOM node here:
$(xml).getElementsByTagName('translation').parentNode.removeChild();
To use jQuery, you would do
$(xml).find('translation').remove();
HOWEVER, I think you're possibly headed down a false path. I see you've declared a var text that isn't used anywhere in your code. You probably want to do something like the following:
$xml = $(xml);
$xml.find('translation').each(function () {
$this = $(this);
$this.text($this.find(lang).text());
});
// do something with $xml...
I couldn't get xml2json working, as I'm not familiar with that plugin, but it should be relatively straightforward for you.
tried this?
getElementsByTagName('translation').children().remove()
Situation
I'm using a custom error XPage, based highly off of the XSnippet from Tony McGuckin. It works rather well but I would like for the browser to execute a client-side JavaScript block (or load and run a JS file from a given URL). If I navigate directly to the custom error XPage, it loads correctly, but given the nature of how it loads on redirect from a SSJS runtime error, it seems to load any attempts at loading a script block in the head tag, inside the body tag. I've attempted passing through a JS script tag in the body (shown in the code below), attempted using the xp:headTag inside xp:resources, and attempted via an xp:script tag in xp:resources.
Browser's Perspective
From the browser's perspective, after encountering a runtime error during an event that invokes SSJS during a partial refresh, the xhr being invoked returns with a 500 and sets the content into the body tag (screen shot).
When viewing the response contents, the entire custom error XPage is there, including the <script type="text/javascript">console.log("hello world");<script>. This does not seem to trigger or put anything out to the JS console of the browser. What is visible via the JS console is some garbage from dojo complaining about getting back an XHR with response code of 500 (my dojoConfig is set to isDebug: true via xsp.client.script.dojo.djConfig in XSP Properties).
Question
Is there a way to get a client-side JS script tag to load and execute in the browser after an error 500 which occurs during the loading of a custom error XPage?
Here's the code for my Error page. To reproduce my results, invoke an SSJS action resulting in a runtime error (such as the ErrorOnClick XPage included in the OpenLog Logger for XPages project from Paul Withers) with a partial refresh event.
Error.xsp (set as the error page in XSP Properties)
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/xsp/core xsdxp://localhost/xsp~core.xsd"
pageTitle="${javascript:database.getTitle() + ' | Error'}">
<style
type="text/css"><![CDATA[
body {
background-color: lightblue;
}
form {
width: 1000px !important;
margin: 0 auto !important;
background-color: white !important;
margin-top: 2rem !important;
padding: 0.5rem !important;
height: auto;
}
.xspTextLabel {
font-weight: bold !important;
}
]]></style>
<img
class="logo-simple"
src="//placehold.it/124x32" />
<xp:panel>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
style="font-weight:bold;font-size:12pt"
value="An Unexpected Error Has Occurred:">
</xp:label>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
value="Error:"></xp:label>
<xp:br />
<xp:text
escape="false">
<xp:this.value><![CDATA[#{javascript:if( !!requestScope.error ){
var output = (requestScope.error.toString() || null)+"<br /><br />";
if(requestScope.error instanceof com.ibm.xsp.exception.XSPExceptionInfo){
var codeSnippet = requestScope.error.getErrorText();
var control = requestScope.error.getErrorComponentId();
var cause = requestScope.error.getCause();
output += "In the control : " + control + "<br /><br />";
if(cause instanceof com.ibm.jscript.InterpretException){
var errorLine = cause.getErrorLine();
var errorColumn = cause.getErrorCol();
output += "At line " + errorLine;
output += ", column " + errorColumn + " of:<br />";
}else{
output += "In the script:<br />";
}
if( #Contains(codeSnippet,"#{javascript:") ){
var snipAr = codeSnippet.split("#{javascript:");
var tmpSnip = snipAr[1];
var nwSnip = tmpSnip.substring(0, tmpSnip.length - 1);
output += "#{javascript:<br /><pre>"+nwSnip+"</pre>}"
}else{
output += codeSnippet;
}
}
return output;
}else{
return "";
}}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
value="Stack Trace:"></xp:label>
<xp:br />
<xp:text
escape="false"
style="font-size:10pt">
<xp:this.value><![CDATA[#{javascript:if( !!requestScope.error ){
var stackTrace = "";
var trace = (requestScope.error.getStackTrace() || null);
if(trace != null){
for(var i = 0; i < trace.length; i++){
stackTrace += trace[i] + "<br/>";
}
return "<pre>"+stackTrace+"</pre>";
}else{
return "nothing";
}
}else{
return "";
}}]]></xp:this.value>
</xp:text>
</xp:panel>
<script
type="text/javscript">
<![CDATA[console.log("Hello world...");]]>
</script>
</xp:view>
For what it's worth: I didn't find anything explicitly on this subject via a search of Google or StackOverflow.
UPDATE 1:
This was a case of either caffeine deprivation or just not seeing the forest through the trees. It helps to not use a CDATA block in your HTML code. The lazy developer in me tried copying and pasting between an xp:script block and the HTML <script> block, preserving it. Now for the public shaming of buying Marky beer in Atlanta.
UPDATE 2:
Marky's beverage of choice may be in peril. While I seem to have had issues with copying a CDATA tag in, the issue remains. In my efforts to produce a simplified page with a button to error out (loosely based on the above mentioned XPage from the OpenLog Logger for XPages ErrorOnClick.xsp), I mistakenly took out a part of what was causing my issues in the first place, the partial refresh. When I do a full refresh, no issue, but when I do a partial, it doesn't load. I'm enclosing a sample page to trigger an error, with two buttons; one to induce a full, the other a partial. SO, with a full refresh, I get an alert of "hello world...", with the partial, no dice.
MakeSomeError.xsp
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:panel
id="somePanel">
<xp:button
value="Failing Partial"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="somePanel">
<xp:this.action><![CDATA[#{javascript:var a:NotesDateTime = null;
viewScope.myStuff = a.toJavaDate().toDateString();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button
value="Failing Full"
id="button2">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var a:NotesDateTime = null;
viewScope.myStuff = a.toJavaDate().toDateString();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br />
<xp:text
value="#{viewScope.myStuff}" />
</xp:panel>
</xp:view>
UPDATE 3:
Okay. Sven's second answer has me very close, but for some reason I can't extrapolate just enough to get what I want to happen to occur. I'm including a GIF below of my results. The only thing different I would like to have happen is for my Error.xsp (custom error XPage) to continue loading after I encounter the error (it seems like I'll need to change the beforeRenderResponse block to an afterRenderResponse script perchance?). I want to append the script, not replace the Error.xsp loading. Basically, I'm trying to run a script after the error XPage is loaded (there's a helper JS file I'm trying to load into my custom error XPage, CSS is loading fine, just not the JS lib). I would love to:
get this working
share what it is (it's kind of cool, if I do say so myself)
Don't use CDATA in your script tags.
<script>
alert('hi Marky!');
</script>
Works for me.
OK, last try. A very interesting one. On your Error.xsp, add the following image:
<xp:text
escape="true"
id="executeOnAjax"
tagName="img">
<xp:this.attrs>
<xp:attr
name="src"
value="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==">
</xp:attr>
<xp:attr
name="onload"
value="alert('Hello World!');this.parentNode.removeChild(this);">
</xp:attr>
</xp:this.attrs>
<xp:this.rendered>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
refreshId?true:false;}]]>
</xp:this.rendered>
</xp:text>
if this doesn't fit your requirements, I don't understand what you are trying to accomplish.
The reason for this is behaviour is that this is a security feature. Browsers don't execute <script> blocks if they where loaded via Ajax.
But there is a workaround:
First you have to add a div for replacement to your calling XPage:
<div id="errRefresh" />
This is just a placeholder for the partial refresh, otherwise it will fail.
Now, you have to modify your error page to handle the partial refreshs. To do this, you have to detect if it is a refresh or not, but you cannot use the build-in functionality (it is nulled in an error page). So you have to do this by your own:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
Now you must set the response status code to 200, otherwise the error method from the event is called:
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = facesContext.getExternalContext().getResponse();
resp.setStatus(200);
Then, you can add your CSJS block which must look like this:
<!-- XSP_UPDATE_SCRIPT_START -->
<script>
alert('Hello World!');
</script>
<!-- XSP_UPDATE_SCRIPT_END -->
When the parial refresh is processed, the refreshed DOM element is replaced, that's why we have to resend the HTML markup with the response, and overwrite the X-XspRefreshId to force replacement of our error element instead:
resp.setHeader('X-XspRefreshId', 'errRefresh' );
Last but not least, we have to skip the JSF lifecycle:
facesContext.responseComplete();
That's it.
Here is the complete code for the beforeRenderResponse event of the error page:
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
if( refreshId ){
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = ex.getResponse();
var writer:java.io.PrintWriter = resp.getWriter();
writer.write( "<!-- XSP_UPDATE_SCRIPT_START -->\n" );
writer.write( "<script>\n");
writer.write( "alert('Hello World!' );\n" );
writer.write( "</script>\n");
writer.write( "<!-- XSP_UPDATE_SCRIPT_END -->\n" );
writer.write( "<div id=\"errRefresh\" />\n");
resp.setStatus(200);
resp.setHeader('X-XspRefreshId', 'errRefresh' );
facesContext.responseComplete();
}
}]]>
</xp:this.beforeRenderResponse>
Keep in mind that this might result in security issues.
Marky has another good idea: Hijack the response.
This could look like this:
<script>
if( !dojo._xhr )
dojo._xhr = dojo.xhr;
var myHandler = function(){
var xhrObj = arguments[1].xhr;
var response = xhrObj.response;
var header = xhrObj.getResponseHeader('X-XspRefreshId');
if( header == "#error" ){
eval( response );
}else{
arguments[1]["error"]( arguments[0], arguments[1]);
}
}
dojo.xhr = function(){
try{
var args = arguments[1];
args["failOk"] = true;
args["error"] = myHandler;
arguments[1] = args;
}catch(e){}
dojo._xhr( arguments[0], arguments[1], arguments[2] );
}
</script>
The beforeRenderResponse event has to be modified like this:
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
if( refreshId ){
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = facesContext.getExternalContext().getResponse();
var writer:java.io.PrintWriter = resp.getWriter();
writer.write( "alert('Hello World!' );\n" );
resp.setHeader('X-XspRefreshId', '#error' );
facesContext.responseComplete();
}
}]]>
</xp:this.beforeRenderResponse>
I need to parse an RSS feed (XML version 2.0) and display the parsed details in an HTML page.
Parsing the Feed
With jQuery's jFeed
(Don't really recommend that one, see the other options.)
jQuery.getFeed({
url : FEED_URL,
success : function (feed) {
console.log(feed.title);
// do more stuff here
}
});
With jQuery's Built-in XML Support
$.get(FEED_URL, function (data) {
$(data).find("entry").each(function () { // or "item" or whatever suits your feed
var el = $(this);
console.log("------------------------");
console.log("title : " + el.find("title").text());
console.log("author : " + el.find("author").text());
console.log("description: " + el.find("description").text());
});
});
With jQuery and the Google AJAX Feed API
$.ajax({
url : document.location.protocol + '//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=10&callback=?&q=' + encodeURIComponent(FEED_URL),
dataType : 'json',
success : function (data) {
if (data.responseData.feed && data.responseData.feed.entries) {
$.each(data.responseData.feed.entries, function (i, e) {
console.log("------------------------");
console.log("title : " + e.title);
console.log("author : " + e.author);
console.log("description: " + e.description);
});
}
}
});
But that means you're relient on them being online and reachable.
Building Content
Once you've successfully extracted the information you need from the feed, you could create DocumentFragments (with document.createDocumentFragment() containing the elements (created with document.createElement()) you'll want to inject to display your data.
Injecting the content
Select the container element that you want on the page and append your document fragments to it, and simply use innerHTML to replace its content entirely.
Something like:
$('#rss-viewer').append(aDocumentFragmentEntry);
or:
$('#rss-viewer')[0].innerHTML = aDocumentFragmentOfAllEntries.innerHTML;
Test Data
Using this question's feed, which as of this writing gives:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:re="http://purl.org/atompub/rank/1.0">
<title type="text">How to parse a RSS feed using javascript? - Stack Overflow</title>
<link rel="self" href="https://stackoverflow.com/feeds/question/10943544" type="application/atom+xml" />
<link rel="hub" href="http://pubsubhubbub.appspot.com/" />
<link rel="alternate" href="https://stackoverflow.com/q/10943544" type="text/html" />
<subtitle>most recent 30 from stackoverflow.com</subtitle>
<updated>2012-06-08T06:36:47Z</updated>
<id>https://stackoverflow.com/feeds/question/10943544</id>
<creativeCommons:license>http://www.creativecommons.org/licenses/by-sa/3.0/rdf</creativeCommons:license>
<entry>
<id>https://stackoverflow.com/q/10943544</id>
<re:rank scheme="http://stackoverflow.com">2</re:rank>
<title type="text">How to parse a RSS feed using javascript?</title>
<category scheme="https://stackoverflow.com/feeds/question/10943544/tags" term="javascript"/><category scheme="https://stackoverflow.com/feeds/question/10943544/tags" term="html5"/><category scheme="https://stackoverflow.com/feeds/question/10943544/tags" term="jquery-mobile"/>
<author>
<name>Thiru</name>
<uri>https://stackoverflow.com/users/1126255</uri>
</author>
<link rel="alternate" href="https://stackoverflow.com/questions/10943544/how-to-parse-a-rss-feed-using-javascript" />
<published>2012-06-08T05:34:16Z</published>
<updated>2012-06-08T06:35:22Z</updated>
<summary type="html">
<p>I need to parse the RSS-Feed(XML version2.0) using XML and I want to display the parsed detail in HTML page, I tried in many ways. But its not working. My system is running under proxy, since I am new to this field, I don't know whether it is possible or not. If any one knows please help me on this. Thanks in advance.</p>
</summary>
</entry>
<entry>
<id>https://stackoverflow.com/questions/10943544/-/10943610#10943610</id>
<re:rank scheme="http://stackoverflow.com">1</re:rank>
<title type="text">Answer by haylem for How to parse a RSS feed using javascript?</title>
<author>
<name>haylem</name>
<uri>https://stackoverflow.com/users/453590</uri>
</author>
<link rel="alternate" href="https://stackoverflow.com/questions/10943544/how-to-parse-a-rss-feed-using-javascript/10943610#10943610" />
<published>2012-06-08T05:43:24Z</published>
<updated>2012-06-08T06:35:22Z</updated>
<summary type="html"><h1>Parsing the Feed</h1>
<h3>With jQuery's jFeed</h3>
<p>Try this, with the <a href="http://plugins.jquery.com/project/jFeed" rel="nofollow">jFeed</a> <a href="http://www.jquery.com/" rel="nofollow">jQuery</a> plug-in</p>
<pre><code>jQuery.getFeed({
url : FEED_URL,
success : function (feed) {
console.log(feed.title);
// do more stuff here
}
});
</code></pre>
<h3>With jQuery's Built-in XML Support</h3>
<pre><code>$.get(FEED_URL, function (data) {
$(data).find("entry").each(function () { // or "item" or whatever suits your feed
var el = $(this);
console.log("------------------------");
console.log("title : " + el.find("title").text());
console.log("author : " + el.find("author").text());
console.log("description: " + el.find("description").text());
});
});
</code></pre>
<h3>With jQuery and the Google AJAX APIs</h3>
<p>Otherwise, <a href="https://developers.google.com/feed/" rel="nofollow">Google's AJAX Feed API</a> allows you to get the feed as a JSON object:</p>
<pre><code>$.ajax({
url : document.location.protocol + '//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=10&callback=?&q=' + encodeURIComponent(FEED_URL),
dataType : 'json',
success : function (data) {
if (data.responseData.feed && data.responseData.feed.entries) {
$.each(data.responseData.feed.entries, function (i, e) {
console.log("------------------------");
console.log("title : " + e.title);
console.log("author : " + e.author);
console.log("description: " + e.description);
});
}
}
});
</code></pre>
<p>But that means you're relient on them being online and reachable.</p>
<hr>
<h1>Building Content</h1>
<p>Once you've successfully extracted the information you need from the feed, you need to create document fragments containing the elements you'll want to inject to display your data.</p>
<hr>
<h1>Injecting the content</h1>
<p>Select the container element that you want on the page and append your document fragments to it, and simply use innerHTML to replace its content entirely.</p>
</summary>
</entry></feed>
Executions
Using jQuery's Built-in XML Support
Invoking:
$.get('https://stackoverflow.com/feeds/question/10943544', function (data) {
$(data).find("entry").each(function () { // or "item" or whatever suits your feed
var el = $(this);
console.log("------------------------");
console.log("title : " + el.find("title").text());
console.log("author : " + el.find("author").text());
console.log("description: " + el.find("description").text());
});
});
Prints out:
------------------------
title : How to parse a RSS feed using javascript?
author :
Thiru
https://stackoverflow.com/users/1126255
description:
------------------------
title : Answer by haylem for How to parse a RSS feed using javascript?
author :
haylem
https://stackoverflow.com/users/453590
description:
Using jQuery and the Google AJAX APIs
Invoking:
$.ajax({
url : document.location.protocol + '//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=10&callback=?&q=' + encodeURIComponent('https://stackoverflow.com/feeds/question/10943544'),
dataType : 'json',
success : function (data) {
if (data.responseData.feed && data.responseData.feed.entries) {
$.each(data.responseData.feed.entries, function (i, e) {
console.log("------------------------");
console.log("title : " + e.title);
console.log("author : " + e.author);
console.log("description: " + e.description);
});
}
}
});
Prints out:
------------------------
title : How to parse a RSS feed using javascript?
author : Thiru
description: undefined
------------------------
title : Answer by haylem for How to parse a RSS feed using javascript?
author : haylem
description: undefined
Another deprecated (thanks to #daylight) option, and the easiest for me (this is what I'm using for SpokenToday.info):
The Google Feed API without using JQuery and with only 2 steps:
Import the library:
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("feeds", "1");</script>
Find/Load feeds (documentation):
var feed = new google.feeds.Feed('http://www.google.com/trends/hottrends/atom/feed?pn=p1');
feed.load(function (data) {
// Parse data depending on the specified response format, default is JSON.
console.dir(data);
});
To parse data, check documentation about the response format.
If you are looking for a simple and free alternative to Google Feed API for your rss widget then rss2json.com could be a suitable solution for that.
You may try to see how it works on a sample code from the api documentation below:
google.load("feeds", "1");
function initialize() {
var feed = new google.feeds.Feed("https://news.ycombinator.com/rss");
feed.load(function(result) {
if (!result.error) {
var container = document.getElementById("feed");
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
var div = document.createElement("div");
div.appendChild(document.createTextNode(entry.title));
container.appendChild(div);
}
}
});
}
google.setOnLoadCallback(initialize);
<html>
<head>
<script src="https://rss2json.com/gfapi.js"></script>
</head>
<body>
<p><b>Result from the API:</b></p>
<div id="feed"></div>
</body>
</html>
If you want to use a plain javascript API, there is a good example at https://github.com/hongkiat/js-rss-reader/
The complete description at https://www.hongkiat.com/blog/rss-reader-in-javascript/
It uses fetch method as a global method that asynchronously fetches a resource. Below is a snap of code:
fetch(websiteUrl).then((res) => {
res.text().then((htmlTxt) => {
var domParser = new DOMParser()
let doc = domParser.parseFromString(htmlTxt, 'text/html')
var feedUrl = doc.querySelector('link[type="application/rss+xml"]').href
})
}).catch(() => console.error('Error in fetching the website'))
For anyone else reading this (in 2019 onwards) unfortunately most JS RSS reading implementations don't now work. Firstly Google API has shut down so this is no longer an option and because of the CORS security policy you generally cannot now request RSS feeds cross-domains.
Using the example on https://www.raymondcamden.com/2015/12/08/parsing-rss-feeds-in-javascript-options (2015) I get the following:
Access to XMLHttpRequest at 'https://feeds.feedburner.com/raymondcamdensblog?format=xml' from origin 'MYSITE' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is correct and is a security precaution by the end website but does now mean that the answers above are unlikely to work.
My workaround will probably be to parse the RSS feed through PHP and allow the javascript to access my PHP rather than trying to access the end-destination feed itself.
I was so exasperated by many misleading articles and answers that I wrote my own RSS reader:
https://gouessej.wordpress.com/2020/06/28/comment-creer-un-lecteur-rss-en-javascript-how-to-create-a-rss-reader-in-javascript/
You can use AJAX requests to fetch the RSS files but it will work if and only if you use a CORS proxy. I'll try to write my own CORS proxy to give you a more robust solution. In the meantime, it works, I deployed it on my server under Debian Linux.
My solution doesn't use JQuery, I use only plain Javascript standard APIs with no third party libraries and it's supposed to work even with Microsoft Internet Explorer 11.
You can use jquery-rss or Vanilla RSS, which comes with nice templating and is super easy to use:
// Example for jquery.rss
$("#your-div").rss("https://stackoverflow.com/feeds/question/10943544", {
limit: 3,
layoutTemplate: '<ul class="inline">{entries}</ul>',
entryTemplate: '<li>[{author}#{date}] {title}<br/>{shortBodyPlain}</li>'
})
// Example for Vanilla RSS
const RSS = require('vanilla-rss');
const rss = new RSS(
document.querySelector("#your-div"),
"https://stackoverflow.com/feeds/question/10943544",
{
// options go here
}
);
rss.render().then(() => {
console.log('Everything is loaded and rendered');
});
See http://jsfiddle.net/sdepold/ozq2dn9e/1/ for a working example.
Trying to find a good solution for this now, I happened upon the FeedEk jQuery RSS/ATOM Feed Plugin that does a great job of parsing and displaying RSS and Atom feeds via the jQuery Feed API. For a basic XML-based RSS feed, I've found it works like a charm and needs no server-side scripts or other CORS workarounds for it to run even locally.
I did not find a solution for parsing RSS just with js due to CORS error I kept receiving. Installing a plugin is not an option for me and building a proxy is not fun either and the small solutions I found didn't work.
So just in case someone is getting here and can use server-side, I found this solution in PHP that worked for me perfectly! (without the CORS error! "x has been blocked by CORS policy...")