Generate multiple DOM-Node levels with jQuery dynamically

I am currently working on a small calculator project, where previous calculations are saved into a database, which in turn will be called and rendered as a list on the UI. A list element should roughly look like this in HTML:

<div class="item">
   <div class="item__value">1.197</div>
   <div class="item__btn">
     <button class="item__btn--el item__btn--del"><i class="ion-ios-close-outline"></i></button>
     <button class="item__btn--el item__btn--pull"><i class="ion-ios-refresh-outline"></i></button>
   </div>
</div>

With jQuery, I could only get as far, as the first tier. As the previous calculations are returned from the DB as an array, I made a small function, to create an item for every single one of them, but I couldn’t go deeper, than the first div.

const popCalc = function(arr, area) {
    arr.forEach(el => {
        $(area)
            .append(
               $(document.createElement('div'))
                    .text(el.result)
                    .addClass('item')
            )
    });
};

I also tried appending further child elements to the created ‘div’, but it didn’t help, as the system returned an error.

const popCalc = function(arr, area) {
    arr.forEach(el => {
        $(area)
            .append(
               $(document.createElement('div'))
                .addClass('item')
                .appendChild(
                    $(document.createElement('div'))
                    .addClass('item__value')
                    .text(el.result)
            )
        )
    });
};

So, has got anybody tips, how I could generate a ‘div’ and add two other ‘div’s within it, all while working with a dynamic source? Thanks a lot in advance.

Answer

I think appendChild works for standard javascript rather than jQuery and you probably just need to replace it with append:

const arr = [{ result: 'result 1' }, { result: 'result 2' }];

const popCalc = function(arr, area) {
  arr.forEach(el => {
    $(area)
      .append(
        $(document.createElement('div'))
          .addClass('item')
          .append(
            $(document.createElement('div'))
              .addClass('item__value')
              .text(el.result),
            $(document.createElement('div'))
              .addClass('item__btn')
              .append($(document.createElement('Button')).html('A button'))
          )
      )
  });
};

popCalc(arr, $('#area'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="area"></div>

If you’re working with something complicated it can be nicer to use some templating like in this answer (though that might be slower than using document.createElement (but may not be slower by enough to matter depending on what you’re doing)): https://stackoverflow.com/a/25852490/14454939

Another way to approach this sort of thing can be to have the structure in a somewhat more readable form and then use code to build the DOM elements:

const arr = [{ result: 'result 1' }, { result: 'result 2' }];

const new_item_def = (el) => ({
  tag: 'div', className: 'item', children: [
    { tag: 'div', className: 'item__value', innerHTML: el.result },
    { tag: 'div', className: 'item__btn', children: [
        { tag: 'button', innerHTML: 'A button' }
      ]
    },
  ]
});

const element_for_def = ({ tag, children, ...attr }) => {
  const element = Object.assign(document.createElement(tag), attr);

  if(children && children.length > 0)
    append_children_to_ele(element, children);

  return element;
};

const append_to_element = (parent) => (child) => parent.appendChild(child);

const append_children_to_ele = (parent, children) =>
  children
    .map(element_for_def)
    .forEach(append_to_element(parent));

const popCalc = function(arr, area) {
  arr.forEach(
    el => append_to_element(area)( element_for_def(new_item_def(el)) )
  );
};

popCalc(arr, document.getElementById('area'));
<div id="area"></div>

Leave a Reply

Your email address will not be published. Required fields are marked *