my name jeff
my name jeff

Reputation: 99

Iteratively print JavaScript dictionary of objects into HTML divs

I'd appreciate some help with iteratively generating the beneath div based on the amount of items in a Javascript dictionary.

    <div class="container" style="padding-bottom: 10px;">
        <div class="dropdown" style="padding: 10px;">
            <a href="#">TOP 3 PS5 HEADSETS<i class="fa fa-chevron-down"></i>
            </a>
            <ul>
                <div id="links">

                    <center>
                        <p>3: &darr; INSERT TITLE FOR STEELSERIES &darr;</p>
                    </center>
                    <div class="product">
                        <img src="img/products/h-steelseries.png">
                        <a class="link" href="INSERT LINK HERE">Read More</a>
                    </div>

                    <center>
                        <p>3: &darr; INSERT TITLE FOR OTHER&darr;</p>
                    </center>
                    <div class="product">
                        <img src="img/products/h-other.png">
                        <a class="link" href="INSERT LINK HERE">Read More</a>
                    </div>
                </div>
            </ul>
        </div>
    </div>

Beneath is the read.js file that contains the items in which I wish to generate the div class "product" for.

I'd really apprecaite any help with this.

var prod_obj = {
    "headphone_products" : {
        "title": "Steelseries",
        "IMAGE": "h-steelseries.png",
        "HREF" : "steelseries.html"
},
"other_products" : {
    "title": "Other product",
    "IMAGE": "h-other.png",
    "HREF" : "other.html"
}
};

I have looked at other answers and couldn't find an example of a dictionary of object that was used to automatically generate divs. I intend on using this to list items on a website and would like to append objects to the dictionary and them to automatically generate a new div for each object once the script is executed.

Thank you for your time.

Upvotes: 1

Views: 1752

Answers (4)

Cat
Cat

Reputation: 4236

The traditional way to add content to the DOM on-the-fly is to use a series of calls to createElmenent and appendChild (which is less error-prone than just inserting HTML strings). And you can loop through your data object's keys and extract the details you need to configure your new DOM elements. This script does both of these things in a function called updateDOM, which invokes the appendProductDetails function once per product.

I changed the hrefs to create functional (if arbitrary) links, and of course the images don't show up because they don't exist on StackOverflow's server. See the in-code comments for further explanation.

const currentProds = getProductsToShow();
updateDOM(currentProds);

function updateDOM(prod_obj) {

  // Identifies parent div
  const linksDiv = document.getElementById("links");

  // Clears parent div
  linksDiv.innerHTML = "";

  // Loops through productName (keys) in prod_obj
  const productNames = Object.keys(prod_obj);
  for (let productName of productNames) {

    // Gets details (inner object) for each product
    const details_obj = prod_obj[productName];

    // Creates, configures, and appends new elements for each product
    appendProductDetails(linksDiv, details_obj);
  }
}

function appendProductDetails(parentElement, detailsObject) {
  const
  // Gets local copies of values via "destructuring"
  { title, image, href } = detailsObject,
  path = "img/products/", // Defines path to images

    // Creates elements to add to the DOM
    productDiv = document.createElement("div"),
    titleP = document.createElement("p"),
    img = document.createElement("img"),
    anchor = document.createElement("a");

  // Configures newly created elements
  productDiv.classList.add("product");
  titleP.textContent = title;
  img.src = path + image;
  img.alt = image;
  anchor.classList.add("link");
  anchor.href = href;
  anchor.textContent = "Read More";

  // Puts children into productDiv
  productDiv.appendChild(titleP);
  productDiv.appendChild(img);
  productDiv.appendChild(anchor);

  // Attaches everything to the DOM
  parentElement.appendChild(productDiv);
}

// Provides demo data
function getProductsToShow() {
  const productsObj = {
    "headphone_products": {
      "title": "Steelseries",
      "image": "h-steelseries.png", // In img/products/
      "href": "https://stackoverflow.com"
    },
    "other_products": {
      "title": "Other product",
      "image": "h-other.png",
      "href": "https://eloquentjavascript.net/"
    }
  };
  return productsObj;
}
.container{ width: 250px; text-align: center; }
.dropdown > a{ text-decoration: none; }
p{ margin: -0.1rem 0; font-size: 1.2rem; }
.product{ padding: 0.5rem ; }
.link{ margin-left: 1rem; }
<div class="container">
  <div class="dropdown">
    <a href="#">PS5 HEADSETS</a>
    <div id="links"></div>
  </div>
