I am trying to dynamically add objects from a json file to my HTML file. For some reason, when looping through the fetched json file, the properties of objects are undefined. Logging the entire json file into console works, the problem appears looping through the file for some reason. I have double checked, the title and body properties do exist for the json objects.
Here's the Javascript file:
window.onload = function() {
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then(json => {
console.log(json); // console output shows the objects fine
for (post in json) {
console.log(post.title);
var div = document.createElement('div');
var title = document.createElement('h1');
var body = document.createElement('p');
title.innerText = post.title;
body.innerText = post.body;
div.appendChild(title);
div.appendChild(body);
document.body.appendChild(div);
}
})
}
Here's the HTML file:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="src/js/script.js"></script>
<link rel="stylesheet" type="text/css" href="res/css/style.css">
<title>Lecture 7 - JS-4</title>
</head>
<body>
<h1> Fetch API</h1>
</body>
</html>
This is what the page looks like:
I have searched online and in most cases, the problem seems to be that the function fetching the data does not return anything. However, as I am not asking for the value of the function, this doesn't appear to be the problem in my case.
the for loop is wrong
try
for (const post of json) {...}
instead of
for (post in json) {...}
the for..of loop iterates over values while the for..in loop iterates over indices (for some strange legacy reasons)
also don't forget const or let since you introduce a new variable/constant called post inside the loop
Use of instead of in
for (post of json) {
After long searching on here and on the web I could not find what I require. Here is a distilled minimum verifiable example of my issue.
To set the background for the problem, here is the actual use case that I intend to implement. There is a heatmap on endpoint /heatmap, users can click on the cells and they should be redirected to a different endpoint /more-info-on-cell. Depending on the cell clicked, the user will get different results. I almost get the intended result. The only issue is that the page is not really properly rendered because the javascript of /more-info-on-cell does not get executed for some reason.
Here is the folder structure that you require for this example:
root
|-templates
| -index.html
| -page.html
|-index.py
And here are the files:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<a id="clickMe" style="cursor: pointer">Click here to send POST to render template on the backend</a>
</body>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
$("#clickMe").on('click', function() {
console.log('clicked, making a request')
$.ajax({
type: 'POST',
url: '/',
data: JSON.stringify({}),
contentType: 'application/json',
success: function(response) { window.document.write(response); }
})
})
})
</script>
</html>
page.html
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
The content of the document......
</body>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
alert('the doc is ready');
})
</script>
</html>
index.py
#!/usr/bin/env python3
from flask import Flask, request, render_template, jsonify
app = Flask(__name__)
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
return render_template('index.html')
elif request.method == "POST":
return render_template('page.html')
You can verify that if you click on the link in index.html you are successfully redirected to page.html but its javascript is never executed. And so you never get the alert that we have defined in page.html.
The problem should be solved by completely working with the flask backend here, since the templates in my actual use case have jinja variables in them too. Everything works perfectly if you make a request through a form with method having the correct url and action being POST. Although, as soon as I start making javascript client POST requests, it sort of works in that I get to the right page, but it is rendered incorrectly + javascript on document ready never gets executed.
For anyone who has such a use case too, ensure that you use a method post like below (put it inside the index.html in this case):
<script>
$(document).ready(function() {
$("#clickMe").on('click', function() {
console.log('clicked, making a request')
post('/', {'key-1': 'val-1'});
})
})
function post(path, params, method='post') {
const form = document.createElement('form');
form.method = method;
form.action = path;
for (const key in params) {
if (params.hasOwnProperty(key)) {
const hiddenField = document.createElement('input');
hiddenField.type = 'hidden';
hiddenField.name = key;
hiddenField.value = params[key];
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
}
</script>
This creates a hidden form on the web page and then submits the form, from then onwards, flask processes everything properly.
I am trying to access on or two data attributes from my personal Duolingo account. When punching in the following URL a JSON object is returned:
http://www.duolingo.com/users/username (I've replaced my id with "username" in the url).
var getDuolingoData = function() {
return $.ajax({
type: "get",
url: "http://www.duolingo.com/users/daniel692007&callback=?",
dataType: "json"
});
}
getDuolingoData().then(function(json) {
console.log(json.site_streak); // this log is causing the error
});
When I try and log a key from the returned object, however, the following error is logged to the console:
Uncaught SyntaxError: Unexpected token <
Ajax is fairly new to me, Duolingo don't have an API that I know of and as this is an external URL I believe the dataType must be set to jsonp due to CORS.
Is there an obvious reason that I am not able to access the JSON object I can see when visiting the specified URL?
TY
The same issue happened to me. I was unable to solve it using jsonp. What i ended up doing was to make an action in the controller that recieved the json from external url and send it to the ajax call.
For exmaple
return $.ajax({
type: "post",
url: "/ActionInProject/ProjectController",
});
then in the Controller it will be different for whichever server side language is being used. For me it was C# so i did something like
[HttpPost]
public JsonResult ActionInProject()
{
using(HttpClient client = new HttpClient())
{
var response = client.GetAsync("someothersite.com/api/link");
return Json(client.GetAsync());
}
}
I tried your request with Postman
and I found it not valid json in the respone you can find below what is returned of the server
<!DOCTYPE html><html dir="ltr"><head><title>Duolingo</title><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"><meta name="robots" content="NOODP"><noscript><meta http-equiv="refresh" content="0; url=/nojs/splash"></noscript><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-title" content="Duolingo"><meta name="google" content="notranslate"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-itunes-app" content="app-id=570060128"><meta name="google-site-verification" content="nWyTCDRw4VJS_b6YSRZiFmmj56EawZpZMhHtKXt7lkU"><link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/aiahmijlpehemcpleichkcokhegllfjl"><link rel="apple-touch-icon" href="//d35aaqx5ub95lt.cloudfront.net/images/duolingo-touch-icon.png"><link rel="shortcut icon" type="image/x-icon" href="//d35aaqx5ub95lt.cloudfront.net/favicon.ico"><link href="//d35aaqx5ub95lt.cloudfront.net/css/ltr-9f45956e.css" rel="stylesheet"> <link rel="manifest" href="/gcm/manifest.json"></head><body><div id="root"></div><script>window.duo={"detUiLanguages":["en","es","pt","it","fr","de","ja","zs","zt","ko","ru","hi","hu","tr"],"troubleshootingForumId":647,"uiLanguage":"en","unsupportedDirections":[],"oldWebUrlWhitelist":["^/activity_stream$","^/admin_tools$","^/c/$","^/certification/","^/comment/","^/course/","^/course_announcement/","^/courses$","^/courses/","^/debug/","^/dictionary/","^/discussion$","^/event/","^/forgot_password$","^/guidelines$","^/help$","^/j$","^/mobile$","^/p/$","^/pm/","^/power_practice/","^/preview/.+/","^/quit_classroom_session","^/redirect/","^/referred","^/register_user$","^/reset_password","^/settings/reset_lang","^/skill_practice/","^/team/","^/teams$","^/topic/","^/translation/","^/translations$","^/translations/","^/troubleshooting$","^/ui_strings/","^/upload$","^/vocab","^/welcome$","^/welcome/","^/word","^/words$"]}</script><script>window.duo.version="c89bfb9"</script><script>!function(r){function n(t){if(e[t])return e[t].exports;var s=e[t]={i:t,l:!1,exports:{}};return r[t].call(s.exports,s,s.exports,n),s.l=!0,s.exports}var t=window.webpackJsonp;window.webpackJsonp=function(e,i,o){for(var c,a,f,d=0,u=[];d<e.length;d++)a=e[d],s[a]&&u.push(s[a][0]),s[a]=0;for(c in i)Object.prototype.hasOwnProperty.call(i,c)&&(r[c]=i[c]);for(t&&t(e,i,o);u.length;)u.shift()();if(o)for(d=0;d<o.length;d++)f=n(n.s=o[d]);return f};var e={},s={31:0};n.e=function(r){function t(){c.onerror=c.onload=null,clearTimeout(a);var n=s[r];0!==n&&(n&&n[1](new Error("Loading chunk "+r+" failed.")),s[r]=void 0)}var e=s[r];if(0===e)return new Promise(function(r){r()});if(e)return e[2];var i=new Promise(function(n,t){e=s[r]=[n,t]});e[2]=i;var o=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.timeout=12e4,n.nc&&c.setAttribute("nonce",n.nc),c.src=n.p+""+({0:"js/vendor",1:"js/app",2:"strings/zh-TW",3:"strings/zh-CN",4:"strings/vi",5:"strings/uk",6:"strings/tr",7:"strings/tl",8:"strings/th",9:"strings/te",10:"strings/ta",11:"strings/ru",12:"strings/ro",13:"strings/pt",14:"strings/pl",15:"strings/pa",16:"strings/nl-NL",17:"strings/ko",18:"strings/ja",19:"strings/it",20:"strings/id",21:"strings/hu",22:"strings/hi",23:"strings/fr",24:"strings/es",25:"strings/en",26:"strings/el",27:"strings/de",28:"strings/cs",29:"strings/bn",30:"strings/ar"}[r]||r)+"-"+{0:"2b9feda7",1:"662ee5e7",2:"c444b0a9",3:"a5658bf8",4:"3ea447d8",5:"1573893a",6:"c32ed883",7:"52cac8bc",8:"2c58adbb",9:"681aaba6",10:"d05b78c6",11:"f4071afb",12:"a1349f5c",13:"6a57ec9f",14:"762dfc94",15:"8a02897a",16:"4e429b1e",17:"8e921ddf",18:"524cc86b",19:"8df42324",20:"7d8a8fc5",21:"4fde5d79",22:"509b8809",23:"9f09bcfb",24:"77da48d4",25:"44cfb321",26:"13b268cc",27:"c0cac402",28:"3ecdeec1",29:"dfd2b224",30:"074ffddd"}[r]+".js";var a=setTimeout(t,12e4);return c.onerror=c.onload=t,o.appendChild(c),i},n.m=r,n.c=e,n.d=function(r,t,e){n.o(r,t)||Object.defineProperty(r,t,{configurable:!1,enumerable:!0,get:e})},n.n=function(r){var t=r&&r.__esModule?function(){return r.default}:function(){return r};return n.d(t,"a",t),t},n.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},n.p="/",n.oe=function(r){throw console.error(r),r}}([])</script><script src="//d35aaqx5ub95lt.cloudfront.net/js/vendor-2b9feda7.js"></script> <script src="//d35aaqx5ub95lt.cloudfront.net/strings/en-44cfb321.js"></script> <script src="//d35aaqx5ub95lt.cloudfront.net/js/app-662ee5e7.js"></script></body></html>
as you can see the JSON is wrapped inside a HTML page which is not valid JSON syntax it something related to the API itself. it returns a page instead of JSON object .
In my Wordpress site, I want to create a dropdown menu with all the tags, but since it has more then 7.000, it needs to load only after the user click. I now it is only possible using Ajax, and I have started, but not accomplished yet.
http://jsfiddle.net/7kf1r9vw/2/
In this Fiddle, after the click, this javascript file is loaded:
/echo/js/?js=hello%20world!.
The second example in Fiddle is just to show my actual code. I want that the results populated by the php funcion only starts after the user click.
But I don't know how to change it to a PHP function. Also, is it possible to add plain PHP script in the output or only with it embedded in a file?
As Draco18s said, in your case, Javascript is executed client-side and PHP is executed server side.
In this case what you can do is a request to the server using Ajax.
For example, if you have the following html:
<select name="tag-dropdown" id="selectTags">
<option value="#">Select an artist</option>
</select>
You can use Ajax to make a POST request to a PHP script:
$( document ).ready(function() {
$.ajax({ url: 'merge2.php',
data: {operationName: "tagsNames"},
type: 'post',
success: function(output) {
tagNamesres = JSON.parse(output);
jQuery.each(tagNamesres, function(name, val) {
$("#selectTags").append('<option value="'+val+'">'+name+'</option>');
});
}
});
});
The PHP script can contain something like:
<?php
if (isset($_POST['operationName']) && !empty($_POST['operationName']) && $_POST["operationName"]=="tagsNames") {
$resultarray = array();
$resultarray["tagName1"] = "tagValue";
$resultarray["tagName2"] = "tagValue2";
echo json_encode($resultarray);
return;
}
?>
In order to return something from the PHP function you need to print or use echo.
This is just an example so you could start working with this :)
For more information about how Ajax request works, read this http://thisinterestsme.com/simple-ajax-request-example-jquery-php/
Your question appears to be "how do I call a php function on the server from my browser javascript code". In your code, you have the following function where the first parameter is the server side request:
function getSuccessOutput() {
getRequest(
'/echo/js/?js=hello%20world!', // demo-only URL
drawOutput,
);
return false;
}
To change that to call a server php routine, you would change '/echo/js/?js=hello%20world!' to the url on your server you want to have executed: e.g. '/myPHPFolder/someRoutine.php' . The results will be delivered back to your req object.
In your initial html file (e.g. index.html, or where ever you start your flow), include the onload option on the body tag:
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="yada yada yada">
<meta name="author" content="you">
<title>title for browser tab</title>
</head>
<body class="tutorial" onLoad="initPage()">
<select id="optionList"></select>
</body>
</html>
This tells the browser to run the "initPage()" function with no parameters. The initPage() function then issues an async call and loads up your data, perhaps saving it to a local variable in the process.
function initPage()
{
$('#optionList').empty(); // this ensures that you don't duplicate the info in the select list.
$.when($.get('/myPHPFolder/someRoutine.php').done(function(res)
{ // if of interest, save the results to a cookie or variable.
// you could also include a check to see if the data has already been downloaded.
// This is a 'happy path' example, which does not include error checking from the host
// build the optionList select HTML element
for (let each in res)
{(function(_idx, _arr)
{
// append the correct value to the select list.
// this example is based on returning a JSON object with an
// element called "id" which has the value I want to display in the options list
$('#optionList').append('<option value="'+_arr[_idx].id+'">' +_arr[_idx].id+'</option>');})(each, res);
}
// create a function to execute when the user selects a different buyer
$('#buyer').on('change', function() { /* do something useful here */ });
}
}