简体   繁体   中英

Javascript - object loop and organizing data within HTML

I'm trying to loop thro object recursively and output the data in a organized way within the HTML. Below is what i have so far but i'm currently stuck as i can only output the rows but not indentation levels. The sample data in the preview element is output representation of what i'm trying to do.

Also if there is a better way to organize/structure the data (via grouping or similar) i would appreciate the suggestion.

 var obj = { 0: { "Shirts": 0, "Jeans": 1, "Brand": "Lorem ipsum" }, 1: { "Color": { "blue" : 1, "red" : 0 }, "Size": { "Man": { "small": 1, "medium": 0, "large": 0 }, "Women": { "small": 1, "medium": 1, "large": 1 } } }, 2: { "Shipping": { "overNight": "$ 10", "3 days": "$ 4" } }, 3: { "Stock": { "Man": [ { "green jeans": 13, "green shirts": 17, "green shoes": 21 }, { "black jeans": 1, "black shirts": 12, "black shoes": 53 } ], "Women": [ { "green jeans": 2, "green shirts": 53, "green shoes": 11 }, { "black jeans": 6, "black shirts": 22, "black shoes": 29 } ] } } } var dataEl = document.getElementById('data'); var getProperties = function (obj) { for (var property in obj) { if (!obj.hasOwnProperty(property)) continue; if (obj.hasOwnProperty(property) && obj[property] !== null) { if (obj[property].constructor == Object) { dataEl.innerHTML += '<div class="row title">' + property + '</div>'; getProperties(obj[property]) } else if (obj[property].constructor == Array) { dataEl.innerHTML += '<div class="row title">' + property + '</div>'; for (var i = 0; i < obj[property].length; i++) { getProperties(obj[property][i]); } } else { dataEl.innerHTML += '<div class="row"><span>' + property +'</span>: ' + obj[property] + '</div>'; } } } } //getProperties(obj); 
 #data, #preview { display: block; width: 80%; font-size: 12px; font-family: Arial, Helvetica; padding: 10px; } #data .row, #preview .row { display: inline-block; width: 100%; margin-bottom: 2px; } #data .row.title, #preview .row.title { font-weight: bold; font-size: 14px; } #data .row.indent-1, #preview .row.indent-1 { padding-left: 20px; } #data .row.indent-2, #preview .row.indent-2 { padding-left: 40px; } 
 <div id="data"></div> <!-- This is how it should be structured --> <div id="preview"> <div class="row"><span>Shirts:</span>0</div> <div class="row"><span>Jeans:</span>1</div> <div class="row"><span>Brand:</span>Lorem ipsum</div> <div class="row title">Color</div> <div class="row indent-1"><span>blue:</span> 1</div> <div class="row indent-1"><span>red:</span> 0</div> <div class="row title">Size</div> <div class="row title indent-1">Man</div> <div class="row indent-2"><span>small:</span> 1</div> <div class="row indent-2"><span>medium:</span> 0</div> <div class="row indent-2"><span>large:</span> 0</div> <div class="row title indent-1">Women</div> <div class="row indent-2"><span>small:</span> 1</div> <div class="row indent-2"><span>medium:</span> 1</div> <div class="row indent-2"><span>large:</span> 1</div> <div class="row title">Shipping</div> <div class="row indent-1"><span>overNight:</span> $ 10</div> <div class="row indent-1"><span>3 days:</span> $ 4</div> <div class="row title">Stock</div> <div class="row title indent-1">Man</div> <div class="row indent-2"><span>green jeans:</span> 13</div> <div class="row indent-2"><span>green shirts:</span> 17</div> <div class="row indent-2"><span>green shoes:</span> 21</div> <div class="row indent-2"><span>black jeans:</span> 1</div> <div class="row indent-2"><span>black shirts:</span> 127</div> <div class="row indent-2"><span>black shoes:</span> 53</div> <div class="row title indent-1">Women</div> <div class="row indent-2"><span>green jeans:</span> 2</div> <div class="row indent-2"><span>green shirts:</span> 53</div> <div class="row indent-2"><span>green shoes:</span> 11</div> <div class="row indent-2"><span>black jeans:</span> 6</div> <div class="row indent-2"><span>black shirts:</span> 22</div> <div class="row indent-2"><span>black shoes:</span> 29</div> </div> 

When you're using a recursive function for html construction, instead of appending the resulting strings into an element directly it's better to store it into a variable & return it after the function finishes, which makes enclosing the results in a grouping element much easier - see an example approach compatible with your source object:

function getProperties(obj, depth) {
  depth = depth === undefined ? 0 : depth + 1;
  var html = '';

  // first handle arrays
  if (obj && obj.forEach) {
    html += '<div class="list" data-indent="' + depth + '">';
    obj.forEach(function (elems, index) {
      html += '<div class="item ' + (index % 2 ? 'even' : 'odd') + '" data-index"' + index + '">'
        + getProperties(elems, depth)
        + '</div>';
    });
    html += '</div>';
  }
  // the main block (comes after arrays as an array would pass this condition too)
  else if (obj && typeof obj === 'object') {
    // uncomment if you'd like the whole group enclosed in a parent:
    // if (depth === 0) html += '<div class="group">';
    Object.keys(obj).forEach(function (key) {
      html += '<div class="row" data-indent="' + depth + '">';
      if (depth > 0)
        html += '<span class="title">' + key + '</span>'
      html += getProperties(obj[key], depth)
      html += '</div>';
    });
    // if (depth === 0) html += '</div>';
  }
  // else the object is just a value
  else {
    html += '<span class="value">' + obj + '</span>';
  }

  return html;
}

document.getElementById('data').innerHTML = getProperties(obj);

You can see inside iterations (the forEach calls) I'm just appending result of another getProperties call to the resulting string ( html variable).

Another advantage is that you're not messing with "global state" (#data element's innerHTML), so the function always returns the same results no matter what the element's contents are - that's what's called functional approach.

As done so, nested elements are enclosed in parents, which makes indenting it easier - just set margin-left on a parent element (see the css below).

I replaced for loops with iteration functions ( forEach ) for more functional style and in case of the array to prevent introduction of intermediary variables ( i ), but it's just a matter of preference; also the conditions are a bit different as it's better to use feature detection (does the object have the function I'm going to use, eg forEach ?) Object.keys can be used on any object of the object type (that's why array resp. forEach detection comes before, array is also of the object type) and checking on a constructor is not recommended as it can be overwritten by a custom one.

I also added depth (that's for the data-indent attribute, which doesn't have any meaning here but you might utilise it somehow) & odd/even classes for the groups resulting from array iteration, I think these are quite self-explanatory.

I changed css classes in attempt to make it more contextually sensible, using this css should make it clear:

.row {
  margin-left: 10px;
}
.title {
  font-weight: bold;
}
.value {
  padding-left: 10px;
}
.list .odd {
  background-color: lightgray;
}
/* just to see how to target a custom attribute */
[data-indent="3"] {
  color: red;
}

(The data- prefix for custom attributes is needed to make it HTML validator compliant.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM