将 Splidejs 集成到 Grapesjs 中:失败的 HTMLElement 实例

Integrate Splidejs into Grapesjs: failing instanceof HTMLElement

我正在尝试集成 Splidejs into a Grapesjs 编辑器。 安装幻灯片时,我得到 Uncaught Error: [splide] A track/list element is missing.

经过调试,我发现Splide 没有找到需要正确挂载的曲目或列表HTML 元素。但是,它们存在于 Grapes 组件的 HTML 中。

...
<div class="splide__track">
<ul class="splide__list">
...

Splide找不到它们的原因似乎与HTML元素基类型不同有关,导致我的元素无法识别。

下面的测试(Splide 3.6.9)returns false

Chrome 开发工具控制台 中进行调查时,instanceof 评估的 __proto__ 链乍一看似乎是正确的。然而,仔细观察会发现 subject 具有额外的 __zone_symbol__onxxx 属性。

> subject.__proto__.__proto__
    HTMLElement {…}
        ...
        __zone_symbol__ononabortpatched: true
        __zone_symbol__ononanimationendpatched: true
        __zone_symbol__ononanimationiterationpatched: true
        ...
> HTMLElement.prototype
    HTMLElement {…}
        ...
        none of the __zone_symbol __onxxx present
        ...
> subject.__proto__.__proto__ == HTMLElement.prototype
    false

这可以用这两个参考文献来解释:

(1) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_context_e.g._frames_or_windows

instanceof and multiple context (e.g. frames or windows)

Different scopes have different execution environments. This means that they have different built-ins (different global object, different constructors, etc.). This may result in unexpected results. For instance, [] instanceof window.frames[0].Array will return false,

(2) https://grapesjs.com/docs/modules/Components-js.html#important-caveat

Keep in mind that all component scripts are executed inside the iframe of the canvas (isolated, just like your final template), and therefore are NOT part of the current document.

我怀疑我的 Splide 内容被 zone.js 增强了。 因此,我开始在 Angular 区域

之外创建 Grapes 组件
this.zone.runOutsideAngular(() => {
      this.initGrapesEditor()
    })

但错误仍然存​​在。

关于如何解决这个问题,你有什么提示吗?

我也在 grapesjs 上报告了这个问题github https://github.com/artf/grapesjs/discussions/4062

并添加了两个解决方法

  1. 按如下方式修补 splidejs(未提交 PR)。 这允许 splidejs 被我的新组件类型的脚本正确加载。

/**
 * Tests to see if the given TypeName appears anywhere
 * in the prototype chain of the given subject.
 * This is a lose version of the instanceof operator
 * (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof)
 * required when checking the type of elements that cross window and iframe bouderies.
 * 
 * @param subject 
 * @param typeName 
 * @returns `true` if 
 */
function isInstanceOf(subject: any, typeName: string) {
  if (subject === null) {
    return false;
  }
  let p = subject.__proto__;
  while (p !== null) {
    if (p.constructor.name === typeName) {
      return true;
    }
    p = p.__proto__;
  }
  return false;
}

export function isHTMLElement( subject: unknown ): subject is HTMLElement {
  return isInstanceOf( subject, 'HTMLElement' );
}

export function isHTMLButtonElement( subject: unknown ): subject is HTMLButtonElement {
  return isInstanceOf( subject, 'HTMLButtonElement' );
}
  1. 替换Splide with Swiper。 Swiper开箱即用不依赖instanceof HTMLElement,简化了我的开发