问题
I'm self-teaching myself vanilla JS and hope to get really good with it.
I've been racking my brain over this for several days. I'm trying to use template literals to display the properties of objects that are in an array.
Those objects are portfolio projects. In each object, I have a property that is an array of icon images. I'm able to get everything rendered on the page except for all of the icon images.
I can only get 1 icon image to display in my div with the class="skills-used".
Can someone help me figure out how to map through all of the images in that array of icons?
Alternatively, is there a better (or a recommended) way to do this instead?
const projects = [
{
image: "http://via.placeholder.com/360x180",
title: "Project 1",
skillIconImages: ["http://via.placeholder.com/50x50", "http://via.placeholder.com/55x50", "http://via.placeholder.com/50x55", "http://via.placeholder.com/60x50"],
description: "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
},
{
image: "http://via.placeholder.com/360x180",
title: "Project 2",
skillIconImages: ["http://via.placeholder.com/50x50", "http://via.placeholder.com/55x50", "http://via.placeholder.com/50x55", "http://via.placeholder.com/60x50"],
description: "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
},
{
image: "http://via.placeholder.com/360x180",
title: "Project 3",
skillIconImages: ["http://via.placeholder.com/50x50", "http://via.placeholder.com/55x50", "http://via.placeholder.com/50x55", "http://via.placeholder.com/60x50"],
description: "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
}
];
Here is how I'm trying to render it to the page:
let portfolioItemsDiv = document.getElementById('portfolio-items');
projects.forEach((project) => {
portfolioItemsDiv.innerHTML += `
<div class="portfolio-item">
<img src="${project.image}" alt="${project.title}" />
<h3 class="project-title">${project.title}</h3>
<div class="skills-used">
${project.skillIconImages.map((x) => '<img src="' + x +'"')}
</div>
<div class="project-description">
<p>${project.description}</p>
</div>
</div>
`;
});
If the entire (non-refactored) function is needed, here it is:
(() => {
const projects = [
{
image: "http://via.placeholder.com/360x180",
title: "Project 1",
skillIconImages: ["http://via.placeholder.com/50x50", "http://via.placeholder.com/55x50", "http://via.placeholder.com/50x55", "http://via.placeholder.com/60x50"],
description: "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
},
{
image: "http://via.placeholder.com/360x180",
title: "Project 2",
skillIconImages: ["http://via.placeholder.com/50x50", "http://via.placeholder.com/55x50", "http://via.placeholder.com/50x55", "http://via.placeholder.com/60x50"],
description: "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
},
{
image: "http://via.placeholder.com/360x180",
title: "Project 3",
skillIconImages: ["http://via.placeholder.com/50x50", "http://via.placeholder.com/55x50", "http://via.placeholder.com/50x55", "http://via.placeholder.com/60x50"],
description: "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
}
];
// get the DOM element to where all the portfolio items will go into
let portfolioItemsDiv = document.getElementById('portfolio-items');
// render each project to the DOM
projects.forEach((project) => {
portfolioItemsDiv.innerHTML += `
<div class="portfolio-item">
<img src="${project.image}" alt="${project.title}" />
<h3 class="project-title">${project.title}</h3>
<div class="skills-used">
${project.skillIconImages.map((x) => '<img src="' + x +'"')}
</div>
<div class="project-description">
<p>${project.description}</p>
</div>
</div>
`;
});
})();
回答1:
You have to close that img
tag (which is the main problem), and join
the mapped array to get a string:
${project.skillIconImages.map((x) => '<img src="' + x +'">').join("")}
// ^ ^^^^^^^^
Also you should minimize the number of assignments to innerHTML
, instead of using +=
and constantly alter it (which is bad for performance), you should accumulate the HTML into a string and then use innerHTML = thatAccumulatedString
only once:
var htmlString = "";
htmlString += "... generated HTML for a portfolio ...";
.innerHTML = htmlString;
Or use reduce
which does the accumulation internally (also why not go ahead and use a template literal inside map
too) like:
let portfolioItemsDiv = document.getElementById('portfolio-items');
portfolioItemsDiv.innerHTML = projects.reduce((htmlString, project) => {
return htmlString + `
<div class="portfolio-item">
<img src="${project.image}" alt="${project.title}" />
<h3 class="project-title">${project.title}</h3>
<div class="skills-used">
${
project.skillIconImages.map(x => `<img src="${x}">`).join("")
}
</div>
<div class="project-description">
<p>${project.description}</p>
</div>
</div>
`;
}, "");
CSSless demo:
const projects = [{"image":"http://via.placeholder.com/360x180","title":"Project 1","skillIconImages":["http://via.placeholder.com/50x50","http://via.placeholder.com/55x50","http://via.placeholder.com/50x55","http://via.placeholder.com/60x50"],"description":"Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."},{"image":"http://via.placeholder.com/360x180","title":"Project 2","skillIconImages":["http://via.placeholder.com/50x50","http://via.placeholder.com/55x50","http://via.placeholder.com/50x55","http://via.placeholder.com/60x50"],"description":"Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."},{"image":"http://via.placeholder.com/360x180","title":"Project 3","skillIconImages":["http://via.placeholder.com/50x50","http://via.placeholder.com/55x50","http://via.placeholder.com/50x55","http://via.placeholder.com/60x50"],"description":"Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."}];
let portfolioItemsDiv = document.getElementById('portfolio-items');
portfolioItemsDiv.innerHTML = projects.reduce((htmlString, project) => {
return htmlString + `
<div class="portfolio-item">
<img src="${project.image}" alt="${project.title}" />
<h3 class="project-title">${project.title}</h3>
<div class="skills-used">
${
project.skillIconImages.map(x => `<img src="${x}">`).join("")
}
</div>
<div class="project-description">
<p>${project.description}</p>
</div>
</div>
`;
}, "");
<div id="portfolio-items"></div>
回答2:
Apart from missing the closing />
tag for the img
as specified by the answer above, if you are concerned about the performance then YES it would be an issue if you are frequently changing the HTML directly from the document, you can use DocumentFragment
for such requirements.
The DocumentFragment interface represents a minimal document object that has no parent. It is used as a lightweight version of Document that stores a segment of a document structure comprised of nodes just like a standard document. The key difference is that because the document fragment isn't part of the active document tree structure, changes made to the fragment don't affect the document, cause reflow, or incur any performance impact that can occur when changes are made.
You can create a document fragment by using document.createDocumentFragment and then add all your HTML to the fragment.
You can even modify elements after adding them to the fragment
just like you do with the document
like document.querySelector('div')
select all the divs from the document similarly fragment.querySelector('div')
will select the div elements within the fragment.
Do whatever you like and then, in the end, append it to your element that should be a valid document node.
You can separate the template
by wrapping them into the object literals that way you can categorize them too.
let portfolioItemsDiv = document.querySelector('#portfolio-items');
let fragment = document.createDocumentFragment();
let template = {
portfolio: (project) => {
return `<div class="portfolio-item">
<img src="${project.image}" alt="${project.title}" />
<h3 class="project-title">${project.title}</h3>
<div class="skills-used">
${project.skillIconImages.map((x) => '<img src="' + x +'"/>')}
</div>
<div class="project-description">
<p>${project.description}</p>
</div>
</div>
`
}
};
// render each project to the DOM
projects.forEach((project) => {
let temp = document.createElement('div');
temp.innerHTML = template['portfolio'].call(this, project);
fragment.appendChild(temp.firstChild);
});
portfolioItemsDiv.appendChild(fragment);
See a demo below
const projects = [{
"image": "https://via.placeholder.com/360x180",
"title": "Project 1",
"skillIconImages": ["https://via.placeholder.com/50x50", "https://via.placeholder.com/55x50", "https://via.placeholder.com/50x55", "https://via.placeholder.com/60x50"],
"description": "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
},
{
"image": "https://via.placeholder.com/360x180",
"title": "Project 2",
"skillIconImages": ["https://via.placeholder.com/50x50", "https://via.placeholder.com/55x50", "https://via.placeholder.com/50x55", "https://via.placeholder.com/60x50"],
"description": "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
},
{
"image": "https://via.placeholder.com/360x180",
"title": "Project 3",
"skillIconImages": ["https://via.placeholder.com/50x50", "https://via.placeholder.com/55x50", "https://via.placeholder.com/50x55", "https://via.placeholder.com/60x50"],
"description": "Ex rationibus deterruisset eos, ius tale nullam officiis an. Duo propriae mentitum salutandi id, nulla nobis persequeris ut eam, ex dicta libris laboramus duo. Amet errem voluptatibus an vix, eum ut nulla scriptorem. Ea etiam noluisse perfecto eum. Nec ad consul ubique, his id reque nonumy percipit."
}
];
// get the DOM element to where all the portfolio items will go into
let portfolioItemsDiv = document.querySelector('#portfolio-items');
let fragment = document.createDocumentFragment();
let template = {
portfolio: (project) => {
return `<div class="portfolio-item">
<img src="${project.image}" alt="${project.title}" />
<h3 class="project-title">${project.title}</h3>
<div class="skills-used">
${project.skillIconImages.map((x) => '<img src="' + x +'"/>')}
</div>
<div class="project-description">
<p>${project.description}</p>
</div>
</div>
`
}
};
// render each project to the DOM
projects.forEach((project) => {
let temp = document.createElement('div');
temp.innerHTML = template['portfolio'].call(this, project);
fragment.appendChild(temp.firstChild);
});
//append the fragment to the document element
portfolioItemsDiv.appendChild(fragment);
<div id="portfolio-items">
</div>
来源:https://stackoverflow.com/questions/50125489/using-vanilla-js-loop-through-an-array-of-objects-with-an-array-as-a-prop-using