Mikhail Shcheglakov
Mikhail Shcheglakov

Reputation: 3

HTML generation from Javascript nested object

really need your help. I'm trying to write a function that would generate HTML markup from javascript object.

My thoughts is I send an object and a root element as parameters and recursively append elements.

Here is the code.

const struct = [
  {
    tag: 'div',
    classes: ['container'],
    innerHtml: [
      {
        tag: 'input',
        classes: ['input'],
        attributes: [
          ['type', 'text'],
          ['placeholder', 'Some input']
        ]
      },
      {
        tag: 'div',
        classes: ['btn-block'],
        innerHtml: [
          {
            tag: 'div',
            classes: ['btn', 'btn-long'],
            innerText: 'Long Button'
          },
          {
            tag: 'div',
            classes: ['btn', 'btn-big', 'btn-img'],
            innerHtml: [
              {
                tag: 'img',
                attributes: [
                  ['src', 'https://www.w3schools.com/images/w3certified_logo_250.png']
                ],
              }
            ],
          }
        ]
      },
      {
        tag: 'div',
        classes: ['red']
      }
    ]
  }
];

const root = document.body;

function create(obj, root) {
  obj.forEach(o => {
    const element = document.createElement(o.tag);

    if (o.classes) {
      const classes = o.classes;
     element.classList.add(...classes);
    }
    if (o.attributes) {
      o.attributes.forEach(a => {
        element.setAttribute(a[0], a[1]);
      })
    }

    if (o.hasOwnProperty('innerHtml')) {
      element.append(create(o.innerHtml, element));
    }

    if (o.innerText) {
      element.innerText = o.innerText
    }

    root.append(element);
  });
}

create(struct, root);

And there is a result;

As you can see the function add text 'Undefined' to every element.

Could you help me to fix it?

UPD: Solved by answers from @CertainPerformance and @Nina Scholz

Upvotes: 0

Views: 58

Answers (2)

CertainPerformance
CertainPerformance

Reputation: 370699

The problem is

element.append(create(o.innerHtml, element));

But create doesn't return anything, so undefined is appended to the end of every element. Change to just

create(o.innerHtml, element)

instead:

const struct = [{
  tag: 'div',
  classes: ['container'],
  innerHtml: [{
      tag: 'input',
      classes: ['input'],
      attributes: [
        ['type', 'text'],
        ['placeholder', 'Some input']
      ]
    },
    {
      tag: 'div',
      classes: ['btn-block'],
      innerHtml: [{
          tag: 'div',
          classes: ['btn', 'btn-long'],
          innerText: 'Long Button'
        },
        {
          tag: 'div',
          classes: ['btn', 'btn-big', 'btn-img'],
          innerHtml: [{
            tag: 'img',
            attributes: [
              ['src', 'https://www.w3schools.com/images/w3certified_logo_250.png']
            ],
          }]
        }
      ]
    },
    {
      tag: 'div',
      classes: ['red']
    }
  ]
}];

const root = document.body;

function create(obj, root) {
  obj.forEach(o => {
    const element = document.createElement(o.tag);

    if (o.classes) {
      const classes = o.classes;
      element.classList.add(...classes);
    }
    if (o.attributes) {
      o.attributes.forEach(a => {
        element.setAttribute(a[0], a[1]);
      })
    }

    if (o.hasOwnProperty('innerHtml')) {
      create(o.innerHtml, element)
    }

    if (o.innerText) {
      element.innerText = o.innerText
    }

    if (element !== undefined) {
      root.append(element);
    }
  });
}

create(struct, root);
.container {
  padding: 5px;
  border: 1px solid black;
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.input {
  height: 20px;
  width: 200px;
}

.btn-block {
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.btn {
  border: 1px solid black;
  border-radius: 5px;
  padding: 5px 15px;
  text-align: center;
}

.btn:hover {
  cursor: pointer;
}

.btn-long {
  width: 300px;
  margin-right: 10px;
}

.red {
  background: red;
  height: 100px;
  width: 100px;
}

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386560

You need only

create(o.innerHtml, element);

without wrapping element.append(/* ... */); because your function does not return somthing.

function create(obj, root) {
  obj.forEach(o => {
    const element = document.createElement(o.tag);

    if (o.classes) {
      const classes = o.classes;
      element.classList.add(...classes);
    }
    if (o.attributes) {
      o.attributes.forEach(a => {
        element.setAttribute(a[0], a[1]);
      })
    }

    if (o.hasOwnProperty('innerHtml')) {
       create(o.innerHtml, element);
      //element.append();
    }

    if (o.innerText) {
      element.innerText = o.innerText
    }

    root.append(element);
  });
}


const struct = [{
  tag: 'div',
  classes: ['container'],
  innerHtml: [{
      tag: 'input',
      classes: ['input'],
      attributes: [
        ['type', 'text'],
        ['placeholder', 'Some input']
      ]
    },
    {
      tag: 'div',
      classes: ['btn-block'],
      innerHtml: [{
          tag: 'div',
          classes: ['btn', 'btn-long'],
          innerText: 'Long Button'
        },
        {
          tag: 'div',
          classes: ['btn', 'btn-big', 'btn-img'],
          innerHtml: [{
            tag: 'img',
            attributes: [
              ['src', 'https://www.w3schools.com/images/w3certified_logo_250.png']
            ],
          }],
        }
      ]
    },
    {
      tag: 'div',
      classes: ['red']
    }
  ]
}];

const root = document.body;
create(struct, root);

Upvotes: 1

Related Questions