0

I am trying to make the following bit of code easier to maintain. I am not a web developer so bear with me. I think the following approach is appropriate.

I would like to dynamically add content and attributes to an html file using either javascript or jQuery. The items could reside in a .csv or .json (or something else?) file.


Given content like this

<div class="filtr-container">

    <div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="cat-1" data-date="2018-02-09">
        <div class="card-inner-border box-shadow">
            <a href="address-1.html">
                <img class="card-img-top rounded-top" src="./images/image-1.jpg" alt="img-2-alt">
            </a>
            <div class="card-body">
                <h5 class="card-title">Title-1</h5>
                <p class="card-text card-desc">
                    This is a description for title-1 content.
                </p>
                <a href="address-1.html">
                    <button type="button" class="btn btn-sm btn-outline-secondary">View</button>
                </a>
                <p class="card-text">
                    <small class="text-muted">Last updated February 2, 2018</small>
                </p>
            </div>
        </div>
    </div>

    <div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="cat-2, cat-3" data-date="2018-02-14">
        <div class="card-inner-border box-shadow">
            <a href="address-2.html">
                <img class="card-img-top rounded-top" src="./images/image-2.jpg" alt="img-2-alt">
            </a>
            <div class="card-body">
                <h5 class="card-title">Title-2</h5>
                <p class="card-text card-desc">
                    Here is a long description for title-2 content.
                </p>
                <a href="address-2.html">
                    <button type="button" class="btn btn-sm btn-outline-secondary">View</button>
                </a>
                <p class="card-text">
                    <small class="text-muted">Last updated February 14, 2018</small>
                </p>
            </div>
        </div>
    </div>

    <!-- MANY MORE CARDS / ITEMS ... -->

</div> <!-- End of filtr-container -->

I think we could abstract the details into something like this (.csv)

item-id,title,description,categories,address,image,image-alt,update
1,Title-1,This is a description for title-1 content.,cat-1,address-1.html,image-1.jpg,img-1-alt,2018-02-09
2,Title-2,Here is a long description for title-2 content.,"cat-2, cat-2",address-2.html,image-2.jpg,img-2-alt,2018-02-14

What's a nice approach of attack for using `javascript` or `jQuery` to add this content from the `.csv` or `.json` file?

