The function invokes as an IIFE and is exposed in global scope (i.e, window.wishlist
or simply, wishlist
). This means that you simply drop it in via <script>
. It provides several methods for usage. By default, it will not do anything until you wire it up. Firstly, let me explain the main methods:
// use render method to optionally create a virtual wishlist, more on this later
wishlist.render((product) => `<a href="#"><img class="img" src="${product.img}"></a>`);
// If you need to listen for wishes
wishlist.on('wished', (args) => console.log(args));
// If you need to listen for unwishes
wishlist.on('unwish', (args) => console.log(args));
// This returns the number of items in wishlist
wishlist.count
There are additional methods such as update
and init
but you probably won't need them, feel free to adjust if you do. Let's look at how we can leverage this in our Shopify stores. The script works rather simple, it looks for certain elements with attribution. The only expectation is that you provide a custom element. Using data-
attributes, you can compose reference objects of products and they will be saved to LocalStorage, here is a "wish" button typical setup:
<wish-button
data-id="{{ product.id }}"
data-url="{{ product.url }}"
data-title="{{ product.title }}"
data-img="{{ product.featured.image }}"></wish-button>
Whenever a merchant presses a button data is saved to localstorage as an object structure, as an example they above will be:
{ 123456789: { url: '/some-url/', title: 'Sissel' img: 'https://cdn.shopify.com/xxx'}
Each key is a product
id
(or variantid
) is used as the key identifier.
Using the wishlist.render()
method, you can compose a template in your DOM. The only expectation is an element with id wishlist
https://tinyurl.com/for-the-little-dogs
As per the flems, you will see the code and setup.
The flems example is VERY easy to understand. Click the
.js
tab and the.css
tab where I provide you nerds with a demo for how things might look. Some other key points and takeaways if the 40 lines is too hard for you:
'wish-button' // this is the selector for wishes, i.e: a button to like a product
'#wishlist' // use this selector if you want to virtuall render wishes, all entries will be inserted
'data-wishes' // apply this selector to elements you want a count to show.
Please note that the <wishlist-button>
will apply an active
class when a wished item is selected.
window.wishlist = (function (cache) {
let tm = null; // template
let am = 0; // amount, i.e: count
const on = { wished: [], unwish: [] } // on events store
const wishes = JSON.parse(typeof cache === 'string' ? cache : localStorage.getItem("wishes"));
const elements = (el = 'wish-button') => document.body.querySelectorAll(el);
const attrs = (k, e) => k.reduce((a, n) => ({ ...a,[n.slice(5)]: e.getAttribute(n) }),{});
const storage = (id, product, node, ev = ['wished', { ...product }]) => {
if (typeof product !== 'object') return;
if (id in wishes) ev[0] = 'unwish', delete wishes[id]; else wishes[id] = product;
localStorage.setItem("wishes", JSON.stringify(wishes));
update(id, node);
count()
on[ev[0]].forEach(cb => cb({ id, node, product: ev[1] }));
if (tm) render(tm)
}
const count = (selector = '[data-wishes]') => {
am = Object.keys(wishes).length
elements(selector).forEach(c => c.innerText = am)
}
const update = (id, node = null) => {
if (typeof id !== 'string' && typeof id !== 'number') return;
if (node === null) return elements().forEach(node => update(id, node));
if (!(id in wishes) && node.classList.contains('active')) node.classList.remove('active');
else if (id in wishes && !node.classList.contains('active')) node.classList.add('active');
};
const render = (dom, { selector } = { selector: '#wishlist' }) => {
if (!tm) tm = dom // the callback template
const nodes = Object.values(wishes).map((product, idx) => tm(product));
const element = document.body.querySelector(selector)
if (element) element.innerHTML = nodes.join('');
}
const init = () => elements().forEach(node => {
update(node.dataset.id, node);
node.onclick = () => storage(node.dataset.id, attrs(node.getAttributeNames(), node), node)
})
init()
count()
return { get count () { return am }, on: (ev,cb) =>on[ev].push(cb), wishes, update, render };
})(localStorage.getItem("wishes") || localStorage.setItem("wishes", '{}'))
Follow me on X if you like Shopify and banter.
You are not allowed to use this in themes published to theme store. Everyone else, you are fine, DWTFYW.