首页 > 解决方案 > 渲染到文档嵌套的 dom 节点

问题描述

我一直在研究如何将 html 渲染到文档中,主要是为了好玩和学习。我看到了如何反应,并且大多数 vdom/hyperscript 实现都递归地呈现嵌套节点。想知道如何使用数组实现类似的功能。

所以,如果我有一个返回渲染方法的函数,它会输出一个数组。

    function Comp() {
        return {
          render: function () {
            return [
              "<div>",
              '  <button name="button-test" type="button">test</button>',
              SubComp(),
              "</div>",
            ];
          },
          postRender: function (dom) {
            dom
              .querySelector("button[name=button-test]")
              .addEventListener("click", function () {
                console.log("haha");
              });
          },
        };
      }


      function SubComp() {
    return {
      render: function () {
        return [
          "<div>",
          '  <button name="button-does-nothing" type="button">nothing</button>',
          "</div>",
        ];
      },
    };
  }

在上面的示例中,我可以忽略 postrender 方法,从 Comp 内部调用 SubComp().render() 方法并执行以下操作:

 document.getElementById('someid').innerHTML(Comp().render().flat(Infinity).join(''));

然后在执行任何与 dom 相关的东西之后。这将嵌套我放入的任何内容。但是,例如,如果我想专门为该节点或 html 的一部分附加 dom 事件,我也会失去处理每个函数的能力。

所以我的问题是,有没有人知道我可以通过为每个函数提供可用的 dom 节点来实现这一点。然后我可以执行它的 postrender 方法(或当节点准备好时我想要使用的 wtv),然后才将它嵌套到它可能具有的外部节点中。

同样,这更像是一种练习和学习,所以请尽量不要向我推荐库或框架。

PS:如果有人知道如何,在运行时将返回的数组转换为超脚本对象也对我有用。

非常感谢。

标签: javascriptarraysrecursion

解决方案


这就是我想出的。

我认为这可以通过减少来改善。这我目前走了多远,没有做太多的测试,但它似乎有效。

<!DOCTYPE html>
<html lang="en">
  <head>
    <script>
    function render(component) {
  if (component.preRender) {
    component.preRender();
  }
  var thisComponent = component.render();
  var thisDom = document.createElement('div');
  var children = [];
  thisComponent.forEach(function (c, index) {
    if (typeof c === 'object' && c.hasOwnProperty('render')) {
      children.push(render(c, thisDom.querySelector('div')));
      thisComponent[index] = '<div id="child' + (children.length) + '"></div>';
    }
  });
  var parsedDom = new DOMParser().parseFromString(thisComponent.flat(Infinity).join(''), 'text/html').body.childNodes;
  var length = parsedDom.length;
  for (var i = 0; i < length; i += 1) {
    thisDom.appendChild(parsedDom[0]);
  }
  if (component.postRender) {
    component.postRender(thisDom);
  }
  children.forEach(function (child, index) {
    while (child.firstChild) {
      var tchild = child.removeChild(child.firstChild);
      thisDom.querySelector('[id=child' + (index + 1) + ']').parentElement.insertBefore(tchild, thisDom.querySelector('[id=child' + (index + 1) + ']'));
    }
    thisDom.querySelector('[id=child' + (index + 1) + ']').remove();
  });
  return thisDom;
}
    </script>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
<script>
  function Component() {
    return {
      render: function () {
        return [
          '  <button name="button-test" type="button">test click me</button>',
          '<div id="test">',
          SubComponent(),
          "</div>",
        ];
      },
      postRender: function (dom) {
        dom
          .querySelector("button[name=button-test]")
          .addEventListener("click", function () {
            console.log("this was added while detached haha");
          });
      },
    };
  }
  function SubComponent() {
    return {
      render: function () {
        return [
          '  <button name="button-does-nothing" type="button">nothing</button>',
          SubSubComp(),
        ];
      },
    };
  }
  function SubSubComp() {
    return {
      render: function () {
        return [
          '  <button name="button-does-nothing-too" type="button">does nothing as well</button>',
        ];
      },
    };
  }
  document.getElementById("app").appendChild(render(Component()));
</script>

因此,如果有人可以帮助改进代码,我也会接受作为答案。

另一件事,你们知道操纵分离的dom有多快吗?还是有这方面的基准?我发现只有 dom 附加到浏览器。


推荐阅读