如何正确地在视觉上嵌套 HTML 自定义元素

How to properly visually nest HTML Custom Elements

我正在尝试编写一个简单的 HTML 自定义元素,用于将树放在页面上,使用的简单代码如下:

<html-tree title="root">
  <tree-node title="child1">
    <tree-node title="leaf1"></tree-node>
    <tree-node title="leaf2"></tree-node>
  </tree-node>
  <tree-node title="child2">
    <tree-node title="leaf3"></tree-node>
    <tree-node title="leaf4"></tree-node>
  </tree-node>
</html-tree>

每个元素基本上都是一个影子 dom,带有一个未命名的 <slot></slot> 用于放置子元素,所以我希望有一个漂亮的嵌套结构。相反,如果我将标准调试样式 :host>* { border:1px red solid; } 分配给这些自定义元素,每个元素都会显示在自己的行上,周围有边框,而不是将它们显示为嵌套。

如何以 CSS 很好的方式保留标记指定的嵌套?

片段:

/**
 * Main tree node class
 */
class GenericNode extends HTMLElement {
  constructor() {
    super();
    this._shadow = enrich(this.attachShadow({mode: `open`}));
    this._shadow.addSlot = () => this._shadow.add(create(`slot`));
    if (!this.get) {
      this.get = e => this.getAttribute(e);
    }
    this.setupDOM(this._shadow);
  }
  setupDOM(shadow) {    
    this.setStyle(`:host>* { border:1px red solid; }`)
    if (this.leadIn) this.leadIn(shadow);
    shadow.addSlot();    
    if (this.leadOut) this.leadOut(shadow);
  }
  setStyle(text) {
    if (!this._style) {
      this._style = create(`style`, text);
      this._shadow.add(this._style);
    } else {
      this._style.textContent = text;
    }
  }
}


/**
 * "not the root" element
 */
class Node extends GenericNode {
  constructor() {
    super();
  }
  leadIn(shadow) {
    shadow.add(create(`p`, this.get(`title`)));
  }
}

// register the component
customElements.define(`tree-node`, Node);

/**
 * "the root" element, identical to Node, of course.
 */
class Tree extends Node  {
  constructor() {
    super();
  }
}

// register the component
customElements.define(`html-tree`, Tree);


/**
 * utility functions
 */

function enrich(x) {
  x.add = e => x.appendChild(e);
  x.remove = e => {
    if (e) x.removeChild(e);
    else e.parentNode.removeChild(e);
  };
  x.get = e => x.getAttribute(x);  
  return x;
}

function find(qs) {
  return Array.from(
    document.querySelectorAll(qs).map(e => enrich(e))
  );
}

function create(e,c) {
  let x = enrich(document.createElement(e));
  x.textContent = c;
  return x;
};
<html-tree title="root">
  <tree-node title="child1">
  <tree-node title="leaf1"></tree-node>
    <tree-node title="leaf2"></tree-node>
  </tree-node>
  <tree-node title="child2">
    <tree-node title="leaf3"></tree-node>
    <tree-node title="leaf4"></tree-node>
  </tree-node>
</html-tree>

原来阴影的默认样式 dom 其内容为“无”,因此要实现真正的嵌套,you need to force display:block or be similarly explicit

在上面的代码中,不仅仅是在:host>*上设置边框,:host<slot>也需要显式标记为块:

setupDOM(shadow) {    
  this.setStyle(`
    :host {
      display: block;
      border: 1px red solid;
    }
    :host > slot {
      display: block;
      border: 1px red solid;
      margin-left: 1em;
    }
  `);
  ...
}