You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

继承自定义JS类时出现ReferenceError:超类未定义的原因与解决

核心问题:QuickaddDrawer类未被识别的原因及修复方案

你遇到的Uncaught ReferenceError: QuickaddDrawer is not defined错误,根源是类的作用域限制:QuickaddDrawer是定义在if(!customElements.get("quickadd-drawer"))这个块级作用域内的类,外部的CardBubbles无法访问到局部作用域里的类定义。

你的代码片段

if(!customElements.get("quickadd-drawer")) {
  customElements.define("quickadd-drawer", class QuickaddDrawer extends HTMLElement {
    constructor() {
      super();
      this.sidecart = document.querySelector("side-cart");
    }

    connectedCallback() {
      this.atcBtn = this.querySelector(".atc-btn");
      this.atcBtn.addEventListener("click", this.toggleDrawer.bind(this));

      this.quickaddBtns = this.querySelectorAll(".quickadd-btn:not([disabled])");
      this.quickaddBtns.forEach(btn => btn.addEventListener("click", this.quickAddItem.bind(this)));
    }

    toggleDrawer() {
      this.atcBtn.classList.toggle("drawer--open");
    }

    quickAddItem(event) {
      const btn = event.target;
      const variantId = btn.dataset.quickAdd;
      console.log("button", btn, variantId);
      this.sidecart.addToCart(variantId);
    }
  });
}

class CardBubbles extends QuickaddDrawer {
  constructor() {
    super();
    this.productCard = this.closest('product-card');
    this.bubbles = this.querySelectorAll('li>a');
    this.quickaddListings = this.productCard.querySelector('.quickadd-btn__listing');
    this.slideAnchors = this.productCard.querySelectorAll('.product-card__wrapper .swiper-slide a');
    this.initBubbles();
  }

  initBubbles() {
    this.bubbles.forEach(item=> item.addEventListener('click', this.handleClick.bind(this)));
  }

  handleClick(event) {
    event.preventDefault();
    this.updateImageLink(event);
    // update available size buttons
    this.checkForData(event);
    // update add to cart button
    // update featured images
    // update circled swatch
  }

  updateImageLink(event) {
    const variantLink = new URL(event.target.href);
    this.slideAnchors.forEach(el=>{
      el.href = el.href.replace(/\/products\/(.*)/, variantLink.pathname);
    });
  }

  checkForData(event) {
    // update with available sizes
    let targetVariantJSON = document.querySelector(`product-card script[data-product-id="${event.target.dataset.productId}"]`);
    if (targetVariantJSON) {
      const data = JSON.parse(targetVariantJSON.textContent);
      this.updateQuickaddBtns(data);
      this.updateImages(data);
    } else {
      console.log('fetching JSON');
      fetch(`${event.target.href}.js`)
      .then( res => res.json())
      .then( data => this.updateQuickaddBtns(data));
    }
    // bind to quickadd functions
  }

  updateQuickaddBtns(data) {
    console.log(data);
    console.log(data.available);
    let availableSizes=[];
    this.quickaddListings.innerHTML = '';
    data.variants.forEach(item=>{
      console.log(`Variant: ${item.id}, size ${item.option1} is available: ${item.available}`);
      const newButton = document.createElement('a');
      newButton.classList.add('quickadd-btn');
      newButton.dataset.quickAdd = item.id;
      newButton.innerText = item.option1;
      if (item.available == false) {
        console.log('is false, will attempt to add disabled attribute now');
        newButton.setAttribute("disabled", "");
      }
      this.quickaddListings.append(newButton);
    });
    console.log(availableSizes);
  }
  updateImages(data) {
    //update images with data
  }
}

customElements.define('card-bubbles', CardBubbles);

修复方案

以下三种方式均可解决问题,可根据项目实际情况选择:

方案1:将QuickaddDrawer提升到全局作用域

把类定义移出if块,先声明全局类再注册自定义元素,确保外部子类能访问到:

// 全局声明QuickaddDrawer类
class QuickaddDrawer extends HTMLElement {
  constructor() {
    super();
    this.sidecart = document.querySelector("side-cart");
  }

  connectedCallback() {
    this.atcBtn = this.querySelector(".atc-btn");
    this.atcBtn.addEventListener("click", this.toggleDrawer.bind(this));

    this.quickaddBtns = this.querySelectorAll(".quickadd-btn:not([disabled])");
    this.quickaddBtns.forEach(btn => btn.addEventListener("click", this.quickAddItem.bind(this)));
  }

  toggleDrawer() {
    this.atcBtn.classList.toggle("drawer--open");
  }

  quickAddItem(event) {
    const btn = event.target;
    const variantId = btn.dataset.quickAdd;
    console.log("button", btn, variantId);
    this.sidecart.addToCart(variantId);
  }
}

// 仅当元素未注册时执行注册
if(!customElements.get("quickadd-drawer")) {
  customElements.define("quickadd-drawer", QuickaddDrawer);
}

// 现在可正常继承
class CardBubbles extends QuickaddDrawer {
  // ... 你的子类实现
}

customElements.define('card-bubbles', CardBubbles);

方案2:通过customElements.get获取已注册的类

自定义元素注册后,可通过customElements.get("标签名")获取对应的类,直接用于继承:

if(!customElements.get("quickadd-drawer")) {
  customElements.define("quickadd-drawer", class QuickaddDrawer extends HTMLElement {
    // ... 类实现
  });
}

// 从自定义元素注册表中获取类
const QuickaddDrawer = customElements.get("quickadd-drawer");
class CardBubbles extends QuickaddDrawer {
  // ... 你的子类实现
}

customElements.define('card-bubbles', CardBubbles);

方案3:将CardBubbles移到if块内部(若无需单独暴露子类)

如果CardBubbles仅需依赖QuickaddDrawer存在时才定义,可将子类定义放入if块内:

if(!customElements.get("quickadd-drawer")) {
  class QuickaddDrawer extends HTMLElement {
    // ... 类实现
  }
  customElements.define("quickadd-drawer", QuickaddDrawer);

  // 在块内定义子类
  class CardBubbles extends QuickaddDrawer {
    // ... 你的子类实现
  }
  customElements.define('card-bubbles', CardBubbles);
}

附加问题:QuickaddDrawer包裹在条件语句中的原因

这个条件判断if(!customElements.get("quickadd-drawer"))的核心作用是避免重复注册自定义元素报错。

根据Web Components规范,重复调用customElements.define()注册同一标签名的元素会抛出DOMException。该判断逻辑确保只有当quickadd-drawer未被注册时,才执行定义操作,防止脚本重复加载(比如页面多次引入同一段代码)时出现错误。

其他类未做此处理,可能是因为这些元素的注册逻辑已被确保只会执行一次,或者不会出现重复加载的场景。


内容的提问来源于stack exchange,提问作者Kirkwood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 16:17:54