I have been trying to figure out the reason behind what could be wrong exactly.
The basic HTML and CSS have been pre-written, but JavaScript is used to create the core elements (or items) meant to populate the DOM.
In my script, I am trying to fetch product information to populate the browser interface with shopping items.
The error that I am getting in the console is: Cannot set properties of undefined (setting 'innerText')
async function fakeStoreAPI_Products () {
const urlOfData = await fetch('https://fakestoreapi.com/products');
if(urlOfData.status !== 200 || !urlOfData.ok){
throw new Error('There was a problem fetching the data...')
}else if(urlOfData.status === 200 && urlOfData.ok){
const response = await urlOfData.json();
populate(response)
}
}
function populate (completeData) {
const displayAllCardsHere = document.getElementById('cards_inner');
const data = completeData;
for(const datum of data){
// The parent
const div_class_card = document.createElement('div')
div_class_card.setAttribute('class', 'card')
// Child [p_class_title]
const p_class_title = document.createElement('p')
p_class_title.setAttribute('class', 'title')
// Child [img]
const img = document.createElement('div')
img.setAttribute('class', 'img')
// Child [p_class_description]
const p_class_description = document.createElement('p')
p_class_description.setAttribute('class', 'description')
// Child [div_class_cat_price] ~ With children
const div_class_cat_price = document.createElement('div')
div_class_cat_price.setAttribute('class', 'cat_price')
// Children of [div_class_cat_price] ~ descendants of [div_class_card ~ The [grand] parent]
const p_class_category = document.createElement('p')
p_class_category.setAttribute('class', 'category')
const p_class_price = document.createElement('p')
p_class_price.setAttribute('class', 'price')
p_class_title.innerText = datum['title']
img.setAttribute('src', 'datum["image"]')
p_class_description.innerText = datum['description']
p_class_category.innerText = datum['category']
p_class_price.innerText = datum['price']
//div_class_card.insertAdjacentHTML('beforeend', p_class_title)
div_class_card.append(p_class_title)
//div_class_card.insertAdjacentHTML('beforeend', img)
div_class_card.append(img)
//div_class_card.insertAdjacentHTML('beforeend', p_class_description)
div_class_card.append(p_class_description)
//div_class_card.insertAdjacentHTML('beforeend', div_class_cat_price)
div_class_card.append(div_class_cat_price)
// Place children [p_class_category, p_class_price] into parent [div_class_cat_price]
//div_class_cat_price.insertAdjacentHTML('beforeend', p_class_category)
div_class_cat_price.append(p_class_category)
//div_class_cat_price.insertAdjacentHTML('beforeend', p_class_price)
div_class_cat_price.append(p_class_price)
displayAllCardsHere.append(div_class_card)
}
}
fakeStoreAPI_Products().catch((err) => {
console.log(err)
})
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body{
font-family: Nunito Sans, Yu Gothic UI;
}
div#cards_outer{
width: 100%;
padding: 20px;
display: grid;
grid-row-gap: 20px;
}
div#heading{
text-align: center;
}
div#heading span{
font-size: 40px;
font-weight: 600;
/* hori verti blur colour */
text-shadow: 0px 0px 6px #0007;
}
div#cards_inner{
width: 100%;
display: grid;
grid-template: 1fr / repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 20px;
}
div#cards_inner div.card{
/* hori verti blur color */
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);
padding: 10px;
}
div#cards_inner div.card img{
width: 100%;
}
div#cards_inner div.card p.description{
font-size: 14px;
text-align: justify;
word-wrap: break-word;
}
div#cards_inner div.card p.title,
div#cards_inner div.card div.cat_price p.category,
div#cards_inner div.card div.cat_price p.price{
text-align: center;
}
div#cards_inner div.card p.title{
font-size: 18px;
font-weight: 600;
text-transform: capitalize;
}
div#cards_inner div.card div.cat_price p.category,
div#cards_inner div.card div.cat_price p.price{
font-size: 14px;
font-weight: 600;
}
div#cards_inner div.card div.cat_price p.category{
text-transform: capitalize;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito Sans&display=swap" />
<title>Fetch data from API and display data on the browser</title>
</head>
<body>
<div id="cards_outer">
<div id="heading"><span>Fake API Store</span></div>
<div id="cards_inner">
<!-- populate cards here -->
</div>
</div>
</body>
</html>
What exactly did I miss?
CodePudding user response:
The problem is, that you set the variable to document.createElement('div').setAttribute('class', 'title')
, but setAttribute('class', 'title')
doesn't return the created HTML element.
You should do it like this:
// The parent
const div_class_card = document.createElement('div')
div_class_card.setAttribute('class', 'card')
// Child [p_class_title]
const p_class_title = document.createElement('p')
p_class_title.setAttribute('class', 'title')
// Child [img]
const img = document.createElement('img')
img.setAttribute('class', 'img')
// Child [p_class_description]
const p_class_description = document.createElement('p')
p_class_description.setAttribute('class', 'description')
// Child [div_class_cat_price] ~ With children
const div_class_cat_price = document.createElement('div')
div_class_cat_price.setAttribute('class', 'cat_price')
// Children of [div_class_cat_price] ~ decendants of [div_class_card ~ The [grand] parent]
const p_class_category = document.createElement('p')
p_class_category.setAttribute('class', 'category')
const p_class_price = document.createElement('p')
p_class_price.setAttribute('class', 'price')
First create the element and set the variable for it, then use the variable to set attributes.
Edit:
The image is not shown, because you create a div element in your img
variable. And div elements don't handle src attributes. It should work, if you do the following:
// Child [img]
const img = document.createElement('img')
img.setAttribute('class', 'img')
Also, you are using the string 'datum["image"]'
as the src attribute. It should be the following.
img.setAttribute('src', datum["image"])
CodePudding user response:
My preference is use HTML for HTML and then use javascript to manipulate/populate the HTML. From your code, the internal structure of your card is not immediately obvious. Use the <template>
tag to structure your card. Then use javascript to populate a clone of the template, then add that to the DOM.
Not only do I find it easier to work with HTML as HTML, this also results in simpler javascript.
/*Nothing has changed here, move along*/
async function fakeStoreAPI_Products() {
const urlOfData = await fetch('https://fakestoreapi.com/products');
if (urlOfData.status !== 200 || !urlOfData.ok) {
throw new Error('There was a problem fetching the data...')
} else if (urlOfData.status === 200 && urlOfData.ok) {
const response = await urlOfData.json();
populate(response)
}
}
function populate(completeData) {
const displayAllCardsHere = document.getElementById('cards_inner');
//Get the template object
const template = document.getElementById("cardTemplate");
const data = completeData;
for (const datum of data) {
//Clone the template
let clone = template.content.cloneNode(true);
/*Use query selectors to populate the clone*/
clone.querySelector(".title").innerText = datum["title"];
clone.querySelector(".img img").setAttribute("src", datum["image"]);
clone.querySelector(".description").innerText = datum["description"];
clone.querySelector(".cat_price .category").innerText = datum["category"];
clone.querySelector(".cat_price .price").innerText = datum["price"];
//Append clone to the DOM
displayAllCardsHere.appendChild(clone);
}
}
fakeStoreAPI_Products().catch((err) => {
console.log(err)
})
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Nunito Sans, Yu Gothic UI;
}
div#cards_outer {
width: 100%;
padding: 20px;
display: grid;
grid-row-gap: 20px;
}
div#heading {
text-align: center;
}
div#heading span {
font-size: 40px;
font-weight: 600;
/* hori verti blur colour */
text-shadow: 0px 0px 6px #0007;
}
div#cards_inner {
width: 100%;
display: grid;
grid-template: 1fr / repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 20px;
}
div#cards_inner div.card {
/* hori verti blur color */
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);
padding: 10px;
}
div#cards_inner div.card img {
width: 100%;
}
div#cards_inner div.card p.description {
font-size: 14px;
text-align: justify;
word-wrap: break-word;
}
div#cards_inner div.card p.title,
div#cards_inner div.card div.cat_price p.category,
div#cards_inner div.card div.cat_price p.price {
text-align: center;
}
div#cards_inner div.card p.title {
font-size: 18px;
font-weight: 600;
text-transform: capitalize;
}
div#cards_inner div.card div.cat_price p.category,
div#cards_inner div.card div.cat_price p.price {
font-size: 14px;
font-weight: 600;
}
div#cards_inner div.card div.cat_price p.category {
text-transform: capitalize;
}
<div id="cards_outer">
<div id="heading"><span>Fake API Store</span></div>
<div id="cards_inner">
<!-- populate cards here -->
</div>
</div>
<template id="cardTemplate">
<div >
<p ></p>
<div ><img src=""/></div>
<div ></div>
<div >
<p ></p>
<p ></p>
</div>
</div>
</template>