JS自定义实现同页面多位置渲染Shopify购买按钮的问题排查
原有方案的错误点
第一种方案的两处核心错误
- HTML的id属性书写错误:
#是CSS、JS选择器中用于匹配id的语法前缀,不属于id值本身。你将容器id设置为#buybutton后,document.querySelectorAll('#buybutton')实际匹配的是id为buybutton的元素,完全无法定位到你写的挂载容器。 - 参数类型不符合SDK要求:Shopify Buy SDK的
ui.createComponent方法的node参数仅支持传入单个DOM节点,querySelectorAll返回的是NodeList节点集合,SDK无法识别该类型参数,自然不会执行渲染。
第二种方案的两处核心错误
- DOM API方法名书写错误:原生DOM不存在
document.getElementByClass这个方法,通过类名获取元素的正确方法是document.getElementsByClassName,注意方法名中Elements为复数、后缀为ClassName。 - 同样存在参数类型错误:即使写对方法名,
getElementsByClassName返回的也是HTMLCollection节点集合,不符合node参数要求的单个节点类型,因此渲染失败。
可行实现方案
同页面多位置挂载同一商品的购买按钮,只需要遍历所有挂载容器,逐个调用组件渲染方法即可,注意配置全局仅生成一次购物车、侧边栏toggle组件,避免页面出现重复的全局功能模块。所有挂载的按钮会自动共享同一个购物车状态,任意按钮的加购操作都会同步到全局购物车,不会出现数据不同步问题。
步骤1:修改HTML挂载容器
放弃重复id的写法,给两个按钮容器添加统一类名作为挂载标识即可:
<!-- PC端表格内的挂载点 --> <div class="buybutton-wrapper"></div> <!-- 移动端表格内的挂载点 --> <div class="buybutton-wrapper"></div>
步骤2:替换原有初始化JS代码
将原代码中单次调用createComponent的逻辑改为遍历所有挂载点渲染,第一处挂载点保留完整的购物车、toggle配置,其余挂载点仅渲染产品按钮模块,修改后的完整初始化逻辑如下:
/*<![CDATA[*/ (function () { var scriptURL = 'https://sdks.shopifycdn.com/buy-button/latest/buy-button-storefront.min.js'; if (window.ShopifyBuy) { if (window.ShopifyBuy.UI) { ShopifyBuyInit(); } else { loadScript(); } } else { loadScript(); } function loadScript() { var script = document.createElement('script'); script.async = true; script.src = scriptURL; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(script); script.onload = ShopifyBuyInit; } function ShopifyBuyInit() { var client = ShopifyBuy.buildClient({ domain: 'sugatsune-uk.myshopify.com', storefrontAccessToken: '000', }); ShopifyBuy.UI.onReady(client).then(function (ui) { const containers = document.querySelectorAll('.buybutton-wrapper'); // 抽离通用产品配置复用 const baseConfig = { id: '7369316827224', moneyFormat: '%C2%A3%7B%7Bamount%7D%7D', options: { "product": { "styles": { "product": { "@media (min-width: 601px)": { "max-width": "calc(25% - 20px)", "margin-left": "20px", "margin-bottom": "50px" } }, "button": { "font-weight": "bold", "font-size": "12px", "padding-top": "6px", "padding-bottom": "6px", ":hover": { "background-color": "#2d2d2d" }, "background-color": "#000000", ":focus": { "background-color": "#2d2d2d" }, "padding-left": "6px", "padding-right": "6px" }, "quantityInput": { "font-size": "12px", "max-width": "20px", "padding-top": "6px", "padding-bottom": "6px" } }, "contents": { "img": false, "button": false, "buttonWithQuantity": true, "title": false, "price": false }, "text": { "button": "Add to cart" } }, "modalProduct": { "contents": { "img": false, "imgWithCarousel": true, "button": false, "buttonWithQuantity": true }, "styles": { "product": { "@media (min-width: 601px)": { "max-width": "100%", "margin-left": "0px", "margin-bottom": "0px" } }, "button": { "font-weight": "bold", "font-size": "12px", "padding-top": "6px", "padding-bottom": "6px", ":hover": { "background-color": "#2d2d2d" }, "background-color": "#000000", ":focus": { "background-color": "#2d2d2d" }, "padding-left": "6px", "padding-right": "6px" }, "quantityInput": { "font-size": "12px", "max-width": "20px", "padding-top": "5px", "padding-bottom": "5px" } }, "text": { "button": "Add to cart" } }, "option": {} } }; containers.forEach((node, index) => { // 深拷贝基础配置,避免多实例配置互相污染 const instanceConfig = JSON.parse(JSON.stringify(baseConfig)); instanceConfig.node = node; // 第一个实例加载完整全局组件,其余实例不重复生成购物车、toggle if (index === 0) { instanceConfig.options.productSet = { "styles": { "products": { "@media (min-width: 601px)": { "margin-left": "-20px" } } } }; instanceConfig.options.cart = { "styles": { "button": { "font-weight": "bold", "font-size": "14px", "padding-top": "15px", "padding-bottom": "15px", ":hover": { "background-color": "#2d2d2d" }, "background-color": "#000000", ":focus": { "background-color": "#2d2d2d" } } }, "text": { "total": "Subtotal", "button": "Checkout" } }; instanceConfig.options.toggle = { "styles": { "toggle": { "font-weight": "bold", "background-color": "#000000", ":hover": { "background-color": "#2d2d2d" }, ":focus": { "background-color": "#2d2d2d" } }, "count": { "font-size": "14px" } } }; } else { instanceConfig.options.productSet = { embed: false }; instanceConfig.options.cart = { embed: false }; instanceConfig.options.toggle = { embed: false }; } ui.createComponent('product', instanceConfig); }) }); } })(); /*]]>*/
代码中用
JSON.parse(JSON.stringify())做深拷贝是为了兼容所有浏览器,如果不需要兼容IE等老旧环境,可以替换为structuredClone()方法。
轻量替代方案(仅适配响应式场景)
如果两个按钮不会同时出现在用户视口中,仅需要根据设备类型分别在PC/移动端表格展示,也可以只初始化一次按钮,通过CSS媒体查询结合绝对定位、显隐控制,让同一个按钮在不同分辨率下显示在对应表格位置,不需要重复初始化组件,加载性能更好。
内容的提问来源于stack exchange,提问作者nietsnut
相关产品推荐
相关产品推荐