</div>

(A more modern approach would be to repeatedly clone the contents of a template element and to use slot elements to insert corresponding product details into each new instance.)

Upvotes: 0

Professor Abronsius
Professor Abronsius

Reputation: 33813

What you describe sounds like a suitable candidate for a template which, according to the documentation on MDN says:

The HTML Content Template () element is a mechanism for holding HTML that is not to be rendered immediately when a page is loaded but may be instantiated subsequently during runtime using JavaScript.

The following uses a simple class to load a new instance of the designated template for each product found within the source data ( what you refer to as a dictionary ). Once the template has been loaded from the shadows you can manipulate the contents as you wish. If you change the design of the template you change the design of the final layout. In the original HTML there is no span element surrounding the individual products but the way I wrote the template loader( for a specific job ) clones the first child element entirely - so a span will not affect layout unless styled specifically to do so.

class TemplateLoader{
  constructor( id ){
    this.id=id;
    return this.create();
  };
  gettemplate(){
    return document.querySelector( 'template[ data-id="'+this.id+'" ]' ) || false
  };
  clone(){
    let tmpl=this.gettemplate();
    return tmpl ? tmpl.content.firstElementChild.cloneNode( true ) : false;
  };
  gettarget(){
    return document.querySelector( 'div[ id="'+this.id+'" ]' ) || false;
  };
  create(){
    let tmpl=this.clone();
    if( tmpl ){
      let target=this.gettarget();
        target.appendChild( tmpl );
      return tmpl;
    }
    return false;
  };
};

  var prod_obj = {
    'headphone_products' : {
      'title': 'steelseries',
      'image': 'h-steelseries.png',
      'href' : 'steelseries.html'
    },
    'other_products' : {
      'title': 'other product',
      'image': 'h-other.png',
      'href' : 'other.html'
    },
    'banana':{
      'title':'curvy & yellow',
      'image':'b-a-nana.png',
      'href':'banana.html'
    }
  };

  let id='links';

  Object.keys( prod_obj ).forEach( cat => {
    let data=prod_obj[ cat ];

    let oTmpl=new TemplateLoader( id );
      oTmpl.querySelector('center > p').textContent=data.title;
      oTmpl.querySelector('div.product > img').src=['img/products',data.image].join('/');
      oTmpl.querySelector('div.product > a.link').href=data.href;
  });
<!-- regular HTML -->
<div class='container'>
  <div class='dropdown'>
    <a href='#'>TOP 3 PS5 HEADSETS<i class='fa fa-chevron-down'></i></a>
    <ul>
      <div id='links'>
        <!-- items will be populated here -->
      </div>
    </ul>
  </div>
</div>








<!-- our template that will be used to generate new content within the above, regular HTML' -->
<template data-id='links'>
  <span>
    <center>
      <p></p>
    </center>
    <div class='product'>
      <img />
      <a class='link'>Read More</a>
    </div>
  </span>
</template>

Upvotes: 0

ultrayam
ultrayam

Reputation: 144

You can use for-in loops and template literals to achieve what you want to achieve here.

const prod_obj = {
  "headphone_products": {
    "title": "Steelseries",
    "image": "h-steelseries.png",
    "href": "steelseries.html"
  },
  "other_products": {
    "title": "Other product",
    "image": "h-other.png",
    "href": "other.html"
  }
};

const div = document.getElementById('insertHere');

for (let products_key in prod_obj) {
  let {title, image, href} = prod_obj[products_key];
  let html = `<p>Title: ${title}, Image: ${image}, href: ${href}</p>`;
  div.insertAdjacentHTML('beforeend', html);
}
<div id="insertHere">
</div>

Upvotes: 1

Som Shekhar Mukherjee
Som Shekhar Mukherjee

Reputation: 8168

You can simply loop over the object and create the desired nodes inside the loop.

Here's a simpler version of the same.

var prod_obj = {
  "headphone_products": {
    "title": "Steelseries",
  },
  "other_products": {
    "title": "Other product",
  }
};

for (let keys in prod_obj) {
  const div = document.createElement("div");
  div.innerText = prod_obj[keys].title
  document.body.appendChild(div)
}

Upvotes: 2

Related Questions