0

I'm using fetch() to get jobs from JSON. For each job found, I want to generate a .card. I have the .card markup written in JS and when trying to append it to its parent container, I get a not a function error on console. Unsure why?

Code:

  function appendDataToHTML(data) {
    var mainContainer = document.getElementById("jobListing");

    console.log(mainContainer);

    // get count of json objs
    var data_count = Object.keys(data.jobs).length;

    // for each object, create card
    for (var i = 0; i < data_count; i++) {

      var job_title     = data.jobs[i].title;
      var job_location  = data.jobs[i].location.name;
      var job_link      = data.jobs[i].absolute_url;
      var description   = data.jobs[i].content;


      var html =
        '<div class="card">'+
          '<div class="card__body">'+
            '<div class="card__title">'+ job_title +'</div>'+
            '<div class="card__subtitle">'+ job_location +'</div>' +
            '<div class="card__copy">'+ description +'</div>'+
          '</div>'+
        '</div>';

      mainContainer.appendChild(html);
      // mainContainer.innerHTML(html);
    }

  }
<div id="jobListing">
  <!-- .card generated for each obj here -->
</div>
Freddy
  • 683
  • 4
  • 35
  • 114
  • `getElementsByClassName` produces a list of elements, not a single element. – VLAZ May 14 '20 at 10:55
  • @VLAZ - I've just changed my code to reference ID's (have updated code in question), but I get a `Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.` on `mainContainer.appendChild(html);`. I've also tried `mainContainer.innerHTML(html);` but get a `mainContainer.innerHTML is not a function` error on console? – Freddy May 14 '20 at 11:06
  • 1
    You also need to attach an actual element, not a DOM string. – VLAZ May 14 '20 at 11:10
  • Use `mainContainer.innerHTML = html;` instead of `.appendChild()`. `.appendChild()` expects a node, while `innerHTML` sets markup using a DOMString. https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild and https://developer.mozilla.org/nl/docs/Web/API/Element/innerHTML – Luuuud May 14 '20 at 11:11
  • 1
    @LuudJacobs you can't really use `innerHTML` in this case because you won't be appending new elements to the list, but rather replacing it with the last element that was created. – goto May 14 '20 at 11:26
  • 1
    @goto1 You're right. In that case use `mainContainer.insertAdjacentHTML('beforeend', html);` https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML – Luuuud May 14 '20 at 11:29

1 Answers1

1

The issue with your code is that Node.appendChild expects a node rather than a string that represents HTML, so what you need to do is to actually create appropriate elements and then append them to jobListing.

Here's one way you could go about this:

function appendDataToHTML(data) {
  var data = {
    jobs: {
      "0": {
        title: "title_1",
        location: { name: "location.name_1" },
        absolute_url: "absolute_url_1",
        content: "content_1"
      },
      "1": {
        title: "title_2",
        location: { name: "location.name_2" },
        absolute_url: "absolute_url_2",
        content: "content_2"
      }
    }
  };

  var mainContainer = document.getElementById("jobListing");

  // get count of json objs
  var data_count = Object.keys(data.jobs).length;

  // for each object, create card
  for (var i = 0; i < data_count; i++) {
    var job_title = data.jobs[i].title;
    var job_location = data.jobs[i].location.name;
    var job_link = data.jobs[i].absolute_url;
    var description = data.jobs[i].content;
    
    // create appropriate HTML elements
    var card = document.createElement("div");
    var cardBody = document.createElement("div");
    var cardTitle = document.createElement("div");
    var cardSubtitle = document.createElement("div");
    var cardCopy = document.createElement("div");
    
    // append classes to each element
    card.classList.add("card");
    cardBody.classList.add("card__body");
    cardTitle.classList.add("card__title");
    cardSubtitle.classList.add("card__subtitle");
    cardCopy.classList.add("card__copy");

    // set the text content for each node
    cardTitle.textContent = job_title;
    cardSubtitle.textContent = job_location;
    cardCopy.textContent = description;

    // append each element to where they belong
    cardBody.appendChild(cardTitle);
    cardBody.appendChild(cardSubtitle);
    cardBody.appendChild(cardCopy);
    card.appendChild(cardBody);

    // append final `card` element to `mainContainer`
    mainContainer.appendChild(card);
  }
}
appendDataToHTML();
<div id="jobListing">
  <!-- .card generated for each obj here -->
</div>

Alternatively, you could use Node.innerHTML but you have to change your for loop slightly so that it appends all elements to a single string and then uses that string to set innerHTML of the mainContainer element.

function appendDataToHTML(data) {
  var data = {
    jobs: {
      "0": {
        title: "title_1",
        location: { name: "location.name_1" },
        absolute_url: "absolute_url_1",
        content: "content_1"
      },
      "1": {
        title: "title_2",
        location: { name: "location.name_2" },
        absolute_url: "absolute_url_2",
        content: "content_2"
      }
    }
  };

  var mainContainer = document.getElementById("jobListing");

  // get count of json objs
  var data_count = Object.keys(data.jobs).length;

  var cards = "";

  for (var i = 0; i < data_count; i++) {
    var job_title = data.jobs[i].title;
    var job_location = data.jobs[i].location.name;
    var job_link = data.jobs[i].absolute_url;
    var description = data.jobs[i].content;

    cards +=
      '<div class="card">' +
        '<div class="card__body">' +
          '<div class="card__title">' + job_title + "</div>" +
          '<div class="card__subtitle">' + job_location + "</div>" +
          '<div class="card__copy">' + description + "</div>" +
        "</div>" +
      "</div>";
  }
  mainContainer.innerHTML = cards;
}
appendDataToHTML();
<div id="jobListing">
  <!-- .card generated for each obj here -->
</div>
goto
  • 4,336
  • 15
  • 20