基于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
相关产品推荐
相关产品推荐

