Creating User Likes/Bookmarks Using Webflow and Memberstack – A Community Contribution

Article author
Josh Lopez
  • Updated

Introduction:

In our continuous effort to support our user community with practical solutions, we’re delighted to share a helpful guide contributed by Daniel Thomas from our community. This guide is for those who are looking to implement a 'like' button system on their Webflow site using Memberstack, without the need for external tools. A special thanks to Daniel for his valuable contribution!

How to Create a Like System with Webflow and Memberstack

Setting Up the Like Button

  1. Create a CMS Collection List: Start by creating the CMS collection list you want users to 'like' items from. Ensure each CMS item has a 'like' button.
  2. Assign Class and Custom Attribute to Buttons: Give each 'like' button a class name 'like-button'. Add a custom attribute (`data-cms-item="Unique CMS Item ID"`) to these buttons, linking them to a unique identifier in your Webflow CMS.
  3. Pre-style and Remove the 'is-liked' Class: Before publishing, pre-style the 'is-liked' class for these buttons and then remove it.

Implementing the JavaScript Code

Daniel provided the following JavaScript code to handle the functionality:

<script>
document.addEventListener('DOMContentLoaded', (event) => {
    const memberstack = window.$memberstackDom;

    // Get all the like buttons
    let likeButtons = document.querySelectorAll('.like-button');

    // Add an event listener to each like button
    likeButtons.forEach(likeButton => {
        let likeButtonLastClickedAt = 0;

        likeButton.addEventListener('click', async function () {
            // If the last click was less than 500ms ago, ignore this one
            const now = Date.now();
            if (now - likeButtonLastClickedAt < 500) {
              return;
            }
            likeButtonLastClickedAt = now;

            // Optimistically update the UI
            const cmsData = this.getAttribute('data-cms-item');
            if (this.classList.contains('is-liked')) {
                this.classList.remove('is-liked');
            } else {
                this.classList.add('is-liked');
            }

            // Get the current member
            let member = await memberstack.getCurrentMember();

            // If the member is not logged in, redirect to /signup
            if (!member || !member.data) {
                window.location.href = '/signup';
                return;
            }

            // Get current member's JSON
            let memberJson = await memberstack.getMemberJSON();

            // Unwrap unnecessary "data" objects
            while (memberJson.data) {
                memberJson = memberJson.data;
            }

            // Create the likes array if it doesn't exist
            if (!memberJson.likes) {
                memberJson.likes = [];
            }

            // Check if cmsData is already in the array, if not, add it
            if (!memberJson.likes.includes(cmsData)) {
                memberJson.likes.push(cmsData);
            } else {
                // If the item is already liked, remove it from the array
                memberJson.likes = memberJson.likes.filter(item => item !== cmsData);
            }

            // Update member's JSON asynchronously
            memberstack.updateMemberJSON({ json: memberJson }).catch((error) => {
                console.error("Failed to update member JSON: ", error);
                // If there was an error updating the JSON, revert the button style
                if (this.classList.contains('is-liked')) {
                    this.classList.remove('is-liked');
                } else {
                    this.classList.add('is-liked');
                }
            });
        });

        // Initialise the button state based on the member's likes
        (async function initButtonState() {
            // Get the current member
            let member = await memberstack.getCurrentMember();

            // If the member is not logged in, do nothing
            if (!member || !member.data) {
                return;
            }

            let memberJson = await memberstack.getMemberJSON();

            // Unwrap unnecessary "data" objects
            while (memberJson.data) {
                memberJson = memberJson.data;
            }

            let cmsData = likeButton.getAttribute('data-cms-item');

            // If the member has already liked the item, style the button as 'is-liked'
            if (memberJson.likes && memberJson.likes.includes(cmsData)) {
                likeButton.classList.add('is-liked');
            }
        })();
    });
});
</script>

Displaying User Likes on a Separate Page

To showcase user 'likes' on a different page:

  • Use JavaScript for conditional visibility in the Webflow CMS list.
  • Duplicate your initial CMS list onto a new page, like "Your Likes," ensuring each item has the required custom attribute.
  • Implement another JavaScript code snippet (provided by Daniel) to handle the visibility based on user 'likes'.
<script>
document.addEventListener('DOMContentLoaded', async () => {
    const memberstack = window.$memberstackDom;
    let memberJson = await memberstack.getMemberJSON();
    
    // Unwrap unnecessary "data" objects
    while (memberJson.data) {
        memberJson = memberJson.data;
    }

    // Assuming each CMS item has a data attribute 'data-cms-item' 
    // which matches the names stored in the user's 'likes' array
    let cmsItems = document.querySelectorAll('.featured-frequencies-card');

    cmsItems.forEach(item => {
        let itemName = item.getAttribute('data-cms-item');

        // If the user has not liked this item, hide it
        if (!memberJson.likes.includes(itemName)) {
            item.style.display = 'none';
        }
    });
});
</script>

Conclusion:

Daniel’s contribution exemplifies the power of community knowledge sharing. We hope this guide helps you enhance your website’s interactivity with a user-friendly 'like' system.

Was this article helpful?

Comments

0 comments

Please sign in to leave a comment.