For any inquiries,feel free to reach out to us at--Email us: info@blainewilson.com

For any inquiries,feel free to reach out to us at--Email us: info@blainewilson.com

(function(){ let w = window.innerWidth; function setHeaderCssVar() { const headerEle = document.getElementById('shoplaza-section-header'); if(!headerEle){ return }; document.body.style.setProperty('--window-height', `${window.innerHeight}px`); document.body.style.setProperty('--header-height', `${headerEle.clientHeight}px`); const mdScorllHideEle = headerEle.querySelector('.header__mobile .header__scroll_hide'); if (mdScorllHideEle) { document.body.style.setProperty('--header-scroll-hide-height-md', `${mdScorllHideEle.clientHeight}px`); } const pcScorllHideEle = headerEle.querySelector('.header__desktop .header__scroll_hide'); if (pcScorllHideEle) { document.body.style.setProperty('--header-scroll-hide-height-pc', `${pcScorllHideEle.clientHeight}px`); } } function handlResize() { if(w == window.innerWidth){return}; w = window.innerWidth; setHeaderCssVar(); }; function init(){ setHeaderCssVar(); window.removeEventListener('resize', window._theme_header_listener) window._theme_header_listener = handlResize; window.addEventListener('resize', window._theme_header_listener); } init(); })();

Reindeer Letter Print Ragaln-sleeve Red Plaid Pajamas Sets

$33.99
Style:  Women
Size:  S
people are viewing this right now
Free worldwide shipping
Free returns
Sustainably made
Secure payments
Quantity

Description

WHY DO I NEED FAMILY MATCHING PAJAMAS SETS

👪You may think you don’t want Christmas pajamas for the whole family. But then you see your friends on Instagram posing for their holiday photos with the glimmering lights of the tree in the background and everyone from dad to the dog sporting matching holiday PJs, you’ll likely change your mind.

👨‍👩‍👧‍👦And when you do, we’re here to help with a look at this holiday season’s comfiest and cutest matching family pajama sets.We hope you enjoy these pajamas from our unique collection and Merry Christmas !!! 

Features:

🎅Styles Suitable For The Whole Family: Styles for the Whole Family Comes in sizes for adults, boys, girls, toddlers,even provide sizes suitable for your pets,create a perfect match shape during the holiday, create family memories full of laughter, and start matching the pajamas tradition for the whole family!

🎉Perfect Family Festive Holiday Celebration: These pajamas do this perfectly. They bring the joy and an unique feeling of christmas to your family while at the same time you keep alive the tradition of family matching pajamas.“From home to home, and heart to heart, from one place to another. The warmth and joy of Christmas, brings us closer to each other.” Perfect comfy PJs for Christmas morning pictures, lounging on movie night, gift-giving traditions, and breakfast with Santa. Whether it's for family photo, Christmas morning, or all winter long, these family two-piece pajamas are a must have for a family festive holiday celebration. Take the perfect family photo card and send your Christmas and holiday greetings in style!

🔔High-Quality Pajamas: They are made from a soft and long-lasting material. Excellent elasticity for perfect fit, skin-friendly, breathable, comfortable and soft wearing, easy-to-clean and stain resistant.

🎄Festive Multi-element Design: These family matching christmas pjs With a cute and colorful graphic on each shirt, Christmas tree, snowflake, snowman, santa claus, deer, print "Merry Christmas". These pjs are sure to be hit this Christmas season! Grab the family and enjoy making memories in these classic family pajamas!

🎁Best Pajama Presents Gift: The family matching pjs is the best gift for new years festival holiday,thanksgiving day,Xmas eve ,Christmas morning.Bring the christmas Jammies out during family game night, get matching sets as a couple to make your time together extra cute.

