Add an “Apply to All” Button to Copy Address Options in the Multi-Shipping Cart
In this tutorial, we’ll show you how to add an “Apply to All” button to each address in the Giftship multi-shipping cart. Clicking the button copies that address’s option values — like a gift message or gift wrap selection — to every other address on the page, so customers don’t have to re-enter the same info for each address.
Note: This is an advanced tutorial designed for qualified Shopify developers with JavaScript experience. If you’re not working with a developer and need assistance, we can implement this for a flat fee of $X USD.
Step 1: Duplicate Your Live Theme
Always work on a duplicated development theme to ensure that your live store remains unaffected.
Step 2: Create and Upload the Snippet
Go to the Assets folder in your theme. Create a new JavaScript file named apply-to-all.js. Copy and paste the following code into the file:
/**
* Adds an 'Apply to All' button to each address in the Giftship multi-shipping cart.
* Clicking it copies that address's option input values (e.g. gift message, gift wrap)
* to the matching inputs for every other address on the page.
*
* Requires Giftship's multi-shipping cart markup (.gs__form-options-wrapper,
* .gs__app-container, .gs__cart-option, etc.) and the GSSDK global.
*
* To customize, edit CONFIG below.
*/
(() => {
var CONFIG = {
// Which .gs__cart-option[data-child-type] the button is anchored after.
// Note: this only controls placement — all "attributes[...]" inputs in the
// address's app container are copied, not just this input type.
anchorInputType: "message",
buttonClass: "gs__apply-all-btn",
buttonText: "Apply to All",
};
var APPLY_TO_ALL = {
init: function () {
this.registerEventHandlers();
},
registerEventHandlers: function () {
document.addEventListener('giftship.loaded', this.handleGiftshipLoaded.bind(this));
},
/**
* When Giftship loads on the multi-shipping page, add the 'Apply to All' button
* to each app container.
*/
handleGiftshipLoaded: function () {
var self = this;
setTimeout(function () {
var currentPage = window.GSSDK.page.get();
if (currentPage === "multi-shipping") {
self.insertApplyToAllBtns();
}
}, 500);
},
/**
* Add a button to each app container of the MS cart to apply its attributes to all other containers.
*/
insertApplyToAllBtns: function () {
var self = this;
var groups = document.querySelectorAll('.gs__form-options-wrapper');
if (!groups.length) {
console.warn("No multishipping groups found so no Apply To All button will be inserted.");
return;
}
groups.forEach(function (group) {
// Skip if this group already has a button (e.g. giftship.loaded fired again).
if (group.querySelector('.' + CONFIG.buttonClass)) {
return;
}
var optionsWrapperEl = group.querySelector('.gs__cart-option[data-child-type="' + CONFIG.anchorInputType + '"]');
if (!optionsWrapperEl) {
console.warn("No Giftship app options wrapper was found so no Apply To All button will be inserted.");
return;
}
var button = document.createElement('button');
button.type = "button";
button.className = CONFIG.buttonClass;
button.textContent = CONFIG.buttonText;
button.addEventListener('click', self.handleApplyToAllBtnClick.bind(self));
optionsWrapperEl.after(button);
});
},
/**
* On 'Apply to All' button click in the MS cart, get the attribute values for all inputs in
* the relevant app container and populate the other app container inputs with the values.
*/
handleApplyToAllBtnClick: function (e) {
var appContainer = e.target && e.target.closest(".gs__app-container");
if (!appContainer) {
console.warn("Invalid Apply to All button click event: ", e);
return;
}
var attributes = this.getAttributesFromCartOptions(appContainer, true);
this.populateCartOptionsWithCartAttributes(attributes);
},
/**
* Build an object of cart attibutes from the populated cart option values in a given element.
*/
getAttributesFromCartOptions: function (wrapperEl, msCart) {
var self = this;
var attributes = {};
var drawerCartOptionEls = wrapperEl.querySelectorAll('[name^="attributes"]');
if (!drawerCartOptionEls.length) {
console.warn("No cart options inputs found.");
return attributes;
}
Array.from(drawerCartOptionEls).forEach(function (optionEl) {
var attributeName = optionEl.name;
var attributeValue = optionEl.value;
// If in the MS cart we need to strip the address from the name
if (msCart) {
attributeName = self.getFormattedAttributeName(attributeName);
}
// If the input is a checkbox, handle unchecked scenario
if (optionEl.type === "checkbox" && !optionEl.checked) {
attributeValue = "";
}
attributes[attributeName] = attributeValue;
});
return attributes;
},
/**
* Strip address from MS cart option input name.
*/
getFormattedAttributeName: function (attributeName) {
var addressHyphenIndex = attributeName.indexOf('-');
if (addressHyphenIndex === -1) {
return attributeName;
}
return attributeName.slice(0, addressHyphenIndex).trim();
},
/**
* Given an object of cart attributes, populate all cart options with their respective value.
*/
populateCartOptionsWithCartAttributes: function (cartAttributes) {
var self = this;
if (!cartAttributes || typeof cartAttributes !== "object") {
console.warn("Attempting to populate cart options with invalid attributes: ", cartAttributes);
return;
}
Object.entries(cartAttributes).forEach(function ([key, value]) {
var modifiedKey = (key.charAt(key.length - 1) === "]") ? key.slice(0, -1) : key; // remove trailing square bracket
var optionEls = document.querySelectorAll('[name^="' + modifiedKey + '"]');
if (!optionEls.length) {
console.warn("No option elements found on the multi-shipping page with key: ", key);
return;
}
Array.from(optionEls).forEach(function (optionEl) {
if (optionEl.type === 'checkbox') {
self.toggleCheckboxOption(optionEl, value);
} else {
optionEl.value = value;
}
});
});
},
/**
* Toggle checkbox option input and show hidden gift message fields if necessary.
*/
toggleCheckboxOption: function (checkboxInputEl, value) {
var shouldCheck = (value === "yes" || value === "1");
checkboxInputEl.checked = shouldCheck;
var optionParent = checkboxInputEl.closest(".gs__cart-option");
if (shouldCheck && optionParent && optionParent.classList.contains("gs__collapsed")) {
optionParent.classList.remove("gs__collapsed");
}
},
};
// Initialize the module
APPLY_TO_ALL.init();
})();
Step 3: Edit theme.liquid to Include the Snippet
Open theme.liquid in the theme editor. Scroll to the bottom, just above the closing </body> tag. Paste the following code:
<script src="{{ 'apply-to-all.js' | asset_url }}" defer="defer"></script>
Step 4: Customize the Anchor Input (Optional)
By default, the button is inserted after the cart option with data-child-type="message" in each address group. If your store uses a different option as the anchor point, update CONFIG.anchorInputType in the script to match.
Note that this setting only controls where the button is placed — clicking “Apply to All” still copies every attributes[...] input found in that address’s container, not just the anchor input.
Can’t find the answer in our documentation? Contact Support. Let me know if you need help adjusting this for your theme.