Using vanilla JS, Loop through an array of objects with an array as a prop using template literals

。_饼干妹妹 提交于 2021-02-11 06:44:54

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!