Looping through JSON Data to Generate HTML - javascript

I have JSON data that looks like this:
data = {
"tennis": [{
"Description": "Insert description here.",
"Price": 379.99,
"ProductName": "Babolat Play Pure Drive",
}, {
"Description": "Insert description here.",
"Price": 199.99,
"ProductName": "Yonex AI 98 Tennis Racquet",
}],
"basketball": [{
"Description": "Insert description here.",
"Price": 64.99,
"ProductName": "Wilson NCAA Solution Official Game Basketball",
}, {
"Description": "Insert description here.",
"Price": 59.99,
"ProductName": "Spalding NBA NeverFlat Size 7 Composite Leather Basketball",
}]
}
I am using this data to generate HTML so it looks properly formatted and easily readable for the user. The way I am doing this is by creating a for loop to read through tennis and basketball categories. For example:
for (var i = 0; i < data.tennis.length; i++) {
tennisProducts.push(data.tennis[i]);
var tennisProductsTitle = tennisProducts[i].ProductName;
var tennisProductsDescription = tennisProducts[i].Description;
var tennisProductsPrice = tennisProducts[i].Price;
var badge = document.createElement('div');
badge.className = 'badge';
badge.innerHTML =
'<h1>' + tennisProductsTitle + '</h1>' +
'<h2>' + tennisProductsDescription + '</h1>' +
'<div class="options-only-phone">' +
'<a class="service-provider-call" href="#" target="_blank"> Buy for $' + tennisProductsPrice + '</a>';
document.getElementById('tennis-products-list').appendChild(badge);
}
How can I create one for loop that can read through both (or multiple) categories?
Here is my working example in this JSFiddle: https://jsfiddle.net/dsk1279b/1

Double loop, one to iterate the object properties, the next to iterate the array:
for (var key in data) {
for (var i = 0; i < data[key].length; i++) {
//HTML logic
}
}
Final code:
for (var key in data) {
for (var i = 0; i < data[key].length; i++) {
var title = data[key][i].ProductName;
var desc = data[key][i].Description;
var price = data[key][i].Price;
var badge = document.createElement('div');
badge.className = 'badge';
badge.innerHTML =
'<h1>' + title + '</h1>' +
'<h2>' + desc + '</h1>' +
'<div class="options-only-phone">' +
'<a class="service-provider-call" href="#" target="_blank"> Buy for $' + price + '</a>';
//I gave the div the same ID's as the keys in the object for ease
document.getElementById(key).appendChild(badge);
}
}
data = {
"tennis": [{
"Description": "Insert description here.",
"Price": 379.99,
"ProductName": "Babolat Play Pure Drive",
}, {
"Description": "Insert description here.",
"Price": 199.99,
"ProductName": "Yonex AI 98 Tennis Racquet",
}],
"basketball": [{
"Description": "Insert description here.",
"Price": 64.99,
"ProductName": "Wilson NCAA Solution Official Game Basketball",
}, {
"Description": "Insert description here.",
"Price": 59.99,
"ProductName": "Spalding NBA NeverFlat Size 7 Composite Leather Basketball",
}]
}
for (var key in data) {
for (var i = 0; i < data[key].length; i++) {
var title = data[key][i].ProductName;
var desc = data[key][i].Description;
var price = data[key][i].Price;
var badge = document.createElement('div');
badge.className = 'badge';
badge.innerHTML =
'<h1>' + title + '</h1>' +
'<h2>' + desc + '</h1>' +
'<div class="options-only-phone">' +
'<a class="service-provider-call" href="#" target="_blank"> Buy for $' + price + '</a>';
document.getElementById(key).appendChild(badge);
}
}
body {
font-family: Arial, sans-serif;
line-height: 125%;
}
h1 {
font-size: 0.875em;
padding: 0;
margin: 0;
}
h2,
a {
font-size: 0.750em;
padding: 0;
margin: 0;
font-weight: normal;
}
a:hover {
text-decoration: none;
}
.badge {
border-radius: 2px;
border: 1px solid rgba(0, 0, 0, 0.15);
padding: 12px;
margin: 12px 0;
}
.badge:hover {
border: 1px solid rgba(0, 0, 0, 0.3);
}
<div id="tennis">
</div>
<hr>
<div id="basketball">
</div>

