formatting HTML from arrays in a JSON file - javascript

I've gone through 5-6 questions on SO before asking _ The other questions appear to refer to formatting all the data from complete JSON files _ This question is about formatting the elements of an array parsed from a JSON file _
The complete test page is online here but for the sake of brevity on this page I have shortened the JSON data _
{
"PGRgames" : [
{
"id" : "Abzu",
"fullName" : "Abzû",
"PEGI" : 7,
"platforms" : ["PS4"],
"genres" : ["Adventure", "Simulation"],
"image" : "img_Abzu.png",
"details" : "ABZÛ is a beautiful underwater adventure that evokes the dream of diving. Immerse yourself in a vibrant ocean world full of mystery and bursting with colour and life."
},
{
"id" : "AdventurePirates",
"fullName" : "Adventure Time: Pirates Of The Enchridion",
"PEGI" : 7,
"platforms" : ["XBoxOne", "PS4", "Switch"],
"genres" : ["Adventure", "Sandbox", "KIDS"],
"image" : "img_AdventurePirates.png",
"details" : "The Land of Ooo is underwater and it’s up to Finn and Jake to find out why. Join our heroes as they explore the high seas."
},
{
"id" : "KingdomCome",
"fullName" : "Kingdom Come: Deliverance",
"PEGI" : 18,
"platforms" : ["XBoxOne", "XBoxOneX", "PS4"],
"genres" : ["Action", "RPG"],
"image" : "img_KingdomCome.png",
"details" : "Massive realistic open world: Majestic castles, vast fields, all rendered in stunning high-end graphics. Solve quests in multiple ways, then face the consequences of your decisions."
}
]
}
My JS code is _
<script>
let askHTTP = new XMLHttpRequest();
askHTTP.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
let gamesList = JSON.parse(askHTTP.responseText);
let output = '';
let PGRgames = gamesList.PGRgames;
for (let i = 0; i < PGRgames.length; i++) {
output += '<div class="col-md-3 col-sm-6 padBox"><div class="gameBox center"><img src="media/'
+ PGRgames[i].image
+ '" /><div class="horizBuffer"></div>'
+ '<div class="center"><span class="fontBrand1 smallText"><strong>' + PGRgames[i].genres + '</strong></span></div>'
+ '<div class="horizBuffer"></div>'
+ '<div class="left"><span class="fontBlack text">' + PGRgames[i].details + '</span></div>'
+ '<div class="horizBuffer"></div>'
+ '<div class="center"><span class="fontBlack text"><strong>' + PGRgames[i].platforms + '</strong></span></div>'
+ '</div></div>';
}
document.getElementById('displayGames').innerHTML = output;
}
};
askHTTP.open("GET", "js/PGRgames.json", true);
askHTTP.send();
</script>
If you look at the content on the page I have linked to you'll see that PGRgames.genres & PGRgames.platforms have commas but no spaces between the array elements _ Also that the arrays are not conforming to the area they are supposed to be confined to _
Formatting these two arrays is specifically what my question refers to _ I'd be grateful for any assistance : )

You can use .join() to format array to string.
var array = ["foo", "bar", "tet"];
console.log('formatted:', array.join(', '));
console.log('tostring:', ''+array);
In your case replace PGRgames[i].genres with PGRgames[i].genres.join(', ') and other array outputs in similar manner.

your problem is that you write this line:
PGRgames[1].platforms
and it is like writing this:
PGRgames[1].platforms.toString()
and what it does is just use the tostring of each element in the array and connect them with comma.
what you need to do is use join to tha array and format it as you need like this:
PGRgames[1].platforms.join(', ')

Related

Javascript - How to get star rating from object array field rating?

