Skip to content

Instantly share code, notes, and snippets.

@panoply
Last active July 8, 2024 06:26
Show Gist options
  • Save panoply/3656a32f1936a556d02f4f20bd082870 to your computer and use it in GitHub Desktop.
Save panoply/3656a32f1936a556d02f4f20bd082870 to your computer and use it in GitHub Desktop.
Client Slide Wishlist for Shopify

Usage

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 variant id) is used as the key identifier.

Virtual DOM list

Using the wishlist.render() method, you can compose a template in your DOM. The only expectation is an element with id wishlist

Example

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.

Code

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", '{}'))

Author

Follow me on X if you like Shopify and banter.

License

You are not allowed to use this in themes published to theme store. Everyone else, you are fine, DWTFYW.

window.wishlist=function(e){let t=null,s=0;const i={wished:[],unwish:[]},o=JSON.parse("string"==typeof e?e:localStorage.getItem("wishes")),n=(e="wish-button")=>document.body.querySelectorAll(e),r=(e="[data-wishes]")=>{s=Object.keys(o).length,n(e).forEach((e=>e.innerText=s))},c=(e,t=null)=>{if("string"==typeof e||"number"==typeof e)return null===t?n().forEach((t=>c(e,t))):void(!(e in o)&&t.classList.contains("active")?t.classList.remove("active"):e in o&&!t.classList.contains("active")&&t.classList.add("active"))},a=(e,{selector:s}={selector:"#wishlist"})=>{t||(t=e);const i=Object.values(o).map(((e,s)=>t(e))),n=document.body.querySelector(s);n&&(n.innerHTML=i.join(""))};return n().forEach((e=>{c(e.dataset.id,e),e.onclick=()=>{return((e,s,n,l=["wished",{...s}])=>{"object"==typeof s&&(e in o?(l[0]="unwish",delete o[e]):o[e]=s,localStorage.setItem("wishes",JSON.stringify(o)),c(e,n),r(),i[l[0]].forEach((t=>t({id:e,node:n,product:l[1]}))),t&&a(t))})(e.dataset.id,(s=e.getAttributeNames(),n=e,s.reduce(((e,t)=>({...e,[t.slice(5)]:n.getAttribute(t)})),{})),e);var s,n}})),r(),{get count(){return s},on:(e,t)=>i[e].push(t),wishes:o,update:c,render:a}}(localStorage.getItem("wishes")||localStorage.setItem("wishes","{}"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment