【问题标题】:How do we detect if a shadow root was made with v0 or v1 API?我们如何检测影子根是使用 v0 还是 v1 API 创建的?
【发布时间】:2016-10-12 16:47:36
【问题描述】:

假设一个 JS 模块导出 shadowRoot,它是用 el.createShadowRootel.attachShadow 创建的(我们不知道是哪个)。我们如何检测根是 v0 影子根还是 v1 影子根(即我们如何检测使用哪种方法创建根)?

f.e.,我将在以下条件语句中填写什么?

// for argument's sake, we don't create the root, we only get a reference
// to it:
import shadowRoot from 'somewhere'

function getShadowRootVersion(root) {
    if ( ... )
        return 'v0'

    if ( ... )
        return 'v1'
}

console.log(getShadowRootVersion(shadowRoot)) // should output "v0" or "v1".

更多信息:

我们想知道影子根是从createShadowRoot 还是从attachShadow 创建的。生成的根不同:在使用createShadowRoot 创建的根中,<content> 元素用于分配元素。在使用attachShadow 创建的根中,<content> 元素不做任何事情,而是使用<slot> 元素。我们如何检测使用哪种方法来创建根(即我们是有 v0 根还是 v1 根)?

【问题讨论】:

  • 您应该将此信息添加到问题中。 “我们想知道根是从 createShadowRoot 还是从 attachShadow 创建的。生成的根是不同的。在使用 createShadowRoot 创建的根中, 元素用于分发元素。在使用 attachShadow 创建的根中, 元素什么都不做,必须使用 元素。我们如何检测使用哪种方法创建根? - 它更好地阐明了你真正想要什么,这样人们就不会做出错误的假设并为他们投反对票。
  • @nixkuroi 很抱歉投了反对票。我会补充一点。谢谢!!
  • 通常人们会使用特征检测而不是版本检测,因为它应该更有帮助......
  • @MikeMcCaughan 很容易检测特征(例如,如果attachShadow 方法存在),但我需要检测根是否是用一种方法或另一种方法(产生的根有差异) .这不是特征检测,它是检测一种类型的实例与另一种类型的实例。假设 v0 和 v1 API 都存在(最新的 Chrome 就是这种情况)。
  • 为什么投反对票?我已经更新了这个问题,也许现在它有足够的信息?

标签: javascript html dom web-component shadow-dom


【解决方案1】:

我的方向与 Hayato Ito 的回答类似。但是,我没有创建slot 元素,而是以content 元素为目标。我无法找到一种方法来检测任何 API 方法检测的版本。

我将content 元素作为目标,因为内容元素本身并没有针对它们的事件,这与slot 事件中的slotchange 不同,这有望带来小的性能提升。另外,如果浏览器根本不支持v0,函数返回v1 的速度会更快。

function shadowType(shadowRoot) {
    if (!shadowRoot) {
        // closed shadow dom does not appear to have a shadowRoot...
        // It could be assumed that it is v1, but for now return undefined
        return;
    }

    const content = document.createElement('content');
    // In browsers that support v1, but not v0 (ex: Safari)
    if (!content.getDistributedNodes) {
        return 'v1';
    }

    content.setAttribute('select', 'test-shadow-dom-version');
    shadowRoot.appendChild(content);

    const testElement = document.createElement('test-shadow-dom-version');
    shadowRoot.host.appendChild(testElement);
    const type = (content.getDistributedNodes().length) ? 'v0' : 'v1';
    shadowRoot.removeChild(content);
    shadowRoot.host.removeChild(testElement);

    return type;
}

这绝对感觉像是一个“黑客”,因为需要附加随机 dom :(。我确实在 Chrome、Firefox、Safari、IE11 和 Edge 中进行了测试。我测试了使用 webcomponentsjs (v0) polyfill 制作的组件,并且它为每个组件正确返回了v0。我还使用 shadydom (v1) polyfill 以及使用 v1 规范创建的组件测试了相同的浏览器,并在所有这些浏览器中收到了v1

【讨论】:

  • 这太棒了!感谢 KevBot!
【解决方案2】:

以下 hack 应该可以工作:

function isV1(shadowRoot) {
  const slot = document.createElement('slot');
  shadowRoot.appendChild(slot);
  slot.appendChild(document.createElement('div'));
  const assignedNodes = slot.assignedNodes({ flatten: true });
  slot.remove();
  return assignedNodes.length !== 0;
}

IMO,当你必须检测它时有问题。

【讨论】:

  • 谢谢隼人!这样做的一个缺点是创建元素和触发分发以获得结果的运行时成本。我同意如果 app code 这样做有问题,但我正在制作一个应该可以正常工作的库(用户可以使用 v0 或 v1 阴影)。我的库的自定义元素将渲染到 WebGL 而不是 HTML 引擎,所以如果我的库支持 ShadowDOM,那么(我认为)它需要检测平面树才能正确渲染。
  • A-Frame (aframe.io) 如果希望与 ShadowDOM 兼容(目前不兼容),也会面临同样的问题。例如,这里是 Hello World A-Frame 示例,没有 ShadowDOM:jsfiddle.net/trusktr/1ecfha5h,下面是显示使用 ShadowDOM 的一种方法的相同示例,但它无法工作:jsfiddle.net/trusktr/1ecfha5h/1
  • 这种方法是否会导致任何强制布局、渲染或任何其他不良副作用?
  • 你说“IMO,当你必须检测它时有问题。”,但这是因为我希望我的库在任何一种环境中都可以工作,而不是我在混合 ShadowDOM 的版本在我的应用中。
  • 这个方法应该不会导致任何强制布局,至少在Blink中是这样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-09
  • 2011-01-15
  • 2021-10-08
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
相关资源
最近更新 更多