Specification:
  • Please add each size separately to your shopping cart.
  • 100% satisfaction guaranteed.
  • 60% Cotton, 40% Polyester
  • Machine wash, tumble dry
  • Imported
  • Get in on the biggest trend of the holidays!
  • Hand drawn prints made lovingly by our in-house designers
      Note:
      • Due to manual measurement, please allow ±2-3cm error. Please compare the measurements with yours before ordering.
      • Size chart is for reference only, there may be a little difference with you get.
      • All our pet clothes are only suitable for pets of small size.
      • Up to Now, Over 70K Customers Like You Already Have Purchased Xmas Items Here, Thanks For Your Trust&Time.

      How To Measure Your Body? 

      • Chest: Lift your arms and measure around just under your underarms around your chest.
      • Shoulder: Relaxed shoulders, measure across the widest part of your shoulders.
      • Waist: Measure around the narrowest part of your waist, to ensure a comfortable fit, keep one finger between the measuring tape and your body.
      • Hip: With your feet approximately 6" apart, measure around the widest part of your hip.
      • Length: Choose a clothing item with the preferred length and measure from the top to the bottom 
      Size Chart for Men's Pajamas Set(CM)
      Size Bust Sleeve TOP LENGTH WAIST PANTS LENGTH
      S 106 61 70.1 68.1 - 104 103
      M 112 62 72.1 71.9 - 110 105
      L 118 63 73.9 75.9 - 116 107
      XL 124 64 75.9 79.8 - 122 109
      2XL 130 65 78 84.1 - 128 110
      3XL 136 66 80 87.9 - 134 113
      4XL 142 67.1 81.8 91.9 - 140 115
      Size Chart for Women's Pajamas Set(CM)
      Size Bust Sleeve TOP LENGTH WAIST PANTS LENGTH
      S 97 56.4 64 64 - 101 100
      M 102 57.4 66 68.1 - 106 102
      L 107 58.4 68.1 71.9 - 111 104
      XL 112 59.4 70.1 75.9 - 116 106
      2XL 117 60.5 72.1 80 - 126 108
      3XL 122 61.5 73.9 84.1 - 126 110
      4XL 127 62.5 76.2 87.9 - 131 112
      Size Chart for Kid's Pajamas Set(CM)
      Size Bust Sleeve TOP LENGTH WAIST PANTS LENGTH
      2-3 Years 63 36.1 38.9 41.9 - 59.9 51.1
      3-4 Years 65.5 39.1 40.9 43.9 - 64 55.1
      4-5 Years 68.1 41.1 42.9 46 - 68.1 58.9
      5-6 Years 70.6 43.9 45 48 - 71.9 63
      6-7 Years 72.9 47 47 50 - 75.9 67.1
      7-8 Years 75.4 49 49 53.1 - 81 71.1
      8-9 Years 78 51.1 51.1 55.9 - 86.1 74.9
      9-10 Years 80.5 53.1 53.1 58.9 - 90.9 79
      10-11 Years 83.1 55.1 55.1 62 - 96 83.1
      11-12 Years 85.6 56.9 56.9 65 - 101 87.1
      12-13 Years 88.1 58.9 58.9 68.1 - 106 91.2
      13-14 Years 90.7 61 61 71.1 - 111 95.3
      Size Chart for Baby's Pajamas Set(CM)
      SIZE BUST SLEEVE TOP LENGTH
      1-3 Months 51.1 29 52.1
      3-6 Months 53.1 29.5 56.1
      6-9 Months 55.1 30 59.9
      9-12 Months 56.9 30.5 64
      12-18 Months 58.9 31 68.1
      18-24 Months 61 31.5 71.9

      Sizing & Fit Guide 

      This guide provides general sizing information only, and fits may vary by style or personal preference. All dimensions are produced/measured manually with deviation (ranged) at 2-3cm. Hope for your friendly understanding.

      Please be aware that most of our clothing is Asian sizing, which runs smaller than regular US / European sizing. Refer to the measurements chart for each product to determine the best size for you. 

      If you have questions regarding sizing or any other information on a specific item, please feel free email us at info@blainewilson.com for specific details. 



              const TAG = "spz-custom-product-automatic"; class SpzCustomProductAutomatic extends SPZ.BaseElement { constructor(element) { super(element); this.variant_id = '83cf39dc-6b13-43e5-b42a-cb1caf61c6e1'; this.isRTL = SPZ.win.document.dir === 'rtl'; } static deferredMount() { return false; } buildCallback() { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.setupAction_(); this.viewport_ = this.getViewport(); } mountCallback() { this.init(); // 监听事件 this.bindEvent_(); } async init() { this.handleFitTheme(); const data = await this.getDiscountList(); this.renderApiData_(data); } async getDiscountList() { const productId = '32195efc-e974-4a23-accf-9ca1fc5bf80d'; const variantId = this.variant_id; const productType = 'default'; const reqBody = { product_id: productId, variant_id: variantId, discount_method: "DM_AUTOMATIC", customer: { customer_id: window.C_SETTINGS.customer.customer_id, email: window.C_SETTINGS.customer.customer_email }, product_type: productType } const url = `/api/storefront/promotion/display_setting/text/list`; const data = await this.xhr_.fetchJson(url, { method: "post", body: reqBody }).then(res => { return res; }).catch(err => { this.setContainerDisabled(false); }) return data; } async renderDiscountList() { this.setContainerDisabled(true); const data = await this.getDiscountList(); this.setContainerDisabled(false); // 重新渲染 抖动问题处理 this.renderApiData_(data); } clearDom() { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); } async renderApiData_(data) { const parentDiv = document.querySelector('.automatic_discount_container'); const newTplDom = await this.getRenderTemplate(data); if (parentDiv) { parentDiv.innerHTML = ''; parentDiv.appendChild(newTplDom); } else { console.log('automatic_discount_container is null'); } } doRender_(data) { const renderData = data || {}; return this.templates_ .findAndRenderTemplate(this.element, renderData) .then((el) => { this.clearDom(); this.element.appendChild(el); }); } async getRenderTemplate(data) { const renderData = data || {}; return this.templates_ .findAndRenderTemplate(this.element, { ...renderData, isRTL: this.isRTL }) .then((el) => { this.clearDom(); return el; }); } setContainerDisabled(isDisable) { const automaticDiscountEl = document.querySelector('.automatic_discount_container_outer'); if(isDisable) { automaticDiscountEl.setAttribute('disabled', ''); } else { automaticDiscountEl.removeAttribute('disabled'); } } // 绑定事件 bindEvent_() { window.addEventListener('click', (e) => { let containerNodes = document.querySelectorAll(".automatic-container .panel"); let bool; Array.from(containerNodes).forEach((node) => { if(node.contains(e.target)){ bool = true; } }) // 是否popover面板点击范围 if (bool) { return; } if(e.target.classList.contains('drowdown-icon') || e.target.parentNode.classList.contains('drowdown-icon')){ return; } const nodes = document.querySelectorAll('.automatic-container'); Array.from(nodes).forEach((node) => { node.classList.remove('open-dropdown'); }) // 兼容主题 this.toggleProductSticky(true); }) // 监听变体变化 document.addEventListener('dj.variantChange', async(event) => { // 重新渲染 const variant = event.detail.selected; if (variant.product_id == '32195efc-e974-4a23-accf-9ca1fc5bf80d' && variant.id != this.variant_id) { this.variant_id = variant.id; this.renderDiscountList(); } }); } // 兼容主题 handleFitTheme() { // top 属性影响抖动 let productInfoEl = null; if (window.SHOPLAZZA.theme.merchant_theme_name === 'Wind' || window.SHOPLAZZA.theme.merchant_theme_name === 'Flash') { productInfoEl = document.querySelector('.product-info-body .product-sticky-container'); } else if (window.SHOPLAZZA.theme.merchant_theme_name === 'Hero') { productInfoEl = document.querySelector('.product__info-wrapper .properties-content'); } if(productInfoEl){ productInfoEl.classList.add('force-top-auto'); } } // 兼容 wind/flash /hero 主题 (sticky属性影响 popover 层级展示, 会被其他元素覆盖) toggleProductSticky(isSticky) { let productInfoEl = null; if (window.SHOPLAZZA.theme.merchant_theme_name === 'Wind' || window.SHOPLAZZA.theme.merchant_theme_name === 'Flash') { productInfoEl = document.querySelector('.product-info-body .product-sticky-container'); } else if (window.SHOPLAZZA.theme.merchant_theme_name === 'Hero') { productInfoEl = document.querySelector('.product__info-wrapper .properties-content'); } if(productInfoEl){ if(isSticky) { // 还原该主题原有的sticky属性值 productInfoEl.classList.remove('force-position-static'); return; } productInfoEl.classList.toggle('force-position-static'); } } setupAction_() { this.registerAction('handleDropdown', (invocation) => { const discount_id = invocation.args.discount_id; const nodes = document.querySelectorAll('.automatic-container'); Array.from(nodes).forEach((node) => { if(node.getAttribute('id') != `automatic-${discount_id}`) { node.classList.remove('open-dropdown'); } }) const $discount_item = document.querySelector(`#automatic-${discount_id}`); $discount_item && $discount_item.classList.toggle('open-dropdown'); // 兼容主题 this.toggleProductSticky(); }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SpzCustomProductAutomatic);
              const TAG = 'spz-custom-add-to-cart'; class SpzCustomAddToCart extends SPZ.BaseElement { constructor(element) { super(element); this.plugin_timestamp = Date.now(); this.defaultColorConfig = { module_bg: "#FFFFFF", button_bg: "#E84926", button_color: "#FFFFFF", text_color: "#202020", price_color: "#E84926", border_color: "#E6E6E6", border_bg: "#FFFFFF", round_size: '4' }; this.config = this.defaultColorConfig; this.originStickyTop = 0; this.qty = 1; this.variantId = this.element.getAttribute('variant-id'); this.trackMap = { qty: this.trackQty.bind(this), variant: this.trackVariant.bind(this), addToCart: this.trackAddtoCart.bind(this), atcView: this.trackAtcView.bind(this), } this.isHasView = false; this.isFirstUpdateVariant=false; } static deferredMount() { return false; } buildCallback() { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.locale_ = SPZServices.localeFor(this.win); this.hasGlobalAtcListener = !!document.querySelector('#add-cart-event-proxy'); this.setupAction_(); } mountCallback() { // 初始化 this.init(); } async init() { // 如果不是详情页,不需要执行后面js if (window.SHOP_PARAMS.template_type !== '1') return; await this.setLocale(); this.handleIsRender(); this.bingEvents(); } bingEvents() { // 设备切换 重新渲染 window.addEventListener('resize', SPZCore.Types.debounce( this.win, (invocation) => { // 关闭弹窗, 解决切换屏幕尺寸不能滚动的问题 this.triggerEvent_('closeShopModal'); this.config = { ...this.config, isMobile: window.innerWidth < 768 ? true : false, position: window.innerWidth < 768 ? 'down' : this.config.display_position }; this.renderAddToCart(); }, 200 )) } // 获取多语言 async setLocale() { let data; try { //多语言 data = await this.locale_.i18n(['product', 'products']); } catch (error) { console.error(error); } this.i18n = { 'sold_out': 'Sold out', 'add_to_cart': 'Add to cart', 'unavailable': 'Unavailable', 'product_unavailable': 'Product is unavailable.', ...data?.product?.product_detail, ...data?.products?.product, } } getProductData() { let pJson = document.querySelector('#product-json'); if (pJson) { return JSON.parse(pJson.innerHTML)?.product; } else if (typeof $ === 'function') { return $(document).data('djproduct')?.product; } return undefined; } getAddCartBtn() { return document.querySelector('[data-section-type="product_detail"] [data-click="addToCart"], [data-section-type="product_detail"] [role="addToCart"], [data-section-type="product_detail"] [data-click="buyNow"], [data-section-type="product_detail"] [role="buyNow"], [data-section-type="product"] [data-click="addToCart"], [data-section-type="product"] [role="addToCart"], [data-section-type="product"] [data-click="buyNow"], [data-section-type="product"] [role="buyNow"]'); } handleObserver() { if(this.config.trigger_condition == 'theme_hidden') { const $addCartBtn = this.getAddCartBtn(); if($addCartBtn) { // 配置了加购/购买按钮; 设置按钮为observer观察目标 $addCartBtn.setAttribute('id', 'app-atc-need-sticky-buttons') } } } findAncestor(node, selector) { while (node) { if (node.querySelector(selector)) { return node; } node = node.parentElement; } return null; } getThemeProductInfoForm() { let $themeForm = ''; const $themeProductInfo = document.querySelector('[data-section-type="product_detail"], [data-section-type="product"]'); $themeForm = $themeProductInfo?.querySelector('form'); return $themeForm; } // 获取主题商品加购数量 getThemeQuantity() { let $themeForm = this.getThemeProductInfoForm(); if($themeForm) { const formData = new FormData($themeForm); const quantity = formData.get('quantity'); return quantity; } else { return null; } } getThemeInitVariantsData() { const $themeForm = this.getThemeProductInfoForm(); const formData = new FormData($themeForm); } // 获取主题初始表单数据 getThemeProductFormData = () => { const $themeForm = this.getThemeProductInfoForm(); if($themeForm) { const form_data = new FormData($themeForm); const form_data_format = {} for (const [key, value] of form_data) { form_data_format[key] = value; } return form_data_format; } else { return null; } } brightnessByColor(c) { let color = '' + c, isHEX = c.indexOf('#') == 0, isRGB = c.indexOf('rgb') == 0; let r, g, b; if (isHEX) { var m = color.substr(1).match(color.length == 7 ? /(\S{2})/g : /(\S{1})/g); if (m) { (r = parseInt(m[0], 16)), (g = parseInt(m[1], 16)), (b = parseInt(m[2], 16)); } } if (isRGB) { const m = color.match(/^rgba\((\d+),\s*(\d+),\s*(\d+),(\d+)\)$/); if (m) { (r = m[1]), (g = m[2]), (b = m[3]); } } if (typeof r != 'undefined') return (r * 299 + g * 587 + b * 114) / 1000; }; getSelectedVariant() { const product = this.getProductData(); return product.variants.find(item => item.id == this.variantId); } setSelectedOption(product) { const selectedVariant = this.getSelectedVariant(); const productData = Object.assign({}, product); // 处理下拉选项 productData?.options?.forEach(option => { option.selectList = []; option.values?.forEach(value => { let selectStatus = ''; selectedVariant?.options?.forEach(item => { if(item.name == option.name && item.value == value) { selectStatus = 'checked'; } }) option.selectList.push({name: option.name, value: value, checked: selectStatus}); }) }) return productData; } getSelectedSkuText() { const selectVariant = this.getSelectedVariant(); if(!selectVariant) { return ''; } const selectList = selectVariant.options; for (var i = 0; i < selectList.length; i++) { selectList[i].value = selectVariant[`option${i + 1}`]; } const selectedSkuText = selectList .map( item => { return item.value; }) .join('/'); return selectedSkuText; } // 是否展示 加购弹窗 getIsButtonClickInfo() { const product = this.getProductData(); const isMultipleProduct = product.available && !product.has_only_default_variant; // 多款式商品 const isButtonClickInfo = this.config.isMobile && (this.config.style_mobile === "mb_simple" || this.config.style_mobile === "mb_circle") && this.config.button_click_mobile === 'info' && isMultipleProduct; return isButtonClickInfo; } // 是否跟随主题的加购数量 isFollowThemeQty (移动端 simple、circle模版、 PC端 simple模版加购数量从主题获取) getIsFollowThemeQty() { const followThemeQtyMobile = (this.config.style_mobile === "mb_simple" || this.config.style_mobile === "mb_circle") && this.config.isMobile; const followThemeQtyPc = this.config.style === "simple" && !this.config.isMobile; const isFollowThemeQty = (followThemeQtyPc || followThemeQtyMobile) ? true : false; return isFollowThemeQty; } getBannerRenderData() { const product = this.getProductData(); // 当前选中变体 const selectedVariant = this.getSelectedVariant(); // 变体options下拉列表数据处理 const productData = this.setSelectedOption(product); // 选中的sku文案 const selectedSkuText = this.getSelectedSkuText(); // 是否展示 加购弹窗 const is_button_click_info = this.getIsButtonClickInfo(); // 主题是否有配置加购/购买按钮 const isHasAddCartBtn = this.getAddCartBtn() ? true : false; // 加购数量是否跟随主题 const isFollowThemeQty = this.getIsFollowThemeQty(); // 浅色背景颜色配置 const brightness = this.brightnessByColor(this.config.module_bg); const isDarkBg = brightness < 115; const renderData = { product: { ...productData, config: {...this.config}, selectedVariant, selectedSkuText, }, config: this.config, qty: this.qty, variant: selectedVariant, selectedVariant, selectedSkuText, is_button_click_info, isDarkBg, isHasAddCartBtn, isFollowThemeQty, i18n: this.i18n, hasGlobalAtcListener: this.hasGlobalAtcListener }; return renderData; } // render add_to_cart async renderAll() { const renderData = this.getBannerRenderData(); this.triggerEvent_('renderBanner', renderData); } getConfigData() { return fetch(`/api/add-to-cart-config`).then(res => { return res.json(); }); } async getConfig() { if(!this.config.hasOwnProperty('open_status')) { const res = await this.getConfigData(); if (res.state === 0 && res.data.open_status) { // config数据转化 this.transConfigData(res.data); } } return this.config; } transConfigData(data) { const resConfig = data; this.config = { ...this.config, ...resConfig, isMobile: window.innerWidth < 768 ? true : false, position: window.innerWidth < 768 ? 'down' : resConfig.display_position }; // 颜色跟随系统时 if(resConfig.color_setting === 'default' || resConfig.color_setting === 'theme') { this.config = { ...this.config, ...this.defaultColorConfig } } } // 是否应用到商店 async handleIsRender() { const res = await this.getConfigData(); if (res.state === 0 && res.data.open_status) { // config数据转化 this.transConfigData(res.data); this.handleObserver(); // 渲染加购内容 this.renderAddToCart(); } } renderAddToCart() { const product = this.getProductData(); if (product.product_type === 'gift_card') return ; if (!product.available) return; this.renderAll(); } // 选择变体 上报 trackVariant() { const product = this.getProductData(); window.sa && sa.track('plugin_atc_variant_click', { product_id: product.id, style: this.config.style, style_mobile: this.config.style_mobile }); } // 更改数量上报 trackQty() { const product = this.getProductData(); window.sa && sa.track('plugin_atc_qty_click', { product_id: product.id, style: this.config.style, style_mobile: this.config.style_mobile }); } // 加购上报 trackAddtoCart() { const product = this.getProductData(); let properties = ''; let source = 'atc'; if (this.config.button_action === 'checkout') { source = 'buy_now'; } const obj = { product_id: product.id, style: this.config.style, style_mobile: this.config.style_mobile, button_action: this.config.button_action, plugin_timestamp: this.plugin_timestamp }; const options = { product_id: product.id, variant_id: this.variantId, quantity: this.qty, source, variant: this.getSelectedVariant(), product: product, process: (window.SHOP_PARAMS.product_settings || {}).add_to_cart_process, properties: properties } // 注册曝光参数到add_to_cart事件 window.spzutm && window.spzutm.registerParams('add_to_cart', { add_to_cart: JSON.stringify(obj) }); window.sa && sa.track('plugin_atc_button_click', obj); this.trackHookAddTocart(); } trackAtcView() { const product = this.getProductData() if(!this.isHasView) { sa.track('plugin_atc_view', { product_id: product.id, button_action: this.config.button_action, style: this.config.style, style_mobile: this.config.style_mobile, plugin_timestamp: this.plugin_timestamp }); this.isHasView = true; } } trackHookAddTocart(options) { //FLASH及之后主题trigger事件上报采用HOOK window.djInterceptors && window.djInterceptors.track && window.djInterceptors.track.use({ event: 'dj.addToCart', params: { id: options.product_id, number: options.quantity, childrenId: options.variant.id, item_price: options.variant.price, name: options.product.title, type: options.variant.type ? options.variant.type : options.product.type, properties: properties, quantity: options.quantity, variant_id: options.variant.id, product_id: options.product_id, source: 'atc', process: options.process }, once: true }); } // 加购弹窗 renderShopModal_() { const renderData = this.getBannerRenderData(); this.triggerEvent_('showAddToCartModal', renderData); } showSuccessToast() { this.triggerEvent_('showAddToCartToast'); } updateSelectVariant = async(selectedVariant, product) => { const configData = await this.getConfig(); const productData = this.setSelectedOption(product); const selectedSkuText = this.getSelectedSkuText(); const renderData = { ...productData, selectedVariant, config: configData, selectedSkuText }; this.triggerEvent_('variantChange', renderData); const renderBannerData = this.getBannerRenderData(); if(renderBannerData.is_button_click_info && this.config.style_mobile === "mb_circle") { this.triggerEvent_('renderBannerClickInfo', renderBannerData); } } getUrlVariantId() { const urlParams = new URLSearchParams(window.location.search); const variantId = urlParams.get('variant'); return variantId; } // 商详页变体切换 djVariantChange(detail) { const product = this.getProductData(); // 是否为商详页当前商品 const isDetailProduct = product?.id === detail?.product?.id; if(!detail || !isDetailProduct) { return; } const selectVariant = detail.selected; // 更新变体id const variantId = selectVariant.id; const oldVariantId = this.variantId; if(variantId) { this.triggerEvent_('renderSelectedVariant'); if(oldVariantId !== variantId || !this.isFirstUpdateVariant) { this.isFirstUpdateVariant = true; this.variantId = variantId; this.updateSelectVariant(selectVariant, detail.product); } } else if(!variantId){ // 主题未选中变体 const isDisabledAtc = this.disabledAtc(); if(isDisabledAtc) { this.triggerEvent_('renderBannerNoSelectedVariant', { product: detail.product }); } } } // 不展示变体的模版 isHiddenVariantsTemplate() { return ((this.config.style_mobile === 'mb_circle' || this.config.style_mobile === 'mb_simple') && this.config.isMobile) || (this.config.style === 'simple' && !this.config.isMobile); } // 主题未选中变体 isUnSelectedThemeVariants() { // 不展示变体的模版 const formData = this.getThemeProductFormData(); return (formData && formData.hasOwnProperty('variant_id') && (!formData.variant_id || formData.variant_id === 'undefined')); } // 禁用加购 disabledAtc() { // 不展示变体的模版 const isHiddenVariantsTemplate = this.isHiddenVariantsTemplate(); const isUnSelectedThemeVariant = this.isUnSelectedThemeVariants(); return isHiddenVariantsTemplate && isUnSelectedThemeVariant; } // 滚动至主题变体处 scrollToThemeVariant() { const themeFormEl = this.getThemeProductInfoForm(); if(themeFormEl) { this.triggerEvent_('showUnSelectedVariantTips'); setTimeout(() => { themeFormEl.scrollIntoView({ behavior: 'smooth' }); }, 1000); } } async setupAction_() { // 上报处理 this.registerAction('saTrack', async(invocation) => { const data = invocation.args; // this.trackMap[data.type]?.(); }) // 更新variantId this.registerAction('updateVriantId', async(invocation) => { const data = invocation.args.data; if(data.variant_id && data.variant_id != 'undefined') { this.variantId = data.variant_id; } }) // 渲染弹窗加购&购买按钮 this.registerAction('renderShopModal', async(invocation) => { this.renderShopModal_(); }) this.registerAction('bannerExtraRender', async(invocation) => { const renderData = this.getBannerRenderData(); if(renderData.is_button_click_info && this.config.style_mobile === "mb_circle") { this.triggerEvent_('renderBannerClickInfo', renderData); } }) // 加购数量同步 this.registerAction('handleAddToCart', async(invocation) => { // 主题未选中变体 const isDisabledAtc = this.disabledAtc(); if(isDisabledAtc) { this.scrollToThemeVariant(); return; } const quantity = this.getThemeQuantity(); if(this.config.button_action === "checkout" ) { this.triggerEvent_('updateQty', { quantity: Number(quantity) || this.qty }); this.triggerEvent_('handleBuyNow'); } else { this.triggerEvent_('updateQty', { quantity: Number(quantity) || this.qty }); this.triggerEvent_('handleAtc'); } }) // 渲染弹窗加购&购买按钮 this.registerAction('handleFixedBanner', async(invocation) => { // 固定定位时, 触发ljs-sticky 组件showSticky方法,展示banner if(this.config.trigger_condition == 'fixed') { this.triggerEvent_('showBanner'); // this.trackAtcView(); } }) this.registerAction('handleVariantsRender', async(invocation) => { const data = invocation.args.data; // 主题未选中变体 const isDisabledAtc = this.disabledAtc(); if(isDisabledAtc) { this.triggerEvent_('renderBannerNoSelectedVariant', { product: data }); return; } this.triggerEvent_('renderSelectedVariant', { product: data }); this.triggerEvent_('variantChange', data); }) this.registerAction('handleShowErrorToast', async(invocation) => { const data = invocation.args.data; if(data?.message) { this.triggerEvent_('showAtcErrorToast', data); } }) } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${TAG}.${name}`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } } SPZ.defineElement(TAG, SpzCustomAddToCart)
              const TAG = "spz-custom-popup"; const DISPLAY_TYPE = { POPUP: "PTT_POPUP" // 弹窗 }; const API = { LIST: `/api/storefront/promotion/placement/list`, // 获取弹窗列表 REPORT: `/api/storefront/promotion/placement/data/report` // 上报数据 }; const DISPLAY_DEVICE = { PC_AND_MOBILE: "PD_PC_MOBILE", // PC和移动端 PC: "PD_PC", // PC MOBILE: "PD_MOBILE" // 移动端 }; const REPORT_EVENT = { CLICK: "PE_CLICK", // 点击事件 IMPRESSION: "PE_IMPRESSION" // 曝光事件 }; class SpzCustomPopup extends SPZ.BaseElement { constructor(element) { super(element); this.popupList_ = []; // 弹窗数据 this.popupZIndex = 1050; // 弹窗层级 // 节流处理 每5s内多次点击 算一次点击上报 this.throttleReport = this.win.SPZCore.Types.throttle( this.win, (data) => { this.reportData(data) }, 5000 ) } static deferredMount() { return false; } buildCallback() { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.setupAction_(); this.viewport_ = this.getViewport(); } mountCallback() { this.fetchData_(); } // 接口请求,获取数据 fetchData_() { const id = window.SHOPLAZZA.meta.page.template_type === 51 ? window.SHOPLAZZA.meta.page.resource_id : 0; return this.xhr_.fetchJson(API.LIST, { method: 'POST', body: { page_id: window.SHOPLAZZA.meta.page.template_type, placement_type: DISPLAY_TYPE.POPUP, discount_id: id } }).then((res) => { // 请求成功 执行render this.doRender_(res.list); }).catch((err) => { console.error(err); }); } // 渲染dom doRender_(data) { this.popupList_ = data || []; if (this.popupList_.length > 0) { this.popupList_.forEach((item) => { item.config = JSON.parse(item.config); }) } return this.templates_ .findAndRenderTemplate(this.element, { list: this.popupList_ }) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }) .then(() => { // 遍历显示弹窗 this.popupList_.forEach((item) => { this.showPopup_(item); }); }) } showPopup_(popup) { // 展示弹窗 符合展示条件的弹窗 const $popup = document.querySelector(`#popup-${popup.id}`); $popup && SPZ.whenApiDefined($popup).then((api)=> { const isPC = this.viewport_.getWidth() >= 960; const isMobile = this.viewport_.getWidth() < 960; const isMatchPCDevice = popup.device === DISPLAY_DEVICE.PC_AND_MOBILE || popup.device === DISPLAY_DEVICE.PC; const isMatchMobileDevice = popup.device == DISPLAY_DEVICE.PC_AND_MOBILE || popup.device === DISPLAY_DEVICE.MOBILE; if((isPC && isMatchPCDevice) || (isMobile && isMatchMobileDevice)) { // 根据推送时间 延迟展示弹窗 setTimeout(() => { api.open(); }, popup.delay_seconds * 1000); } }) } // 上报数据 async reportData(data) { this.xhr_.fetchJson(API.REPORT, { method: "POST", body: { placement_id: data.placement_id, event: data.event } }); } setupAction_() { this.registerAction('handleTrack', async(invocation) => { // 如果是主题编辑器则不用处理 if(window.top !== window.self) { return; } const data = invocation.args; const event = data.event; // 点击上报 节流处理 if(event === REPORT_EVENT.CLICK) { await this.throttleReport(data); } else { this.reportData(data); } }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SpzCustomPopup);
              const TAG = "spz-custom-announcement"; const DISPLAY_TYPE = { ANNOUNCEMENT: "PTT_BANNER" // 公告栏 }; const API = { LIST: `/api/storefront/promotion/placement/list`, // 获取公告栏列表 REPORT: `/api/storefront/promotion/placement/data/report` // 上报数据 }; const DISPLAY_DEVICE = { PC_AND_MOBILE: "PD_PC_MOBILE", // PC和移动端 PC: "PD_PC", // PC MOBILE: "PD_MOBILE" // 移动端 }; const REPORT_EVENT = { CLICK: "PE_CLICK", // 点击事件 IMPRESSION: "PE_IMPRESSION" // 曝光事件 }; const POSITION = { TOP: "PP_TOP", // 顶部 BOTTOM: "PP_BOTTOM" // 底部 } const MODE = { FIXED: "PM_FIXED", // 固定 NORMAL: "PM_SCROLLING" // 滚动 } const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name; class SpzCustomAnnouncement extends SPZ.BaseElement { constructor(element) { super(element); this.announcementList_ = []; // 公告栏数据 } static deferredMount() { return false; } buildCallback() { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.setupAction_(); this.viewport_ = this.getViewport(); } mountCallback() { this.fetchData_(); this.createAnnouncementDom_(); this.listenCartChange_(); } fetchData_(type = '') { const id = window.SHOPLAZZA.meta.page.template_type === 51 ? window.SHOPLAZZA.meta.page.resource_id : 0; return this.xhr_.fetchJson(API.LIST, { method: 'POST', body: { page_id: window.SHOPLAZZA.meta.page.template_type, placement_type: DISPLAY_TYPE.ANNOUNCEMENT, discount_id: id } }).then((res) => { this.announcementList_ = res.list || []; if (this.announcementList_.length > 0) { this.announcementList_.forEach((item) => { item.config = JSON.parse(item.config); }); } if(type === 'cartChange') { this.announcementList_.forEach((item) => { this.updateText_(item); }); } else { this.doRender_(this.announcementList_); } }).catch((error) => { console.error(error); }) } doRender_(data) { return this.templates_ .findAndRenderTemplate(this.element, { list: this.announcementList_ }) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }) .then(() => { this.announcementList_.forEach((item) => { this.showAnnouncement_(item); }); }).then(() => { this.handleThemeCompatibility_(); }); } // 更新文案 updateText_(item) { const announcement = document.querySelector(`#announcement-${item.id}`); const announcementText = announcement.querySelectorAll('.announcement_text'); const textArr = item.config.text_discount.replace_texts; const textDom = textArr.map((text) => { return `${text}`; }).join(','); announcementText.forEach((text) => { text.innerHTML = textDom; }); } // 创建公告栏dom createAnnouncementDom_() { const isHero = /Hero/.test(THEME_NAME); const isEva = /Eva/.test(THEME_NAME); const headerEl = document.querySelector('[data-section-type="header"]'); const headerSticky = headerEl && SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky'; // 创建滚动的底部公告栏 const announcementBottomContainer = document.createElement('div'); announcementBottomContainer.className = 'announcement__container_bottom bootstrap'; document.body.appendChild(announcementBottomContainer); // 创建固定的底部公告栏 const announcementBottomSticky = document.createElement('ljs-sticky'); announcementBottomSticky.className = 'announcement__container_bottom-sticky'; announcementBottomSticky.setAttribute('layout', 'container'); announcementBottomSticky.setAttribute('position', 'bottom'); announcementBottomSticky.style.position = 'fixed'; announcementBottomSticky.style.bottom = '0'; announcementBottomSticky.style.left = '0'; announcementBottomSticky.style.right = '0'; announcementBottomSticky.style.zIndex = '1030'; document.body.appendChild(announcementBottomSticky); const announcementTopContainer = document.createElement('div'); announcementTopContainer.classList.add('announcement__container_top'); if (isHero) { announcementTopContainer.classList.add('announcement__container_top_zIndex_1030'); } announcementTopContainer.classList.add('bootstrap'); document.body.insertBefore(announcementTopContainer, document.body.children[0]); const announcementTopFixedContainer = document.createElement('div'); announcementTopFixedContainer.classList.add('announcement__container_top-fixed'); if (isHero) { announcementTopFixedContainer.classList.add('announcement__container_top_zIndex_1030'); } announcementTopFixedContainer.classList.add('bootstrap'); const insertBeforeElement = headerSticky ? headerEl : document.body; insertBeforeElement.insertBefore(announcementTopFixedContainer, insertBeforeElement.children[0]); if (isEva) { const evaHeader = document.querySelector('header.header'); const isEvaMaskHeader = evaHeader && SPZCore.Dom.computedStyle(this.win, evaHeader).position === 'absolute'; let fixedBannerTopContainer = document.querySelector('.announcement__container_top-fixed'); if (isEvaMaskHeader) { if (fixedBannerTopContainer) { fixedBannerTopContainer.remove(); } const newBanner = document.createElement('div'); newBanner.className = 'announcement__container_top-fixed bootstrap'; document.body.insertBefore(newBanner, document.body.firstChild); fixedBannerTopContainer = newBanner; } else { if (!headerEl) return; const observer = new MutationObserver(() => { const isSticky = SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky'; if (!isSticky) return; const isTopFixedAnnouncementInHeader = headerEl.querySelector('.announcement__container_top-fixed'); if (isTopFixedAnnouncementInHeader) return; const announcementTopFixedContainer = document.querySelector('.announcement__container_top-fixed'); if (announcementTopFixedContainer) { announcementTopFixedContainer.remove(); headerEl.insertBefore(announcementTopFixedContainer, headerEl.children[0]); observer.disconnect(); } }); observer.observe(headerEl, { attributes: true, attributeFilter: ['style', 'class'] }); } if (headerSticky && !isEvaMaskHeader && fixedBannerTopContainer) { fixedBannerTopContainer.style.position = 'relative'; fixedBannerTopContainer.style.zIndex = '29'; } } } // 展示公告栏 showAnnouncement_(item) { const announcement = document.querySelector(`#announcement-${item.id}`); const announcementBottomContainer = document.querySelector('.announcement__container_bottom'); const announcementBottomSticky = document.querySelector('.announcement__container_bottom-sticky'); const announcementTopContainer = document.querySelector('.announcement__container_top'); const announcementTopFixedContainer = document.querySelector('.announcement__container_top-fixed'); const isPC = this.viewport_.getWidth() >= 960; const isMobile = this.viewport_.getWidth() < 960; const isMatchPCDevice = item.device === DISPLAY_DEVICE.PC_AND_MOBILE || item.device === DISPLAY_DEVICE.PC; const isMatchMobileDevice = item.device == DISPLAY_DEVICE.PC_AND_MOBILE || item.device === DISPLAY_DEVICE.MOBILE; if((isPC && isMatchPCDevice) || (isMobile && isMatchMobileDevice)) { if (item.position === POSITION.BOTTOM) { if(item.mode === MODE.FIXED) { announcementBottomSticky && announcementBottomSticky.appendChild(announcement); } else { announcementBottomContainer && announcementBottomContainer.appendChild(announcement); } } else { if (item.mode === MODE.FIXED) { announcementTopFixedContainer && announcementTopFixedContainer.appendChild(announcement); } else { announcementTopContainer && announcementTopContainer.appendChild(announcement); } } this.reportData({ placement_id: item.id, event: REPORT_EVENT.IMPRESSION }); } } // 处理主题兼容 handleThemeCompatibility_() { try { const isBoost = /Boost/.test(THEME_NAME); const isHyde = /Hyde/.test(THEME_NAME); const isEva = /Eva/.test(THEME_NAME); const boostHeader = document.querySelector('.boost-header'); const fixedBannerTopContainer = document.querySelector('.announcement__container_top-fixed'); const notFixedBannerTopContainer = document.querySelector('.announcement__container_top'); const headerEl = document.querySelector('[data-section-type="header"]'); const headerSticky = headerEl && SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky'; const header = document.querySelector('.header__fixed') || document.querySelector('.header__wrapper'); const headerFixed = header && SPZCore.Dom.computedStyle(this.win, header).position === 'fixed'; const handleScroll = SPZCore.Types.throttle(this.win, () => { if (isHyde) { if (header && headerSticky) { header.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; } else { notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; } } if (isEva) { const evaHeader = document.querySelector('header.header'); const isEvaMaskHeader = evaHeader && SPZCore.Dom.computedStyle(this.win, evaHeader).position === 'absolute'; if (!isEvaMaskHeader) return; if (evaHeader.classList.contains('header__fixed')) { evaHeader.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; } else { notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; } if(document.documentElement.scrollTop === 0) { evaHeader.style.marginTop = '0'; } } if (headerSticky) return; if (headerFixed) { header.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; } else { const observer = new MutationObserver((mutationsList, observer) => { for (const mutation of mutationsList) { if (mutation.type === 'childList' && fixedBannerTopContainer.childElementCount > 0) { notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; observer.disconnect(); // 停止观察 break; } } }); // 开始观察 fixedBannerTopContainer 的子节点变化 observer.observe(fixedBannerTopContainer, { childList: true, subtree: true }); // 初始检查 if (fixedBannerTopContainer.childElementCount > 0) { notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; } if(header) { header.style.marginTop = '0'; } } if (isBoost) { fixedBannerTopContainer.style.zIndex = '1031'; if (boostHeader && boostHeader.classList.contains('header__fixed')) { boostHeader.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; } else { notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`; } } }, 16); window.addEventListener('scroll', handleScroll); window.dispatchEvent(new Event('scroll')); } catch (error) { console.error('error', error); } } // 上报数据 async reportData(data) { // 如果是主题编辑器则不用处理 if(window.top !== window.self) { return; } this.xhr_.fetchJson(API.REPORT, { method: "POST", body: { placement_id: data.placement_id, event: data.event } }); } // 监听购物车变化事件dj.cartChange listenCartChange_() { SPZUtils.Event.listen(document, 'dj.cartChange', (event) => { this.fetchData_('cartChange'); }); } setupAction_() { this.registerAction('handleClose', (invocation) => { const data = invocation.args; const id = data.id; const announcement = document.querySelector(`#announcement-${id}`); announcement && SPZCore.Dom.removeElement(announcement); window.dispatchEvent(new Event('scroll')); }); this.registerAction('handleJumpLink', (invocation) => { const data = invocation.args; if(!data.show_url) return; data.url && window.open(data.url, data.open_new_window ? '_blank' : '_self'); this.reportData({ placement_id: data.id, event: REPORT_EVENT.CLICK }); }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SpzCustomAnnouncement);