tymeJV has a good approach, but this can be made even easier.
for(var product in data) {
// logic
}
If you look at your data, you have an object that we're already iterating over in key/value form.
Since you have arrays of items per key, you can use the Array.forEach() function.
for(var product in data) {
// current is the current object in the array
data[product].forEach(function(current){
//HTML logic
})
}
You change the place where you're appending the html template, so I would recommend updating your data object to be something like this:
data = {
"tennis": {
"products: [
{
"Description": "Insert description here.",
"Price": 379.99,
"ProductName": "Babolat Play Pure Drive"
},
{
"Description": "Insert description here.",
"Price": 199.99,
"ProductName": "Yonex AI 98 Tennis Racquet"
}
],
"templateTarget": '#tennis-products-list'
}
"basketball":
"products": [
{
"Description": "Insert description here.",
"Price": 64.99,
"ProductName": "Wilson NCAA Solution Official Game Basketball"
},
{
"Description": "Insert description here.",
"Price": 59.99,
"ProductName": "Spalding NBA NeverFlat Size 7 Composite Leather Basketball"
}
],
"templateTarget": '#basketball-products-list'
}
Something like that is going to allow you to do this:
for(var product in data) {
// current is the current object in the array
product.forEach(function(current){
var badge = document.createElement('div');
badge.className = 'badge';
badge.innerHTML =
'<h1>' + current.productName + '</h1>' +
'<h2>' + current.description + '</h1>' +
'<div class="options-only-phone">' +
'<a class="service-provider-call" href="#" target="_blank"> Buy for $' + current.price + '</a>';
document.getElementById(current.templateTarget).appendChild(badge);
})
}
This can be further optimized by having that giant html string hidden in a script tag with type="text/x-template" (since the browser ignores script types it doesn't understand) and grabbing it with the innerHTML function by referencing the id property on the script tag.
Hope that helps!

Flatten the data to a single array of values with category as a property:
var _data = Object.keys(data).reduce(
(m,c) => m.concat(data[c].map(
(i) => (i.category = c) && i))
, []);
console.log(_data);
Use flattened array for UI:
_data.forEach((d) => {
var badge = document.createElement('div');
badge.className = 'badge';
badge.innerHTML = [
'<h1>',
d.ProductName,
'</h1><h2>',
d.Description,
'</h1><div class="options-only-phone">',
'<a class="service-provider-call" href="#" target="_blank"> Buy for $',
d.Price,
'</a>'].join('');
document.getElementById(d.category + '-products-list').appendChild(badge);
})
'use strict';
var data = {
"tennis": [{
"Description": "Insert description here.",
"Price": 379.99,
"ProductName": "Babolat Play Pure Drive",
}, {
"Description": "Insert description here.",
"Price": 199.99,
"ProductName": "Yonex AI 98 Tennis Racquet",
}],
"basketball": [{
"Description": "Insert description here.",
"Price": 64.99,
"ProductName": "Wilson NCAA Solution Official Game Basketball",
}, {
"Description": "Insert description here.",
"Price": 59.99,
"ProductName": "Spalding NBA NeverFlat Size 7 Composite Leather Basketball",
}]
}
var _data = Object.keys(data).reduce((m,c) => m.concat(data[c].map((i) => (i.category = c) && i) ), []);
console.log(_data);
_data.forEach((d) => {
var badge = document.createElement('div');
badge.className = 'badge';
badge.innerHTML = [
'<h1>',
d.ProductName,
'</h1><h2>',
d.Description,
'</h1><div class="options-only-phone">',
'<a class="service-provider-call" href="#" target="_blank"> Buy for $',
d.Price,
'</a>'].join('');
document.getElementById(d.category + '-products-list').appendChild(badge);
})
body {
font-family: Arial, sans-serif;
line-height: 125%;
}
h1 {
font-size: 0.875em;
padding: 0; margin: 0;
}
h2, a {
font-size: 0.750em;
padding: 0; margin: 0;
font-weight: normal;
}
a:hover {
text-decoration: none;
}
.badge {
border-radius: 2px;
border: 1px solid rgba(0, 0, 0, 0.15);
padding: 12px;
margin: 12px 0;
}
.badge:hover {
border: 1px solid rgba(0, 0, 0, 0.3);
}
<div id="tennis-products-list">
</div>
<hr>
<div id="basketball-products-list">
</div>

Related

Select filter hides all the items in the list

I am trying to create a select dropdown filter which dynamically gets its values and data from rest api.
Here is my code
HTML
<div class="container">
<select data-filter="season" class="filter_category_name filter form-control"></select>
<div class="listContent" id="listitem"></div>
</div>
JS
var data = [{
"category_name": "1",
"title": "One One",
"more": [{
"details": "some details"
}]
},
{
"category_name": "1",
"title": "One Two",
"more": [{
"details": "some details"
}]
},
{
"category_name": "2",
"title": "Two One",
"more": [{
"details": "some details"
}]
},
{
"category_name": "3",
"title": "Three One",
"more": [{
"details": "some details"
}]
}];
let listItems = "",
categoryAll = "";
for(var i = 0;i < data.length; i++) {
var title = data[i].title,
category_name = data[i].category_name;
listItems += "<a onclick='createAVideoContainer(" + i + ")'><div class='listItem'><p>" + title + "</p></div></div></a>";
if (categoryAll.indexOf("<option value='" + category_name + "'>" + category_name + "</option>") == -1) {
categoryAll += "<option value='" + category_name + "'>" + category_name + "</option>";
}
}
$("#listitem").html(listItems);
$(".filter_category_name").append(categoryAll);
var filtersObject = {};
$(".filter").on("change",function() {
var filterName = $(this).data("filter"),
filterVal = $(this).val();
if (filterVal == "") {
delete filtersObject[filterName];
} else {
filtersObject[filterName] = filterVal;
}
var filters = "";
for (var key in filtersObject) {
if (filtersObject.hasOwnProperty(key)) {
filters += "[data-"+key+"='"+filtersObject[key]+"']";
}
}
if (filters == "") {
$(".listItem").show();
} else {
$(".listItem").hide();
$(".listItem").hide().filter(filters).show();
}
});
But for some reason it hides all the items and nothing change after changing the options
I want to initially load all the items and then filter based on the option
I want the select element to dynamically create the options with the values of the category names like the first option should have the value of cars1 and display all the other objects with the same category_name.
Note: The entire code is wrapped inside an async function which renders on page load.
Here is a proper working js fiddle with the above problem
Based on your fiddle code, it would be better to filter the <a> items within the listItem area based on season, as that is the filter that is built, instead of trying to show/hide the entire listItem area.
The two minor code changes to achieve the desired result, is at these two code points:
Add a data-season reference here, to the <a> tags, with the category_name value:
listItems += '<a data-season="' + category_name + '" '
+ "onclick='createAVideoContainer(" + i + ")'><div class='listItem'><p>"
+ title + "</p></div></a>";
Change the filtering to filter on the <a> tags for the data-season that you build:
if (filters == "") {
$("a").show();
} else {
$("a").hide();
$("a").filter(filters).show();
}
Integrating those two changes into your fiddle example code, try the runnable example below:
var data = [
{
"category_name": "1",
"title": "One One",
"more": [
{
"details": "some details"
}
]
},
{
"category_name": "1",
"title": "One Two",
"more": [
{
"details": "some details"
}
]
},
{
"category_name": "2",
"title": "Two One",
"more": [
{
"details": "some details"
}
]
},
{
"category_name": "3",
"title": "Three One",
"more": [
{
"details": "some details"
}
]
},
];
let listItems = "",
categoryAll = "";
for(var i = 0; i < data.length; i++) {
var title = data[i].title,
category_name = data[i].category_name;
listItems += '<a data-season="' + category_name + '"'
+ "onclick='createAVideoContainer(" + i + ")'><div class='listItem'><p>"
+ title + "</p></div></a>";
if (categoryAll.indexOf("<option value='" + category_name + "'>" + category_name + "</option>") == -1) {
categoryAll += "<option value='" + category_name + "'>" + category_name + "</option>";
}
}
$("#listitem").html(listItems);
$(".filter_category_name").append(categoryAll);
var filtersObject = {};
//on filter change
$(".filter").on("change",function() {
var filterName = $(this).data("filter"),
filterVal = $(this).val();
if (filterVal == "") {
delete filtersObject[filterName];
} else {
filtersObject[filterName] = filterVal;
}
var filters = "";
for (var key in filtersObject) {
if (filtersObject.hasOwnProperty(key)) {
filters += "[data-"+key+"='"+filtersObject[key]+"']";
}
}
if (filters == "") {
$("a").show();
} else {
$("a").hide();
$("a").filter(filters).show();
}
});
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="container">
<select data-filter="season" class="filter_category_name filter form-control"></select>
<div class="listContent" id="listitem">
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/#popperjs/core#2.9.3/dist/umd/popper.min.js" integrity="sha384-eMNCOe7tC1doHpGoWe/6oMVemdAVTMs2xqW4mwXrXsW0L84Iytr2wi5v2QjrP/xp" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.0/dist/js/bootstrap.min.js" integrity="sha384-cn7l7gDp0eyniUwwAZgrzD06kc/tftFf19TOAs2zVinnD/C7E91j9yyk5//jjpt/" crossorigin="anonymous"></script>
Here's the updated fiddle with the same change applied.

Display nested array data in a click event

I have this data and I'm displaying the name. But when I click on, in this case Sweden, I want to display additional info. In the code below I just want to display the country code, but this creates four additional <p> tags because of .forEach loop. That's not really what I want.
How would I go about if I just want to display the country code, and what if I wanted to display all the additional info? I'm kinda stuck as of now.
let data = [
{"name": "Swaziland", "code": "SZ"},
{"name": "Sweden",
"info": [
{"code": "SE"},
{"population": "10.2 million"},
{"area": "447 435km"},
{"capital": "Stockholm"},
{"Language": "Swedish"}]
},
{"name": "Switzerland", "code": "CH"},
{"name": "Syrian Arab Republic", "code": "SY"}
]
let output = '<ul class="searchresultCountries">';
let countries = data;
countries.forEach((value) => {
output += '<li>' + value.name + '</li>';
});
output += '</ul>';
document.querySelector('#countries').innerHTML = output;
document.addEventListener('click', (e) => {
data.forEach((item) => {
if(item.name === e.target.textContent) {
if(item.info) {
item.info.forEach((items) => {
let extraInfo = document.createElement("p");
extraInfo.textContent = items.code;
e.target.appendChild(extraInfo);
});
}
}
});
});
ul {
padding: 0;
}
.searchresultCountries li {
list-style-type: none;
border: 1px solid grey;
margin-bottom: 10px;
padding: 5px;
}
<div id="countries"></div>
You should have some kind of button or link upon which clicking you can show/hide the extra information.
You can try the following way:
let data = [
{"name": "Swaziland", "code": "SZ"},
{"name": "Sweden",
"info": [
{"code": "SE"},
{"population": "10.2 million"},
{"area": "447 435km"},
{"capital": "Stockholm"},
{"Language": "Swedish"}]
},
{"name": "Switzerland", "code": "CH"},
{"name": "Syrian Arab Republic", "code": "SY"}
]
let output = '<ul class="searchresultCountries">';
let countries = data;
countries.forEach((value) => {
output += '<li>' + value.name + '</li>';
});
output += '</ul>';
document.querySelector('#countries').innerHTML = output;
document.addEventListener('click', (e) => {
data.forEach((item) => {
if(item.name === e.target.textContent) {
if(item.info) {
let extraInfo = document.createElement("p");
extraInfo.textContent = item.info[0].code;
e.target.appendChild(extraInfo);
if(item.info.length > 1){
let btnExtra = document.createElement("button");
btnExtra.textContent = "Show More";
e.target.appendChild(btnExtra);
btnExtra.addEventListener('click', function(){
let container = document.createElement("div");
if(!document.querySelector('.extra')){
container.classList.add('extra');
item.info.forEach(function(el, i){
if(i > 0){ // skip the first
let extra = document.createElement("p");
extra.textContent = Object.values(el)[0];
container.appendChild(extra);
}
});
e.target.appendChild(container);
btnExtra.textContent = "Show Less";
}
else{
document.querySelector('.extra').remove();
btnExtra.textContent = "Show More";
}
});
}
}
else{
let extraInfo = document.createElement("p");
extraInfo.textContent = item.code;
e.target.appendChild(extraInfo);
}
}
});
});
ul {
padding: 0;
}
.searchresultCountries li {
list-style-type: none;
border: 1px solid grey;
margin-bottom: 10px;
padding: 5px;
}
<div id="countries"></div>

Trying to access json content and display in a grid fashion

I have a JSON file having the following content.
{
"rooms":[
{
"id": "1",
"name": "living",
"Description": "The living room",
"backgroundpath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSrsU8tuZWySrSuRYdz72WWFiYaW5PCMIwPAPr_xAIqL-FgmQ4qRw",
"Settings": {
"Sources": [
{
"srcname":["DirectTV","AppleTV","Sonos"],
"iconpath":["src/assets/images/ThemeEditorImgLib_iTVLight5d3a46c1ad5d7796.png","path2"]
}
],
"hex": "#000"
}
},
{
"id": "2",
"name": "dining",
"Description": "The Dining room",
"backgroundpath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTjjspDkQEzbJJ1sSLS2ReWEx5P2ODNQmOtT572OIF9k3HbTGxV",
"Settings": {
"Sources": [
{
"srcname":["Climate","Shades"],
"iconpath":["path1","path2"]
}
],
"hex": "#000"
}
}
]
}
My HTML file Has:
<div class="roww">
<div class="col-md-4 col-sm-6 col-12">
</div>
</div>
I want to display my JSON content in the HTML.
So far I have tried the following.
private initTileGrid():void
{
var col = document.getElementsByClassName("roww")[0];
var outeritems=this.room_list.rooms;
// console.log(outeritems);
for(var i=0;i<outeritems.length;i++){
var tile = document.getElementsByClassName("col-md-4 col-sm-6 col-12 tile no-padding")[0];
var outerdiv = document.createElement("div");
var h5 = document.createElement("h5");
h5.innerHTML = outeritems[i].Description;
outerdiv.appendChild(h5);
var p = document.createElement("p");
p.innerHTML = outeritems[i].name;
outerdiv.appendChild(p);
// col.appendChild(outerdiv);
tile.appendChild(outerdiv);
var inneritem=outeritems[i].Settings.Sources.srcname;
console.log(inneritem);
var innerdiv = document.createElement("div");
for(var j=0;j<inneritem.length;j++)
{
console.log("hi")
var h5inner = document.createElement("h5");
h5inner.innerHTML = inneritem.srcname;
console.log(h5inner);
innerdiv.appendChild(h5inner);
}
tile.appendChild(innerdiv);
}
I am able to display the description and name from the json but not able to fetch the list of sources.
My final solution should be a grid with number of tiles equal to the number of objects in the JSON, with each tile having a background image from the json and its content.
Can anybody tell me where I am going wrong?
Any help appreciated!
I would suggest and recommend generating your dynamic HTML the following way with Template Literals.
Using Array#map, Array#join and Destructuring assignment
const data = {"rooms":[{"id":"1","name":"living","Description":"The living room","backgroundpath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSrsU8tuZWySrSuRYdz72WWFiYaW5PCMIwPAPr_xAIqL-FgmQ4qRw","Settings":{"Sources":[{"srcname":["DirectTV","AppleTV","Sonos"],"iconpath":["src/assets/images/ThemeEditorImgLib_iTVLight5d3a46c1ad5d7796.png","path2"]}],"hex":"#000"}},{"id":"2","name":"dining","Description":"The Dining room","backgroundpath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTjjspDkQEzbJJ1sSLS2ReWEx5P2ODNQmOtT572OIF9k3HbTGxV","Settings":{"Sources":[{"srcname":["Climate","Shades"],"iconpath":["path1","path2"]}],"hex":"#000"}}]}
const res = data.rooms.map(({name, Description, Settings:{Sources}})=>{
const inner = Sources.map(({srcname})=>{
return srcname.map(src=>`<h5>${src}</h5>`).join("");
}).join("");
return `
<div>
<h5>${Description}</h5>
<p>${name}</p>
<div class="inner">
${inner}
</div>
</div>
`
}).join("");
document.body.innerHTML = res;
body>div{background-color:lightgrey;padding:5px;margin-top:5px}body>div::before{content:"row"}body>div>*{margin-left:10px}h5{background-color:blue;color:white;padding:5px}h5::before{content:"outer h5:: "}p{background-color:purple;color:white;padding:5px}p::before{content:"p:: "}div.inner{padding:5px;background-color:grey}div.inner::before{content:"inner div"}div.inner>h5{background-color:green;color:white}div.inner>h5::before{content:"inner h5:: "}
Unminified CSS:
body > div {
background-color: lightgrey;
padding: 5px;
margin-top: 5px;
}
body > div::before {
content: "row";
}
body > div > * {
margin-left: 10px;
}
h5 {
background-color: blue;
color: white;
padding: 5px;
}
h5::before {
content: "outer h5:: "
}
p {
background-color: purple;
color: white;
padding: 5px;
}
p::before {
content: "p:: ";
}
div.inner {
padding: 5px;
background-color: grey;
}
div.inner::before {
content: "inner div";
}
div.inner > h5 {
background-color: green;
color: white;
}
div.inner > h5::before {
content: "inner h5:: "
}
V_Stack as discussed, this is just the tweak I would do to the sources section of the JSON, moving forward it will make working with a source easier, especially if you add additional properties. Note it is an array of self contained objects now.
{
"rooms": [{
"id": "1",
"name": "living",
"Description": "The living room",
"backgroundpath": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSrsU8tuZWySrSuRYdz72WWFiYaW5PCMIwPAPr_xAIqL-FgmQ4qRw",
"Settings": {
"Sources": [{
"srcname": "DirectTV",
"iconpath": "src/assets/images/ThemeEditorImgLib_iTVLight5d3a46c1ad5d7796.png"
},
{
"srcname": "AppleTV",
"iconpath": "path2"
},
{
"srcname": "Sonos",
"iconpath": ""
}
],
"hex": "#000"
}
},
{
"id": "2",
"name": "dining",
"Description": "The Dining room",
"backgroundpath": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTjjspDkQEzbJJ1sSLS2ReWEx5P2ODNQmOtT572OIF9k3HbTGxV",
"Settings": {
"Sources": [{
"srcname": "Climate",
"iconpath": "path1"
},
{
"srcname": "Shades",
"iconpath": "path2"
}
],
"hex": "#000"
}
}
]
}
Try this:
var col = document.getElementsByClassName("roww")[0];
var outeritems = this.room_list.rooms;
// console.log(outeritems);
for (var i = 0; i < outeritems.length; i++) {
var tile = document.getElementsByClassName("col-md-4")[0];
var outerdiv = document.createElement("div");
var h5 = document.createElement("h5");
h5.innerHTML = outeritems[i].Description;
outerdiv.appendChild(h5);
var p = document.createElement("p");
p.innerHTML = outeritems[i].name;
outerdiv.appendChild(p);
// col.appendChild(outerdiv);
tile.appendChild(outerdiv);
outeritems[i].Settings.Sources.forEach(source => {
var inneritem = source.srcname;
console.log(inneritem);
var innerdiv = document.createElement("div");
for (var j = 0; j < inneritem.length; j++) {
console.log("hi")
var h5inner = document.createElement("h5");
h5inner.innerHTML = inneritem[j];
console.log(h5inner);
innerdiv.appendChild(h5inner);
}
tile.appendChild(innerdiv);
});
}
}
You need to loop through the Settings.Sources and srcname because all of them are arrays
Your main problem starts at this line var inneritem=outeritems[i].Settings.Sources.srcname; Sources is an array so you need to access it like Sources[j] where j is a number within the arrays length.
The inner loop is fine, you just need to loop through Sources properly
I also had to add classes to the tiles class to match your getElementsByClassName query
const room_list = {
"rooms": [{
"id": "1",
"name": "living",
"Description": "The living room",
"backgroundpath": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSrsU8tuZWySrSuRYdz72WWFiYaW5PCMIwPAPr_xAIqL-FgmQ4qRw",
"Settings": {
"Sources": [{
"srcname": ["DirectTV", "AppleTV", "Sonos"],
"iconpath": ["src/assets/images/ThemeEditorImgLib_iTVLight5d3a46c1ad5d7796.png", "path2"]
}],
"hex": "#000"
}
},
{
"id": "2",
"name": "dining",
"Description": "The Dining room",
"backgroundpath": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTjjspDkQEzbJJ1sSLS2ReWEx5P2ODNQmOtT572OIF9k3HbTGxV",
"Settings": {
"Sources": [{
"srcname": ["Climate", "Shades"],
"iconpath": ["path1", "path2"]
}],
"hex": "#000"
}
}
]
}
var col = document.getElementsByClassName("roww")[0];
var outeritems = room_list.rooms;
// console.log(outeritems);
for (var i = 0; i < outeritems.length; i++) {
var tile = document.getElementsByClassName("col-md-4 col-sm-6 col-12 tile no-padding")[0];
var outerdiv = document.createElement("div");
var h5 = document.createElement("h5");
h5.innerHTML = outeritems[i].Description;
outerdiv.appendChild(h5);
var p = document.createElement("p");
p.innerHTML = outeritems[i].name;
outerdiv.appendChild(p);
// col.appendChild(outerdiv);
tile.appendChild(outerdiv);
var inneritems = outeritems[i].Settings.Sources
var innerdiv = document.createElement("div");
for (var j = 0; j < inneritems.length; j++) {
var inneritem = inneritems[j];
var h5inner = document.createElement("h5");
h5inner.innerHTML = inneritem.srcname;
innerdiv.appendChild(h5inner);
}
tile.appendChild(innerdiv);
}
<div class="roww">
<div class="col-md-4 col-sm-6 col-12 tile no-padding">
</div>
</div>

Accept accent marks & ñ on javascript

I'm making a bot that answers questions given some key words. It's a very easy code developed by Deni Spasovski. However, my website is in Spanish, and I need to be able to use accent marks & ñ as part of the key words. For example:
{ "keys": ["cómo", "estás"], "value": 0 }
However, despite my efforts, I can't seem to fix the code. I don't know that much about coding. Maybe it's a very easy thing I have to implement to one of the scripts but I really do not know what it is.
I would be more than grateful if you could help me.
Thank you very, very much in advance!!
This is the HTML:
<!DOCTYPE html>
<html>
<head>
<!--Read the source Luke!-->
<title>Answer bot</title>
<style type="text/css">
body, html
{
height: 100%;
}
.wrap
{
height: 80%;
overflow: auto;
max-height: 80%;
display: block;
}
.content
{
height: 100%;
display: table;
vertical-align: bottom;
}
.subcontent
{
display: table-cell;
vertical-align: bottom;
}
.answerbot-input
{
color: #1AA1E1;
}
.answerbot-ai
{
color: #CE5043;
}
</style>
</head>
<body>
<div style="width: 800px; height: 100%; margin: 0 auto;">
<div id="wrap" class="wrap">
<div class="content">
<div class="subcontent" id='subcontent'>
<p class='answerbot-ai'>
Don't be afraid, talk to me.
</p>
</div>
</div>
</div>
<div>
<input type="text" name="inputtext" style="width: 100%;" onkeyup="keypressInput(this, event);"><br />
</div>
</div>
<script type="text/javascript" src="scripts.js"></script>
<script type="text/javascript" src="data.js"></script>
<script type="text/javascript">
var _answerBot = new answerBot();
function keypressInput(sender, event) {
if (event.which == 13) {
document.getElementById('subcontent').innerHTML += _answerBot.processInput(sender.value);
sender.value = '';
correctScroll("wrap");
}
}
function correctScroll(elementId) {
var objDiv = document.getElementById(elementId);
objDiv.scrollTop = objDiv.scrollHeight;
}
</script>
</body>
</html>
This is the data.js:
if (answerBot) {
answerBot.prototype.specialContext = {
"wrongInput": ["I don't understand you.", "Could you rephrase the question?"],
"emptyInput": ["Please say something", "Speak louder", "Well i can't read minds."],
"rephrase": ["Can you tell me if your question was about one of the following things:"]
};
answerBot.prototype.keywords = [
{ "keys": ["hi"], "value": 0 },
{ "keys": ["hello"], "value": 0 },
{ "keys": ["life", "universe", "everything"], "value": 1 },
{ "keys": ["software", "development"], "value": 2 },
{ "keys": ["software", "engineering"], "value": 2 },
{ "keys": ["who", "made", "you"], "value": 3 },
{ "keys": ["who", "wrote", "you"], "value": 3 },
{ "keys": ["who", "coded", "you"], "value": 3 },
{ "keys": ["is", "this", "real", "life"], "value": 4 },
{ "keys": ["who", "is", "deni"], "value": 5 },
{ "keys": ["tell", "me", "about", "deni"], "value": 5 },
{ "keys": ["tell", "me", "about", "author"], "value": 5 },
{ "keys": ["show", "me", "author"], "value": 5 },
{ "keys": ["can", "see", "source"], "value": 6 },
{ "keys": ["can", "see", "sourcecode"], "value": 6 },
{ "keys": ["show", "me", "code"], "value": 6 },
{ "keys": ["how", "are", "you"], "value": 7 },
{ "keys": ["who", "is", "this"], "value": 8 }];
answerBot.prototype.answers = [
{
"description": "Hi!",
"values": ["Hello there!", "Hi how can I help you today", "Hi! What brings you here?"]
},
{
"description": "What is the answer to life the universe and everything?",
"values": ["42"]
},
{
"description": "What is software development?",
"values": ["Programming! Do you speak it?"]
},
{
"description": "Who created me?",
"values": ["I was created by another <a href='http://about.me/deni' target='_blank'>bot</a>.", "The question is who sent you here?"]
},
{
"description": "Is this real life?",
"values": ["No this is the internetz!", "Find out <a href='http://www.youtube.com/watch?v=txqiwrbYGrs' target='_blank'>yourself</a>!"]
},
{
"description": "Who is Deni?",
"values": ["This is his <a href='https://plus.google.com/+DeniSpasovski/' target='_blank'>G+ profile</a>.", "This is his <a href='www.linkedin.com/in/denispasovski' target='_blank'>Linkedin profile</a>."]
},
{
"description": "Where is your source?",
"values": ["Here is the <a href='https://github.com/denimf/Answer-bot' target='_blank'>source</a>."]
},
{
"description": "How are you?",
"values": ["I'm good how are you?"]
},
{
"description": "Who is this?",
"values": ["StackOverflow Exception occurred", "The question is who are you?"]
}
];
}
And this is the scripts.js:
var answerBot = function () {
var _this = this;
_this.processInput = function (text) {
updateUrl(text);
var _result = "<p class='answerbot-input'>" + text + "</p>";
text = text.replace(new RegExp("[^a-zA-Z ]", "g"), " ");
text = text.replace(new RegExp("[ ]{2,}", "g"), " ");
var _words = text.toLowerCase().split(" ");
var _answers = [];
var _title = "";
if (_words.length === 0 || _words.toString() === '') { //if the input is empty
_answers = _this.specialContext.emptyInput;
_title = _this.specialContext.emptyInput;
} else {
var _possibleAnswers = findMatches(_words);
if (_possibleAnswers.length === 0) { //if no answer found
_answers = _this.specialContext.wrongInput;
_title = _this.specialContext.wrongInput;
}
if (_possibleAnswers.length == 1) { //context recognized
_answers = _this.answers[_possibleAnswers[0]].values;
_title = _this.answers[_possibleAnswers[0]].description;
}
if (_possibleAnswers.length > 1) {
_result += formatText(_this.specialContext.rephrase, _this.specialContext.rephrase);
for (var i = 0; i < _possibleAnswers.length; i++) {
_result += formatText(_this.answers[_possibleAnswers[i]].description, _this.answers[_possibleAnswers[i]].description);
}
}
}
if (_answers.length > 0) {
var _rand = Math.floor((Math.random() - 0.001) * _answers.length);
_result += formatText(_answers[_rand], _title);
}
return _result;
};
function formatText(text, title) {
return "<p class=\'answerbot-ai\' title=\'" + title + "\'>" + text + "</p>";
}
function findMatches(words) {
var foundKeywords = [];
var _possibleAnswers = [];
for (var i = 0; i < _this.keywords.length; i++) {
foundKeywords[i] = 0;
for (var j = 0; j < words.length; j++) {
if (_this.keywords[i].keys.indexOf(words[j]) >= 0) {
foundKeywords[i]++;
if (foundKeywords[i] == _this.keywords[i].keys.length) {
return [_this.keywords[i].value];
}
}
}
if (foundKeywords[i] * 2 > _this.keywords[i].keys.length) {
_possibleAnswers.push(_this.keywords[i].value);
}
}
return _possibleAnswers.filter(function (elem, pos) {
return _possibleAnswers.indexOf(elem) == pos;
});
}
function updateUrl(text){
history.pushState(null, null, "#question=" + encodeURIComponent(text));
if(typeof ga === "function")//google analytics
ga('send', 'event', 'question', text);
}
};
Three possible issues:
1) This line:
text = text.replace(new RegExp("[^a-zA-Z ]", "g"), " ");
will replace anything that isn't in the "normal" alphabet with a space. You might choose to replace it at some point to include characters with diacritics, but to get it working you can simply comment it out.
2) Make sure your files (in particular, data.js) are saved with UTF-8 encoding.
3) Ensure you have an appropriate value in the answers array and then use its index in keywords.value. e.g.
{
"description": "cómo estás?",
"values": ["muy bien"]
}