A few concerns:

  • The headers of the .csv will not match verbatim (e.g. <p class="card-desc"> aligns with the .csv header of description)
  • There could be embedded comma-separated items (e.g. item 2 has categories cat-2, cat-3 so it gets quotes " in the .csv -- maybe .json would better (?) or perhaps its a non-issue)
  • If possible can we reuse the date item for both data-date= and the final piece of text <small class="text-muted"> which converts the date into Last updated month-name-long, dd, yyyy instead of yyyy-mm-dd.
  • Some attributes are partial references (e.g. the src for an image is just the final part of the path; stated as image-1.jpg in the .csv not ./images/image-jpg).

To hopefully help make this feel less complicated, here's a picture with the highlighted elements that could be "referenced" from the .csv file.

Image highlighted referenced elements

To me this feels like:

  1. Read in the .csv file.
  2. For each item in the .csv file, append objects to $(".filtr-container") with the shell layout...

But I'm lost when it comes to the particulars or if that's an appropriate approach.

JasonAizkalns
  • 20,243
  • 8
  • 57
  • 116
  • You have not really stated a goal for doing this other than _I am not a web developer so bear with me. I think the following approach is appropriate._ All of this string parsing is potentially costly in terms of processing. The browser is optimized to do this sort of thing for us rather than writing a bunch of code to wretch control from the browser and do it ourselves. If you have the structure, you have the attributes, you have the attribute values, why not just store all that as HTML and allow the browser to do its job? – Randy Casburn Jan 24 '19 at 15:21
  • @RandyCasburn The point is those `div` elements are currently maintained by hand. So why not store them in `csv` or `json` file. I believe the string processing is limited with the exception of the date field? I am also not exactly sure what you mean by your last statement: "If you have the structure, you have the attributes, you have the attribute values, why not just store all that as HTML and allow the browser to do its job?" Can you elaborate? – JasonAizkalns Jan 24 '19 at 15:25
  • That is rhetorical: Your concept modifies `div`, `image`, `a`, `p`, `textNode` DOM nodes , so how do you cross reference each line of your CSV to the DOM node it modifies? It should become self evident that there is a lot more to think about. Even the single answer you've received has avoided this very topic by simplifying it down to _"it's a matter of rendering it to the DOM_". Best of luck to you. – Randy Casburn Jan 24 '19 at 15:34
  • @RandyCasburn -- ah, I see, I should have been more clear. Every `div` element will have the 'shell' same structure (see the picture) at the bottom; all that changes is the highlighted elements sourced from the csv file. The order of rendering in the DOM does not matter, they can simply be rendered in-order they appear in the `.csv` file (there is already a javascript function that will rearrange the "cards" by date upon load). – JasonAizkalns Jan 24 '19 at 15:38
  • OK. I see it now. Then template parsing is for you. If I get a moment I'll work up an easy answer. – Randy Casburn Jan 24 '19 at 15:41
  • @RandyCasburn yes. I appreciate any insight you can provide. I took a quick peak at VueJS and it looks pretty slick, but probably overkill for something this simple. – JasonAizkalns Jan 24 '19 at 15:45

2 Answers2

1

You seem to be searching for template parsing. You can find many libraries that will ease this burden. In its simplest form, template parses carry out the steps in the following code. If you don't need the flexibility, power, features, etc. from a template parser library or full framework, you should consider not including the thousands of lines of code if all you want to accomplish is what is shown below.

Since you mentioned both JSON and CSV I've included the code to parse both. I'll leave the AJAX and date formatting magic to you. I don't think I populate the ID either, but this shows that more data than template attributes will work fine.

let template = document.getElementById('card-template').innerHTML;
let container = document.querySelector('.filtr-container');

// Do some ajax magic to get csv file
let csv = `item-id,title,description,categories,address,image,image-alt,update
1,Title-1,This is a description for title-1 content.,cat-1,address-1.html,https://via.placeholder.com/75,img-1-alt,2018-02-09
2,Title-2,Here is a long description for title-2 content.,cat-2 cat-2,address-2.html,https://via.placeholder.com/75,img-2-alt,2018-02-14`;

let csvLines = csv.split("\n");
let csvHeaders = csvLines.shift().split(',');
csvLines.forEach(line => {
  let parsed = template;
  let props = line.split(',');
  props.forEach((prop, idx) => {
    parsed = parsed.replace('{{' + csvHeaders[idx] + '}}', props[idx]);
  });
  container.innerHTML = container.innerHTML + parsed;
});

let json = `[{
  "item-id": "1",
  "title": "Title-1",
  "description": "This is a description for title-1 content.",
  "categories": "cat-1",
  "address": "address-1.html",
  "image": "https://via.placeholder.com/75",
  "image-alt": "img-1-alt",
  "update": "2018-02-09"
}, {
  "item-id": "2",
  "title": "Title-2",
  "description": "Here is a long description for title-2 content.",
  "categories": "cat-2 cat-2",
  "address": "address-2.html",
  "image": "https://via.placeholder.com/75",
  "image-alt": "img-2-alt",
  "update": "2018-02-14"
}]`;

let data = JSON.parse(json);
data.forEach(col => {
  let jParsed = template;
  for (prop in col) {
    jParsed = jParsed.replace('{{' + prop + '}}', col[prop]);
  }
  container.innerHTML = container.innerHTML + jParsed;
});
<div class="filtr-container">

  <script type="template" id="card-template">
    <div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="{{categories}}" data-date="{{date}}">
      <div class="card-inner-border box-shadow">
        <a href="{{address}}">
          <img class="card-img-top rounded-top" src="{{image}}" alt="{{image-alt}}">
        </a>
        <div class="card-body">
          <h5 class="card-title">{{title}}</h5>
          <p class="card-text card-desc">
            {{description}}
          </p>
          <a href="{{address}}">
            <button type="button" class="btn btn-sm btn-outline-secondary">View</button>
          </a>
          <p class="card-text">
            <small class="text-muted">Last updated {{update}}</small>
          </p>
        </div>
      </div>
    </div>
  </script>
Randy Casburn
  • 13,840
  • 1
  • 16
  • 31
0

This post might help you parse your CSV document. If your data lives in a JSON, you can use JSON.parse

Once you properly retrieved and parsed your data, it's a matter or rendering it to the DOM.

You can do it using the standard javascript library, JQuery or frameworks such as React or VueJS

Saraband
  • 1,540
  • 10
  • 18