如何正确呈现我的节点树?

时间:2017-02-14 10:08:09

标签: javascript dom tree

我想使用JS对象(经典主题)构建DOM树。我完成了大部分工作但是我得到了一个无意义的结果:DOM树看起来是正确创建的但是所有节点都在一条线上呈现为扁平状态而且剪切了Input元素。

我非常怀疑buildNode功能无法正常工作,但我找不到这个缺陷。

/**** FILTERS ****/
// TODO : change crappy filter using dictionnary
const attrOnly = (str) => !(str === 'content' || str === 'tag' || str === 'children');

/**** TESTS ****/
const hasChildren = (obj) => obj.hasOwnProperty('children'),
      hasContent = (obj) => obj.hasOwnProperty('content');

// TODO: search for namespace given a tag name (SVG)
const findNameSpace = (str) => null;

// Build node with correct attributes and values
const buildNode = function (element, parent) {
  const tag = (element.tag || 'div'),
        tagAttr = Object.keys(element).filter(attrOnly),
        node = document.createElementNS(findNameSpace(tag), tag);
  tagAttr.forEach(
    (attr) => node.setAttributeNS(findNameSpace(tag), attr, element[attr])
  );
  hasContent(element) ? node.innerHTML = element.content : null;
  return parent.appendChild(node);
}

// Walk along the tree mapping current element with function f.
function walkTree(f, element, parent) {
  const current = f(element, parent);
  // Reccursively walk children, if necessary
  (hasChildren(element) && element.children.forEach(
    child => walkTree(f, child, current)
  ));
};

let tree = {
  tag: 'div',
  id: 'tree',
  children: [{
    tag: 'section',
    id: 'section-l1-1',
    class: 'l1',
    content: 'Use <em>me</em> as I am, I am gorgeous!',
    children: [{
      tag: 'div',
      id: 'div-l2',
      class: 'l2',
      children: [{
        tag: 'p',
        content: 'Here is a nested paragraph.'
      }]
    }, {
      tag: 'form',
      id: 'form-l2',
      class: 'l2',
      onsubmit: 'alert("Function called!");',
      children: [{
        tag: 'input',
        type: 'text',
        id: 'input-l3',
        class: 'l3',
        value: 'self-closing tag case!'
      }]
    }]
  }, {
    tag: 'footer',
    id: 'end-page',
    class: 'l1',
    content: 'This is a running experiment.'
  }]
};

walkTree(buildNode, tree, document.getElementById('seed'));
#seed div,form,input {
  display: block;
}
<div id="seed"></div>

1 个答案:

答案 0 :(得分:0)

所以我发现命名空间处理是我的问题。由于我想同时渲染HTML元素和SVG元素,我需要...

我找不到对该问题的正确解决方法,因此我设置了较低的期望,仅使用document.createElement()(和Node.setAttribute())代替document.createElementNS()(和Node.setAttributeNS()来呈现HTML )。

要了解命名空间处理的进度:

&#13;
&#13;
/* DICTIONARIES */
const nonAttr = ['content', 'tag', 'children'],
      // Search for tag's special namespace. Source: https://www.w3.org/TR/2011/WD-html5-20110525/namespaces.html
      tagNamespace = {
        'svg': 'https://www.w3.org/2000/svg'
      };

/**** FILTERS ****/
const attrOnly = (str) => !(nonAttr.includes(str));

/**** TESTS ****/
const hasChildren = (obj) => obj.hasOwnProperty('children'),
      hasContent = (obj) => obj.hasOwnProperty('content');

// Search for namespace given a tag name
const findNameSpace = (str) => (tagNamespace[str] || 'http://www.w3.org/1999/xhtml');

// Build node with correct attributes and values
const buildNode = function (element, parent) {
  const tag = (element.tag || 'div'),
        tagAttr = Object.keys(element).filter(attrOnly),
        node = document.createElementNS(findNameSpace(tag), tag);
  tagAttr.forEach(
    (attr) => node.setAttribute(attr, element[attr])
  );
  hasContent(element) ? node.innerHTML = element.content : null;
  return parent.appendChild(node);
}

// Walk along the tree mapping current element with function f.
function walkTree(f, element, parent) {
  const current = f(element, parent);
  // Reccursively walk children, if necessary
  (hasChildren(element) && element.children.forEach(
    child => walkTree(f, child, current)
  ));
};

