Warm tip: This article is reproduced from stackoverflow.com, please click
javascript

How to build html from template in javaScript?

发布于 2020-03-27 15:34:07

I have template, and i need to build html. I can parse each element but stuck when trying to build all html. My code did not work for now. And may by all my way is wrong, and it shuld be another method to make html from template. Can any one help?

const data = ['html', [
  ['head', [
    ['title', 'titletext'],
  ]],
  ['body', { class: 'bodyClass' }, [
    ['h1', { class: 'headerClass' }, 'h1text'],
    ['div', [
      ['span', 'span1text'],
      ['span', 'span2text'],
    ]], 
  ]],  
]];

const tagBuilder = (tagArray) => {
  if (tagArray[1] instanceof Array) {
    return tagBuilder(tagArray[1]);
  }
  if (tagArray[1] instanceof Object) {
    return `<${tagArray[0]} class="${tagArray[1].class}">` + String(tagArray[2]) + `</${tagArray[0]}>`;
  } 
  return `<${tagArray[0]}>` + String(tagArray[1]) + `</${tagArray[0]}>`;
}

const htmpBuilder = (data) => {
  return data.map(element => tagBuilder(element));
};

document.getElementById("output").textContent = htmpBuilder(data);
<pre id="output">
</pre>

Output i need:

<html>
  <head>
    <title>titletext</title>
  </head>
  <body class="bodyClass">
    <h1 class="headerClass">h1text</h1>
    <div>
      <span>span1text</span>
      <span>span2text</span>
    </div>
  </body>
</html>
Questioner
Lex27
Viewed
15
CertainPerformance 2020-01-31 19:35

You might consider creating a structure with objects instead - that way, you can just look up the .class property of the object, or the .contents property of the object, or the .tag property of the object, the process will probably make a whole lot more sense at a glance. The only real non-trivial logic involved is checking if the .contents is an array (in which case, recursively .map them by tagBuilder and join by the empty string):

const data = {
  tag: 'html',
  contents: [
    {
      tag: 'head',
      contents: [{
        tag: 'title',
        contents: 'titletext'
      }]
    }, {
      tag: 'body',
      class: 'bodyClass',
      contents: [{
        tag: 'h1',
        class: 'headerClass',
        contents: 'h1text'
      }, {
        tag: 'div',
        contents: [{
          tag: 'span',
          contents: 'span1text'
        }, {
          tag: 'span',
          contents: 'span2text'
        }]
      }]
    }
 ]
};
    

const tagBuilder = ({ tag, class: className, contents = '' }) => {
  const contentsStr = Array.isArray(contents)
    ? contents.map(tagBuilder).join('')
    : contents;
  return `<${tag}${className ? ` class="${className}"` : ''}>${contentsStr}</${tag}>`;
}

console.log(tagBuilder(data));

If you need newlines between tags and pretty-printed HTML too (which usually shouldn't matter), then pass along an indent parameter too, which gets added to the start of tag lines (and the start of end-tag lines, when that tag contains non-text children):

const data = {
  tag: 'html',
  contents: [
    {
      tag: 'head',
      contents: [{
        tag: 'title',
        contents: 'titletext'
      }]
    }, {
      tag: 'body',
      class: 'bodyClass',
      contents: [{
        tag: 'h1',
        class: 'headerClass',
        contents: 'h1text'
      }, {
        tag: 'div',
        contents: [{
          tag: 'span',
          contents: 'span1text'
        }, {
          tag: 'span',
          contents: 'span2text'
        }]
      }]
    }
 ]
};
    

const tagBuilder = ({ tag, class: className, contents = '' }, indent = 0) => {
  const contentsStr = Array.isArray(contents)
    ? '\n' + contents.map(item => ' '.repeat(indent + 2) + tagBuilder(item, indent + 2)).join('\n') + '\n'
    : contents;
  return `<${tag}${className ? ` class="${className}"` : ''}>${contentsStr}${Array.isArray(contents) ? ' '.repeat(indent) : ''}</${tag}>`;
}

console.log(tagBuilder(data));

If you have to use your original data structure (which I'd consider to be a mistake, since it requires confusing logic), then extract the tag, contents, and className from the array, then do the exact same thing:

const data = ['html', [
  ['head', [
    ['title', 'titletext'],
  ]],
  ['body', { class: 'bodyClass' }, [
    ['h1', { class: 'headerClass' }, 'h1text'],
    ['div', [
      ['span', 'span1text'],
      ['span', 'span2text'],
    ]], 
  ]],  
]];

    

const tagBuilder = (arr, indent = 0) => {
  const tag = arr.shift();
  const className = !Array.isArray(arr[0]) && typeof arr[0] === 'object'
    ? arr.shift().class
    : '';
  const contents = arr.length
    ? arr.shift()
    : '';
  const contentsStr = Array.isArray(contents)
    ? '\n' + contents.map(item => ' '.repeat(indent + 2) + tagBuilder(item, indent + 2)).join('\n') + '\n'
    : contents;
  return `<${tag}${className ? ` class="${className}"` : ''}>${contentsStr}${Array.isArray(contents) ? ' '.repeat(indent) : ''}</${tag}>`;
}

console.log(tagBuilder(data));