Represent data from json file in rating stars without jquery!! pure javaScript

I am trying to load the data from my JSON file using javaScript and i need to represent the hotel2show.rating in form of stars, just represent them dependig on the value from 'hotels.json'
Here is my JavaScript
function getHotels(i){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function()
{
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
hotel=JSON.parse(xhr.responseText);
var hotel2show = hotel.hotels[i];
document.getElementById("img-container").innerHTML =
"<img src='"+hotel2show.imgUrl+"'>"+
"<p id='name'><strong>"+ hotel2show.name +"</strong></p>" +"<br/>" + "<p id='rating'><strong>"+ hotel2show.rating +"</strong></p>" +"<br/>" + "<br/>" +"<p id='price'><strong>"+ '£' +hotel2show.price +
"</strong></p>" + "<p id='text'><strong>"+ 'Total hotel stay' +"</strong></p>";
} else {
alert("Ha existido un error con el servidor");
}
}
};
xhr.open("GET",'hotels.json', true);
xhr.send();
and here is my html
<div class="container">
<div id="lista">
<ul>
<button onclick="getHotels(0)">Hotel Sunny Palms</button>
<button onclick="getHotels(1)">Hotel Snowy Mountains</button>
<button onclick="getHotels(2)">Hotel Windy Sails</button>
<button onclick="getHotels(3)">Hotel Middle Of Nowhere</button>
</ul>
</div>
<div class="banner-section" id="img-container">
</div>
and my hotels.json
"hotels": [
{
"name": "Hotel Sunny Palms",
"imgUrl": "imgs/sunny.jpg",
"rating": 5,
"price": 108.00
},
{
"name": "Hotel Snowy Mountains",
"imgUrl": "imgs/snowy.jpg",
"rating": 4,
"price": 120.00
},
{
"name": "Hotel Windy Sails",
"imgUrl": "imgs/windy.jpg",
"rating": 3,
"price": 110.00
},
{
"name": "Hotel Middle of Nowhere",
"imgUrl": "imgs/nowhere.jpg",
"rating": 4,
"price": 199.00
}
]
any help is appreciated
For example..if you have UTF-8 charset then this should perhaps be fine. The key is createElement function by which you can construct your DOM as you wish.
var hotels = [{
"name": "Hotel Sunny Palms",
"imgUrl": "imgs/sunny.jpg",
"rating": 5,
"price": 108.00
}, {
"name": "Hotel Snowy Mountains",
"imgUrl": "imgs/snowy.jpg",
"rating": 4,
"price": 120.00
}, {
"name": "Hotel Windy Sails",
"imgUrl": "imgs/windy.jpg",
"rating": 3,
"price": 110.00
}, {
"name": "Hotel Middle of Nowhere",
"imgUrl": "imgs/nowhere.jpg",
"rating": 4,
"price": 199.00
}];
buildRating(hotels);
function buildRating(data) {
data.forEach(function(v) {
createRatingElement(v.rating);
});
}
function createRatingElement(numberOfStars) {
var wrapper = document.createElement('div');
for (var i = 1; i <= 5; i++) {
var span = document.createElement('span')
span.innerHTML = (i <= numberOfStars ? '★' : '☆');
span.className = (i <= numberOfStars ? 'high' : '');
wrapper.appendChild(span);
}
document.getElementById('img-container').appendChild(wrapper);
}
span {
display: inline-block;
position: relative;
width: 1.2em;
height: 1.2em;
color: black;
}
.high {
color: rgb(217, 211, 0);
}
<div class="banner-section" id="img-container">
</div>
Also, jsfiddle: https://jsfiddle.net/md4708oq/
I assume you know how to parse out your ratings, right? If you are just displaying single star values (whole numbers), then you can just write out a class onto a span element where you would style it with CSS to change what the background image would be.
So, you could make it show 1-5 stars with 4 different images.
It's a solution; not the cleanest or most scalable, but it works for this situation.
So first off this is a nightmare.
Let's clean it up a bit?
var appendString = [];
appendString[0] = "<img src='"+hotel2show.imgUrl+"'>";
appendString[1] = "<p id='name'><strong>"+ hotel2show.name +"</strong></p><br/>";
switch(hotel2show.rating):
case(1):
appendString[2] = "<p id='rating' class='rating-1'><strong>";
break;
case(2):
appendStirng[2] = "<p id='rating' class='rating-2><strong>";
break;
//etc
appendString[3] = hotel2show.rating +"</strong></p>";
appendString[4] = "<br/><br/>";
appendString[5] = "<p id='price'><strong>'£'" + hotel2show.price + "</strong></p>";
appendString[6] = "<p id='text'><strong>"+ 'Total hotel stay' +"</strong></p>";
document.getElementById("img-container").innerHTML = appendString.join(' ');
Note: the switch statement syntax may be incorrect.
You will have to use AJAX calls to retrieve JSON data and then use pure javascript to parse the JSON data and use it to display your html accordingly. Let me know if this is what you are looking for i will help you out with the same.

Categories