Polymer/Vaadin14集成Braintree支付网关#id选择器失效问题
问题描述
我正尝试在基于Polymer开发的Vaadin 14前端中集成Braintree支付网关,通过自定义Vaadin前端视图加载https://js.braintreegateway.com/web/dropin/1.9.4/js/dropin.min.js脚本后,调用dropin.create方法,代码如下:
import{PolymerElement}from'@polymer/polymer/polymer-element.js'; import'@polymer/iron-icon/iron-icon.js'; import{html}from'@polymer/polymer/lib/utils/html-tag.js'; import '@webcomponents/webcomponentsjs/webcomponents-loader.js'; import'@polymer/polymer/polymer-legacy.js'; import'@polymer/iron-flex-layout/iron-flex-layout.js'; import{mixinBehaviors}from'@polymer/polymer/lib/legacy/class.js'; import{Polymer}from'@polymer/polymer/lib/legacy/polymer-fn.js'; import{setTouchAction}from'@polymer/polymer/lib/utils/gestures.js'; import{afterNextRender}from'@polymer/polymer/lib/utils/render-status.js'; import'@vaadin/vaadin-text-field/vaadin-text-field.js'; import { sharedStyles } from './drop-in.js'; let dropin = require('braintree-web-drop-in'); class BrainTreeVaadin extends PolymerElement { <vaadin-vertical-layout class="main-div-layout-boder padding5"> <form id="paymentForm" method="post" action="/checkout" class="main-screen-vert-layout-row"> <div id="containPayment" class="main-screen-vert-layout-row"> <div id="btDropin" class="main-screen-vert-layout-row"></div> </div> <vaadin-text-field id="nonce" value={{valueNonce}} hidden></vaadin-text-field> <vaadin-button id="butPayment" theme="theme-button-02" class="button-row">Payment</vaadin-button> </form> </vaadin-vertical-layout> createFormPayment(){ let form = this.$.paymentForm; let butPayment = this.$.butPayment; let btDropin = this.$.btDropin; let textNonce = this.$.nonce; dropin.create({ authorization: this.clientToken, container: btDropin, card: { cardholderName: { required: true } }, paypal: { flow: 'vault', currency: 'USD' }, paypalCredit: { flow: 'vault', currency: 'USD' } } } }
运行后出现报错,经排查问题出在dropin.min.js内部加载的https://www.paypalobjects.com/api/checkout.min.js使用#id格式的JS选择器无法获取到对应HTML元素。
Braintree标准实现中,braintree.dropin.create方法的container参数支持传入#element_id格式的选择器,官方示例代码如下:
<div class="bt-drop-in-wrapper"> <div id="bt-dropin"></div> </div> <div th:include="fragments/homefooter :: footer"></div> <script src="https://js.braintreegateway.com/web/dropin/1.9.4/js/dropin.min.js"></script> <script th:inline="javascript"> /*<![CDATA[*/ var form = document.querySelector('#payment-form'); var client_token = [[${clientToken}]]; braintree.dropin.create({ authorization: client_token, container: '#bt-dropin', paypal: { flow: 'vault' } }, function (createErr, instance) { form.addEventListener('submit', function (event) { event.preventDefault(); $('#errorDiv').hide(); $('#serverSideErrorDiv').hide(); instance.requestPaymentMethod(function (err, payload) { if (err) { console.log('Error', err); showError(err); return; } // Add the nonce to the form and submit document.querySelector('#nonce').value = payload.nonce; form.submit(); }); }); });
用户最初疑问为如何让Vaadin视图支持#element_id格式的JS元素选择器,后续两次更新排查确认:
- 该问题不属于Vaadin Flow范畴,属于Polymer相关问题
- 最终定位为Braintree侧兼容问题,与Polymer无关
解决方案
问题本质是旧版Braintree/PayPal SDK仅支持在全局document DOM树中查询元素,而Polymer自定义元素默认使用Shadow DOM隔离内部DOM,全局document.querySelector无法穿透Shadow DOM获取内部元素,导致选择器失效。可通过以下任意方案解决:
- 关闭自定义元素的Shadow DOM
在Polymer元素类中添加createRenderRoot方法,关闭Shadow DOM,让元素内部DOM直接挂载到全局DOM树,全局ID选择器即可正常访问内部元素:
class BrainTreeVaadin extends PolymerElement { // 关闭Shadow DOM createRenderRoot() { return this; } // 其余原有代码保持不变 }
- 升级Braintree Drop-in SDK版本
1.38.0及以上版本的Braintree Drop-in SDK已优化元素查询逻辑,兼容Shadow DOM场景,升级SDK版本后无需修改现有业务代码即可正常运行。 - 将Braintree渲染容器移至全局DOM
若无法升级SDK也不想关闭Shadow DOM,可在全局DOM中创建独立的容器元素,将Braintree组件渲染到该全局容器中,再通过绝对定位或者slot的方式将容器嵌入到Vaadin视图的对应位置即可。
内容的提问来源于stack exchange,提问作者Thang Le
相关产品推荐
相关产品推荐