I have this object array:
var test_data = [
{
"id" : "Test01", //Has to be a string form ID
"test_info" : "This is the test information",
"test_rating" : 5
},
{
"id" : "Test02", //Has to be a string form ID
"test_info" : "This is the test information",
"test_rating" : 2
}
As it stands I am using the following to populate a unordered list (but it is only populating the id)
fieldOutputId.forEach(function(id) {
$("#idList").append('<li>' + id +" "+ "</li>");
});
I am wondering how to get Stars appearing to the right with the value of test_rating that's stored in fieldOutputRating like showing up like this picture (Stars being the red text)
Fig1 :
-----EDIT-----
Thanks to #ChrisG this gets it inline, id & test_rating:
test_data.forEach(function(item) { $("#idList").append('<li>' + item.id+ '<div class="fa fa-star">'+ item.test_rating+ "</div></li>"); })
With the above code I am trying to get the stars to appear in-line (done) and as stars using font-awesome library with a class="fa fa-star
This is what the above is giving:
So its getting the test_rating but not giving any stars for it.
Any help is appreciated??
Something like this?
const test_data = [
{
"id" : "Test01",
"test_info" : "This is the test information",
"test_rating" : 5
},
{
"id" : "Test02",
"test_info" : "This is the test information",
"test_rating" : 2
}
];
test_data.forEach(({id, test_rating}) => {
$("#idList").append(`<li>${id} ${'*'.repeat(test_rating)}</li>`);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="idList" />
You need to build up the 'star rating' from the input, like below:
test_data.forEach(function(data) {
let rating_str = '';
for (let i = 0; i < data.rating; i++) rating_str += '*';
$("#idList").append('<li>' + data.id +" "+ "<span>" + rating_str + "</span></li>");
});
If your 'star' is some image, instead of just '*' like in my code example, you can do rating_str += '<img src="..." />'

Looping javascript object to print data

I have a javascript object that I would like to traverse and print data based off 1 common category.
Javascript object:
var $states = {
"AL" : {
"longname": "Alabama",
"lawOne": "Alabama Law 1",
"lawTwo": "Alabama Law 2",
"lawThree": "Alabama Law 3",
"region" : "Southeast"
},
"AK" : {
"longname": "Alaska",
"lawOne": "Alaska Law 1",
"lawTwo": "Alaska Law 2",
"lawThree": "Alaska Law 3",
"region" : "Northwest"
},
"AZ" : {
"longname": "Arizona",
"lawOne": "Arizona Law 1",
"lawTwo": "Arizona Law 2",
"lawThree": "Arizona Law 3",
"region" : "Southwest"
},
etc...
}
I am able too loop the array and get as granular as getting the console to log all of the states that are in the same region:
for (var key in $states) {
if ($states.hasOwnProperty(key)) {
var $getStateRegion = $states[key].region
if ($getStateRegion === "Northeast") {
console.log ($states[key].longname);
}
}
}
Once I try to loop through those and print a table with that data is where I am running into an issue. I want to be able to have a withthe 's longname, lawOne, lawTwo, and lawThree values input in there. What gives? Once I try running a for loops through this is where I'm hitting a roadblock. Thanks in advance!
Try the working code below.
var $states = {
"AL" : {
"longname": "Alabama",
"lawOne": "Alabama Law 1",
"lawTwo": "Alabama Law 2",
"lawThree": "Alabama Law 3",
"region" : "Southeast"
},
"AK" : {
"longname": "Alaska",
"lawOne": "Alaska Law 1",
"lawTwo": "Alaska Law 2",
"lawThree": "Alaska Law 3",
"region" : "Northwest"
},
"AZ" : {
"longname": "Arizona",
"lawOne": "Arizona Law 1",
"lawTwo": "Arizona Law 2",
"lawThree": "Arizona Law 3",
"region" : "Southwest"
}
};
var result = {};
Object.keys($states).forEach(function(key) {
if ($states[key]["region"] === "Southwest") {
result[key] = $states[key];
}
});
console.log(result);
You are on the right track. Before I continue, I would like to point out that you are using a for..in loop, but newer versions of Javascript support the for..of loop also, so this is something you may want to consider. The difference is that the for..in loop gives you the keys of the object, and the for..of loop gives you the values, so it shortens the code by skipping the step where you write something to the effect of:
for( var index in array ){
var currentObject = array[ index ];
}
The secret to your solution is in how you handle the DOM, and there are many ways to do this. I will show you one, but it is not necessarily the fastest or the best. I recommend playing around with different DOM manipulations to find the one that works best for you.
First, we know how to get a record, so the javascript aspect of looping, you have that handled...
Next, we need to create the table.... I will assume that you want four columns based on your description, but you could easily adjust this to put the state name and one law on each line, which would probably be a better design allowing a variable number of laws.
The html would look something like this:
<table>
<tr><th>State</th><th>Law 1</th><th>Law 2</th><th>Law 3</th></tr>
<!-- Here is where we would create new html for each state -->
</table>
Your loop then would need to add to this html by creating several lines that appear as:
<tr><td>[State]</td><td>[Law1]</td><td>[Law2]</td><td>[Law3]</td><tr>
We will use string manipulation of the DOM, because it is a good place to start, because it is most similar to what you would write by hand.
We will break the table into three parts: the header, the body and the footer.
var header = "<table><tr><th>State</th><th>Law 1</th><th>Law 2</th><th>Law 3</th></tr>";
var body = ""; //this is where we add the data
var footer = "</table>";
Now, on the loop, we will create each line as needed and add it to the body:
for( var index in stateObj ){
...error checking occurs here...
var state = stateObj[ index ];
var stateColumn = "<td>" + state.longname + "</td>";
var law1Col = "<td>" + state.lawOne + "</td>";
var law2Col = "<td>" + state.lawTwo + "</td>";
var law3Col = "<td>" + state.lawThree + "</td>";
var row = "<tr>" + stateColumn + law1Col + law2Col + law3Col + "</tr>";
//now that we have a row, we add it to the body
body += row; //same as body = body + row;
}
After we have the body, we can make our table by combining the header, body and footer:
var tableHTML = header + body + footer;
And then we find a place to inject it into our document:
var outputDiv = document.getElementById( "stateTableData" );
outputDiv.innerHTML = tableHTML;
Here is a live example:
var states = {
PA: {
longname:"Pennsylvania",
lawOne:"It is illegal to sing in the shower in apartment buildings within the city limits of Philadelphia",
lawTwo:"All motorists are required to stop the vehicle for passing horsemen. The vehicle shall be covered with camoflage so as not to scare the horses.",
lawThree:"Any house having more than four women occupants shall be considered a brothel and shall be in violation of the law."
},
NJ: {
longname:"New Jersey",
lawOne:"There is no such thing as the Mafia",
lawTwo:"Any reference to the denizens of New Jersey shall be derogatory and degrading, think Jersey Shore",
lawThree:"There is no escape from New Jersey and we are not a suburb of NYC"
},
VA: {
longname:"Virginia",
lawOne: "Civil War re-enactments must have the North as the victor.",
lawTwo: "All roads shall end in Richmond, VA",
lawThree: "I have run out of silly ideas for this example."
}
};
function buildTableForState( stateNames ){
var stateList = stateNames.split(",");
//remove spaces
for( var i in stateList ){ stateList[i] = stateList[i].trim(); }
//initialize table parts
var header = "<table><tr><th>State</th><th>Law 1</th><th>Law 2</th><th>Law 3</th></tr>";
var footer = "</table>";
var body = "";
//build body
for( var index in states ){
if( stateList.indexOf( index ) !== -1 ){
var currentState = states[index];
body += buildRowForState( currentState );
}
}
//compose and inject table
var tableHTML = header + body + footer;
var documentOut = document.getElementById( "outputDiv" );
documentOut.innerHTML = tableHTML;
}
function submitTable(value){
buildTableForState( value );
}
function buildRowForState( currentState ){
var state = makeTableCol( currentState.longname );
var law1 = makeTableCol( currentState.lawOne );
var law2 = makeTableCol( currentState.lawTwo );
var law3 = makeTableCol( currentState.lawThree );
var row = makeTableRow( [state, law1, law2, law3] );
return row;
}
function makeTableCol( stringText ){
return "<td>" + stringText + "</td>";
}
function makeTableRow( arrayColumns ){
return "<tr>" + arrayColumns.join("") + "</tr>";
}
<h1>Table Loader</h1>
<form>
<p>Use the values "PA", "NJ", and "VA" to generate a table. You can use more than one value by separating them with a comma.</p>
<input type="text" id="stateNames" name="stateNames" /><br/>
<button onclick="submitTable(stateNames.value);">Build Table</button>
<p>Try:
</p>
<ul>
<li>PA</li>
<li>NJ,VA</li>
<li>VA,PA,NJ</li>
</ul>
</form>
<h1>Table Output appears here</h1>
<div id="outputDiv"></div>
NOTE: Regarding the live code, the HTML is bigger than the display box. In my browser I have to scroll down on the snippit HTML to view the generated table. Either that or expand the snippit window to a full tab to view it in a larger screen.
The above code is simplified, and most coders would tell you not to use HTMLElement.innerHTML, because it is slow, but it is a good place to start. Once you have this down, start practicing with document.create( tagName ), and then use more direct DOM manipulations.

Append only working for first child - looping issue?

I have a for loop that appends an ordered list with a list item and a load of other info.
It works great for the first list item, however, the others do not populate/append.
I'm having trouble finding which part of my code is causing the issue:
<script>
$(document).ready(function(){
var artistID =$(".artist").attr('id');
$.get("songs.php?id="+artistID, function(data){
for (var j=0; j < data.length; j++){
$(".songs."+data[j].ArtistNumber).append(
'<li class="song">'+
'<a class="song-link" onclick="songClick();return false;" href="'+data[j].FileLocation+'">'+
'<p>'+data[j].Title+'</p>'+
'<small>'+data[j].duration+'</small>'+
'</a>'+
'</li>'
);
}
},'json');
});
</script>
Any help would be appreciated.
Edit:
It turns out all I needed was a $(".artist").each(function(){}) wrapped around that.
I hope this can be of help to someone.
After many comments, I finally get your point. Here is how to proceed : you want a nested 'each' loop.
Since you did not provide any data structure, I will assume it looks like this :
data = {
"123" : [
{
"title" : "song 1",
"fileLocation" : "someURL",
"duration" : "2m45s"
},
{
"title" : "song 2",
"fileLocation" : "someOtherURL",
"duration" : "4m10s"
}
],
"456" : [
{
"title" : "song 1",
"fileLocation" : "someNewURL",
"duration" : "3m20s"
},
{
"title" : "song 2",
"fileLocation" : "someOtherNewURL",
"duration" : "4m17s"
}
]
}
I assume your data is an object containing key/values, each key is the artist number and each value is a list (array) of songs of this artist (represented as object with details).
and here's how to exploit the data :
$.each(data, function(artistNumber, songs){ // Iterating through each artist/songs pair. songs is an array
$list = $(".songs." + artistNumber); // Select each list one by one in the dom
$.each(songs, function(index, song){ // song is an object
$list.append(
'<li class="song">'+
'<a class="song-link" onclick="songClick();return false;" href="'+song.fileLocation+'">'+
'<p>'+song.title+'</p>'+
'<small>'+song.duration+'</small>'+
'</a>'+
'</li>'
);
});
});
See this fiddle in action.
I'm not shure about your DOM, I would need your HTML, but here is a try :
<script>
$(document).ready(function(){
var artistID =$(".artist").attr('id');
$.get("songs.php?id="+artistID, function(data){
for (var j=0; j < data.length; j++){
$(".artist").append($("<ul />").addClass(data[j].ArtistNumber).addClass('songs'));
$(".songs."+data[j].ArtistNumber).html(
'<li class="song">'+
'<a class="song-link" onclick="songClick();return false;" href="'+data[j].FileLocation+'">'+
'<p>'+data[j].Title+'</p>'+
'<small>'+data[j].duration+'</small>'+
'</a>'+
'</li>'
);
}
},'json');
});
And this still will not ,because you make selector for class,and this get first item with class artist,you need to change selector.Try thiseach

Accessing JSON object in Javascript variable

I am making a trivia game with JSON, and I am stuck. First off, I can pull my data from JSON and get it to display in a list after working through the documentation, like so:
$.getJSON('/trivia.json', function(data) {
var items = []
$.each(data, function (item, i) {
items.push('<li id="' + i.order + '">' + i.question + ' - ' + i.choices + i.correct + '</li>');
});
$('<ul/>', {
'class': 'my-new-list',
html: items.join('')
}).appendTo('#example');
});
That works fine at creating a list of the questions and answers, so I can confirm I am calling the local JSON file correctly.
My next step is to get the question and answer data to be stored in my Javascript variable for the quiz to wrok. When I made the example quiz, I store the data like so:
var quiz = [
{
"question" : "Q1: Who came up with the theory of relativity?",
"choices" : [
"Sir Isaac Newton",
"Nicolaus Copernicus",
"Albert Einstein",
"Ralph Waldo Emmerson"
],
"correct" : "Albert Einstein",
}];
I don't want the questions to be static, so I want JSON to take over supplying the question and choices.
I have tried to call the appendTo function for the quiz var, but it isn't passing the data through like it was when I created a list. Any ideas on how I can get the question, choices, and correct data sets to apply with the var quiz?
You may update your code to add the grapped data from Ajax request to quiz variable and iterate over choices to print them like the following code:
var quiz = [
{
"question" : "Q1: Who came up with the theory of relativity?",
"choices" : [
"Sir Isaac Newton",
"Nicolaus Copernicus",
"Albert Einstein",
"Ralph Waldo Emmerson"
],
"correct" : "Albert Einstein",
}];
$.getJSON('/trivia.json', function(data) {
var items = []
$.each(data, function (item, i) {
var q = {"question": i.question, "correct": i.correct, "choices": []};
var choicesStr = "";
$.each(i.choices, function (it, choice) {
choicesStr += "<span>choice</span></br>";
q.choices.push(choice);
}
quiz.push(q);
items.push('<li id="' + i.order + '">' + i.question + ' - ' + choicesStr + i.correct + '</li>');
});
$('<ul/>', {
'class': 'my-new-list',
html: items.join('')
}).appendTo('#example');
});

Pulling parts of JSON out to display them in a list

I have the following JSON:
var questions = {
section: {
"1": question: {
"1": {
"id" : "1a",
"title": "This is question1a"
},
"2": {
"id" : "1b",
"title": "This is question2a"
}
},
"2": question: {
"1": {
"id" : "2a",
"title": "This is question1a"
},
"2": {
"id" : "2b",
"title": "This is question2a"
}
}
}
};
NOTE: JSON changed based on the answers below to support the question better as the original JSON was badly formatted and how it works with the for loop below.
The full JSON will have 8 sections and each section will contain 15 questions.
The idea is that the JS code will read what section to pull out and then one by one pull out the questions from the list. On first load it will pull out the first question and then when the user clicks on of the buttons either option A or B it will then load in the next question until all questions have been pulled and then do a callback.
When the button in the appended list item is clicked it will then add it to the list below called responses with the answer the user gave as a span tag.
This is what I have so far:
function loadQuestion( $section ) {
$.getJSON('questions.json', function (data) {
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (item === $section) {
$('#questions').append('<li id="' + item.section.questions.question.id + '">' + item.section.questions.question.title + ' <button class="btn" data-response="a">A</button><button class="btn" data-response="b">B</button></li>');
}
}
});
}
function addResponse( $id, $title, $response ) {
$('#responses').append('<li id="'+$id+'">'+$title+' <span>'+$response+'</span></li>');
}
$(document).ready(function() {
// should load the first question from the passed section
loadQuestion( $('.section').data('section') );
// add the response to the list and then load in the next question
$('button.btn').live('click', function() {
$id = $(this).parents('li').attr('id');
$title = $(this).parents('li').html();
$response = $(this).data('response');
addResponse( $id, $title, $response );
loadQuestion ( $('.section').data('section') );
});
});
and the HTML for the page (each page is separate HTML page):
<div class="section" data-section="1">
<ul id="questions"></ul>
<ul id="responses"></ul>
</div>
I've become stuck and confused by how to get only the first question from a section and then load in each question consecutively for that section until all have been called and then do a callback to show the section has been completed.
Thanks
Do not have multiple id's in html called "section."
Do not have multiple keys in your JSON on the same level called "section". Keys in JSON on the same level should be unique just as if you are thinking about a key-value hash system. Then you'll actually be able to find the keys. Duplicate JSON keys on the same level is not valid.
One solution can be section1, section2, etc. instead of just section. Don't rely on data-section attribute in your HTML - it's still not good if you have "section" as the duplicate html id's and as duplicate JSON keys.
If you have only one section id in HTML DOM, then in your JSON you must also have just one thing called "section" e.g.:
var whatever = {
"section" : {
"1": {
"question" : {
"1" : {
"id" : "1a",
"title" : "question1a"
},
"2" : {
"id" : "2a",
"title" : "question2a"
}
}
},
"2": {
"question" : {
"1" : {
"id" : "1a",
"title" : "aquestion1a"
},
"2" : {
"id" : "2a",
"title" : "aquestion2a"
}
}
}
}
}
console.log(whatever.section[1].question[1].title); //"question1a"
To get question, do something like this:
function loadQuestions(mySectionNum) {
$.getJSON('whatever.json', function(data){
var layeriwant = data.section[mySectionNum].question;
$.each(layeriwant, function(question, qMeta) {
var desired = '<div id="question-' +
qMeta.id +
'"' +
'>' +
'</div>';
$("#section").append(desired);
var quest = $("#question-" + qMeta.id);
quest.append('<div class="title">' + qMeta.title + '</div>');
//and so on for question content, answer choices, etc.
});
});
}
then something like this to actually get the questions:
function newQuestion(){
var myHTMLSecNum = $("#section").attr('data-section');
loadQuestions(myHTMLSecNum);
}
newQuestion();
//below is an example, to remove and then append new question:
$('#whatevernextbutton').on('click',function(){
var tmp = parseInt($("#section").attr('data-section'));
tmp++;
$("#section").attr('data-section', tmp);
$("#section").find('*').remove();
newQuestion();
});
Technically your getJSON function always retrieves the same data. Your code never compares the id given to the id you're extracting.
Your getJSON should look something like:
function loadQuestion( $section ) {
for (var i = 0; i < questions.section.length; i++) {
var item = questions.section[i];
if (item.id === $section) {
for (var j = 0; j < item.questions.length; j++) {
$('#questions').append('<li id="' +
item.questions[i].id + '">' +
item.questions[i].title +
' <button class="btn" data-response="a">A</button><button class="btn" data-response="b">B</button></li>'
);
}
}
}
}
Modify your JSON to:
var questions = {
section: [{
id: 1,
questions: [{
id: "1a",
title: "This is question1a"
},{
id: "2a",
title: "This is question2a"
}]},{
id: 2,
questions: [{
id: "1a",
title: "This is question1a"
},{
id: "2a"
title: "This is question2a"
}]
}]
};
Edit: your first parameter of getJSON is the URL of the JSON returning service.
You don't need getJSON at all if your JSON is already defined on the client. I have modified the code above.

Categories