代码之家  ›  专栏  ›  技术社区  ›  Booboo

当HTML包含两个组件实例时,为什么这个影子DOM实现的web组件会失败?

  •  0
  • Booboo  · 技术社区  · 3 年前

    在这个网站的其他地方,发布了一个关于使用Selenium访问shadowDOM的问题。对这个主题了解不多,我读了MDN的文章 Using shadow DOM 并在我的桌面上复制了代码,看起来工作得很好。真见鬼,我决定在表单中添加第二个元素,用于带有自己“弹出信息”的信用卡号。这是作为第一个表单元素添加的,因为人们通常先输入信用卡号,然后输入CVC号码。我发现,当你将鼠标悬停在信息图标上时,不仅不会显示任何信息(由于该网站上不存在实现此功能的PNG文件,我提供了 alt=“信息” IMG标签上的属性),但当您单击信用卡号输入元素时,CVC输入元素会高亮显示(即获得焦点)。然而,我可以通过选项卡浏览表单中的所有元素或单击其标签来关注信用卡号输入元素。此外,当信用卡元素的信息图标通过选项卡获得焦点时,将显示弹出信息。

    作为一个实验,我在HTML中添加了第二个表单,其中两个输入字段颠倒(为了清晰起见,两个表单在演示中用水平线分隔)。这可以显示两个输入元素的弹出信息,但点击第二个表单的CVC元素仍然会突出显示错误的元素。

    有人能解释为什么这个命令看起来很重要吗?

    使现代化

    在Keith提供了下面的答案之后,很明显,问题的出现是因为第二个元素(CVC)的不可见弹出信息与第一个输入元素(信用卡号)重叠。因此,我添加了第三种形式,在两个输入元素之间留出足够的垂直间隔,这表明问题消失了。但是Keith通过添加来修改弹出信息的样式表的解决方案 pointer-events: none; 即使你有足够的元素间距,这也是你想要的,因为如果没有这个添加,点击隐藏弹出信息的输入元素上方会导致焦点转移到输入元素上,这可能会让用户感到困惑(对我来说)。

    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);
    <!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>
    1 回复  |  直到 3 年前
        1
  •  3
  •   Keith    3 年前

    不透明度为0的元素仍将接收单击事件。这会导致焦点移动到此隐藏的信息元素。

    我想这里使用了不透明度,这样你就可以得到很好的过渡效果。

    这里的一个简单解决方案是使用 pointer-events: none

    .info {
       pointer-events: none;
       font-size: 0.8rem;
       ....
    

    例如。

    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 {
            pointer-events: none;
            font-size: 0.8rem;
            width: 200px;
            display: inline-block;
            border: 1px solid black;
            padding: 10px;
            background: white;
            border-radius: 10px;
            opacity: 0.1;
            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);
    <!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>
    </form>  
    </body>
    </html>