let tree = {
  tag: 'div',
  id: 'tree',
  children: [{
    tag: 'section',
    id: 'section-l1-1',
    class: 'l1',
    content: 'Use <em>me</em> as I am, I am gorgeous!',
    children: [{
      tag: 'div',
      id: 'div-l2',
      class: 'l2',
      children: [{
        tag: 'p',
        content: 'Here is a nested paragraph.'
      }]
    }, {
      tag: 'form',
      id: 'form-l2',
      class: 'l2',
      onsubmit: 'alert("Function called!");',
      action: '',
      method: 'GET',
      children: [{
        tag: 'input',
        type: 'text',
        id: 'text-l3',
        name: 'text-l3',
        class: 'l3',
        placeholder: 'self-closing tag case!'
      },{
        tag: 'input',
        type: 'submit',
        id: 'submit-l3',
        name: 'submit-l3',
        class: 'l3',
        value: 'submit!'
      }]
    }]
  }, {
    tag: 'footer',
    id: 'end-page',
    class: 'l1',
    content: 'This is a running experiment.',
    children: [{
      tag: 'svg',
      class: 'l2',
      children : [{
        tag: 'rect',
        width: '10',
        height: '10',
        fill: 'black'
      }]
    }]
  }]
};

walkTree(buildNode, tree, document.getElementById('seed'));
&#13;
.l1 {
  background-color: DarkGrey;
}
.l2 {
  background-color: Grey;
}
.l3 {
  background-color: LightGrey;
}
.l4 {
  background-color: White;
}
&#13;
<div id="seed"></div>
&#13;
&#13;
&#13;

要查看没有命名空间支持的工作代码

&#13;
&#13;
/* DICTIONARIES */
const nonAttr = ['content', 'tag', 'children'];

/**** FILTERS ****/
const attrOnly = (str) => !(nonAttr.includes(str));

/**** TESTS ****/
const hasChildren = (obj) => obj.hasOwnProperty('children'),
  hasContent = (obj) => obj.hasOwnProperty('content');


// Build node with correct attributes and values
const buildNode = function(element, parent) {
  const tag = (element.tag || 'div'),
    tagAttr = Object.keys(element).filter(attrOnly),
    node = document.createElement(tag);
  tagAttr.forEach(
    (attr) => node.setAttribute(attr, element[attr])
  );
  hasContent(element) ? node.innerHTML = element.content : null;
  return parent.appendChild(node);
}

// Walk along the tree mapping current element with function f.
function walkTree(f, element, parent) {
  const current = f(element, parent);
  // Reccursively walk children, if necessary
  (hasChildren(element) && element.children.forEach(
    child => walkTree(f, child, current)
  ));
};

let tree = {
  tag: 'div',
  id: 'tree',
  children: [{
    tag: 'section',
    id: 'section-l1-1',
    class: 'l1',
    content: 'Use <em>me</em> as I am, I am gorgeous!',
    children: [{
      tag: 'div',
      id: 'div-l2',
      class: 'l2',
      children: [{
        tag: 'p',
        content: 'Here is a nested paragraph.'
      }]
    }, {
      tag: 'form',
      id: 'form-l2',
      class: 'l2',
      onsubmit: 'alert("Function called!");',
      action: '',
      method: 'GET',
      children: [{
        tag: 'input',
        type: 'text',
        id: 'text-l3',
        name: 'text-l3',
        class: 'l3',
        placeholder: 'self-closing tag case!'
      }, {
        tag: 'input',
        type: 'submit',
        id: 'submit-l3',
        name: 'submit-l3',
        class: 'l3',
        value: 'submit!'
      }]
    }]
  }, {
    tag: 'footer',
    id: 'end-page',
    class: 'l1',
    content: 'This is a running experiment.',
    // SVG CAN'T GET RENDERED BECAUSE NAMESPACES
    children: [{
      tag: 'svg',
      class: 'l2',
      children: [{
        tag: 'rect',
        fill: 'black'
      }]
    }]
  }]
};

walkTree(buildNode, tree, document.getElementById('seed'));
&#13;
.l1 {
  background-color: DarkGrey;
}

.l2 {
  background-color: Grey;
}

.l3 {
  background-color: LightGrey;
}

.l4 {
  background-color: White;
}
&#13;
<div id="seed"></div>
&#13;
&#13;
&#13;

PS。我不得不说我嘴里有污垢......