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

基于Shadow DOM的Web组件多实例运行异常原因排查

Shadow DOM自定义组件交互异常:组件顺序为何影响运行?

之前研究Shadow DOM相关内容时,参考教程复现代码,单组件运行正常。但在表单中新增带弹窗的信用卡号输入框(作为第一个表单元素)后,出现两个问题:

  • 悬停信用卡号的info图标时弹窗不显示(已给IMG标签设置alt="Info")
  • 点击信用卡号输入框时,焦点跳转到CVC输入框;仅通过Tab键遍历或点击标签才能正常获取卡号输入框焦点,且Tab聚焦卡号info图标时弹窗能正常显示

做了个实验:新增第二个表单,调换两个输入框顺序(用水平线分隔),此时两个输入元素的弹窗都能显示,但点击第二个表单的CVC输入框仍会聚焦错误元素。

谁能解释组件顺序影响运行的原因?

更新

经解答后明确问题根源:第二个元素(CVC)的隐藏弹窗与第一个输入元素(信用卡号)重叠。我新增第三个表单,加大两个输入元素的垂直间距后问题消失,但更优方案是在弹窗样式中添加pointer-events: none;——即便元素间距足够,该属性也能避免点击隐藏弹窗区域时意外聚焦输入框,提升用户体验。

自定义组件JS代码

class PopupInfo extends HTMLElement {

  constructor() {

    super();

    // Create a shadow root
    const shadow = this.attachShadow({mode: 'open'});

    // Create spans
    const wrapper = document.createElement('span');
    wrapper.setAttribute('class', 'wrapper');

    const icon = document.createElement('span');
    icon.setAttribute('class', 'icon');
    icon.setAttribute('tabindex', 0);

    const info = document.createElement('span');
    info.setAttribute('class', 'info');

    // Take attribute content and put it inside the info span
    const text = this.getAttribute('data-text');
    info.textContent = text;

    // Insert icon
    let imgUrl;
    if(this.hasAttribute('img')) {
      imgUrl = this.getAttribute('img');
    } else {
      imgUrl = 'img/default.png';
    }

    const img = document.createElement('img');
    img.src = imgUrl;
    img.alt = 'Info';
    icon.appendChild(img);

    // Create some CSS to apply to the shadow dom
    const style = document.createElement('style');
    //console.log(style.isConnected);

    style.textContent = `
      .wrapper {
        position: relative;
      }

      .info {
        font-size: 0.8rem;
        width: 200px;
        display: inline-block;
        border: 1px solid black;
        padding: 10px;
        background: white;
        border-radius: 10px;
        opacity: 0;
        transition: 0.6s all;
        position: absolute;
        bottom: 20px;
        left: 10px;
        z-index: 3;
      }

      img {
        width: 1.2rem;
      }

      .icon:hover + .info, .icon:focus + .info {
        opacity: 1;
      }
    `;

    // Attach the created elements to the shadow dom
    shadow.appendChild(style);
    //console.log(style.isConnected);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}
customElements.define('popup-info', PopupInfo);

HTML代码

<!doctype html>
<html>
<head>
<title>Custom Component</title>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta charset="utf-8">
</head>
<body>
  <h1>Pop-up info widget - web components</h1>

  <form>
    <div>
      <label for="cc_number">Enter your credit card number <popup-info data-text="You may include spaces or hyphens."></popup-info></label>
      <input type="text" id="cc_number">
    </div>
    <div>
      <label for="cvc">Enter your CVC <popup-info data-text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."></popup-info></label>
      <input type="text" id="cvc">
    </div>
  </form>
  
  <hr style="margin: 20px 0px;">
  
  <form>
    <div>
      <label for="cvc2">Enter your CVC <popup-info data-text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."></popup-info></label>
      <input type="text" id="cvc2">
    </div>
    <div>
      <label for="cc_number2">Enter your credit card number <popup-info data-text="You may include spaces or hyphens."></popup-info></label>
      <input type="text" id="cc_number2">
    </div>
  
  <hr style="margin: 20px 0px;">

  <form>
    <div>
      <label for="cc_number3">Enter your credit card number <popup-info data-text="You may include spaces or hyphens."></popup-info></label>
      <input type="text" id="cc_number3">
    </div>
    <div style="margin-top: 100px;">
      <label for="cvc3">Enter your CVC <popup-info data-text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."></popup-info></label>
      <input type="text" id="cvc3">
    </div>
  </form>
</form>  
</body>
</html>

内容的提问来源于Stack Exchange,提问作者Booboo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 12:15:25