Table of Contents

Create Dynamic Shopping Cart with JS and CSS

Cesar Banchio Updated by Cesar Banchio

In this article we will provide step-by-step guidelines to help you create a shopping cart that meets your needs based on an Airtable database. The example provided is a Restaurant type bot where you can select the meals you would like to order.

This is how the bot looks

And this is how the builder is structured:

The Builder

Let's dive in on some blocks:

The Airtable block is Get Multiple Items. What this block will do is fetch all items from the Airtable shown below. This is the structure:

We will save the records in an array type variable called @result

The block is a Javascript block with the following snippet:

let menu = @{result}

this.window.menu = menu

We are passing the variable @result which holds all of our items from the Airtable to the Window object.

The set variable block contains the HTML structure where we will display the elements:

<section class="menu">

<div class="title">

<h2>Landbot's Menu</h2>

<div class="underline"></div>

</div>

<div class="btn-container">

<!-- <button class="filter-btn" type="button" data-id="all">all</button>

<button class="filter-btn" type="button" data-id="breakfast">

breakfast

</button>

<button class="filter-btn" type="button" data-id="lunch">lunch</button>

<button class="filter-btn" type="button" data-id="shakes">

shakes

</button> -->

</div>

<div class="section-center">

<!-- <article class="menu-item">

<img src="menu-item.jpeg" class="photo" alt="menu item" />

<div class="item-info">

<header>

<h4>buttermilk pancakes</h4>

<h4 class="price">$15</h4>

</header>

<p class="item-text">

Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque

quidem nihil exercitationem fugiat facilis distinctio nulla aut

voluptas deleniti? Laboriosam!

</p>

</div>

</article> -->

</div>

<div id="total">

<h3>Total: $ 0</h3>

</div>

</section>

We will save this in a string type variable called @menu

After this block, we use a Message block to display this variable:

Then, we will have 2 Code Set block to save the items selected and the total price into 2 variables:

Structure of the Airtable

Keep in mind that the column names have to remain the same in order for the Javascript snippet to work just as it is:

We have 6 columns, Name which is the name of the item, Photo which contains the URL of the photo, ID, Category which will serve for the buttons at the top to filter the items based on this (only use a single word for each category), Price and Description.

The CSS

Please copy and paste this CSS on the Design Section >> Custom CSS of your bot.

:root {

/* dark shades of primary color*/

--clr-primary-1: hsl(205, 86%, 17%);

--clr-primary-2: hsl(205, 77%, 27%);

--clr-primary-3: hsl(205, 72%, 37%);

--clr-primary-4: hsl(205, 63%, 48%);

/* primary/main color */

--clr-primary-5: #49a6e9;

/* lighter shades of primary color */

--clr-primary-6: hsl(205, 89%, 70%);

--clr-primary-7: hsl(205, 90%, 76%);

--clr-primary-8: hsl(205, 86%, 81%);

--clr-primary-9: hsl(205, 90%, 88%);

--clr-primary-10: hsl(205, 100%, 96%);

/* darkest grey - used for headings */

--clr-grey-1: hsl(209, 61%, 16%);

--clr-grey-2: hsl(211, 39%, 23%);

--clr-grey-3: hsl(209, 34%, 30%);

--clr-grey-4: hsl(209, 28%, 39%);

/* grey used for paragraphs */

--clr-grey-5: hsl(210, 22%, 49%);

--clr-grey-6: hsl(209, 23%, 60%);

--clr-grey-7: hsl(211, 27%, 70%);

--clr-grey-8: hsl(210, 31%, 80%);

--clr-grey-9: hsl(212, 33%, 89%);

--clr-grey-10: hsl(210, 36%, 96%);

--clr-white: #fff;

--clr-red-dark: hsl(360, 67%, 44%);

--clr-red-light: hsl(360, 71%, 66%);

--clr-green-dark: hsl(125, 67%, 44%);

--clr-green-light: hsl(125, 71%, 66%);

--clr-gold: #c59d5f;

--clr-black: #222;

--ff-primary: "Roboto", sans-serif;

--ff-secondary: "Open Sans", sans-serif;

--transition: all 0.3s linear;

--spacing: 0.25rem;

--radius: 0.5rem;

--light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);

--dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);

--max-width: 1170px;

--fixed-width: 620px;

}

/*

===============

Global Styles

===============

*/

*,

::after,

::before {

margin: 0;

padding: 0;

box-sizing: border-box;

}

ul {

list-style-type: none;

}

a {

text-decoration: none;

}

img:not(.logo) {

width: 100%;

}

img {

display: block;

}

h1,

h2,

h3,

h4 {

text-transform: capitalize;

line-height: 1.25;

margin-bottom: 0.75rem;

font-family: var(--ff-primary);

}

h1 {

font-size: 2rem;

}

h2 {

font-size: 1.5rem;

}

h3 {

font-size: 1.25rem;

}

h4 {

font-size: 0.875rem;

}

p {

margin-bottom: 1.25rem;

color: var(--clr-grey-5);

}

@media screen and (min-width: 800px) {

h1 {

font-size: 4rem;

}

h2 {

font-size: 2.5rem;

}

h3 {

font-size: 1.75rem;

}

h4 {

font-size: 1rem;

}

body {

font-size: 1rem;

}

h1,

h2,

h3,

h4 {

line-height: 1;

}

}

/* global classes */

.btn {

text-transform: uppercase;

background: transparent;

color: var(--clr-black);

padding: 0.375rem 0.75rem;

letter-spacing: var(--spacing);

display: inline-block;

transition: var(--transition);

font-size: 0.875rem;

border: 2px solid var(--clr-black);

cursor: pointer;

box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);

border-radius: var(--radius);

}

.btn:hover {

color: var(--clr-white);

background: var(--clr-black);

}

/* section */

/*

===============

Menu

===============

*/

.btn--menu {

text-transform: uppercase;

background: transparent;

color: var(--clr-black);

padding: 0.375rem 0.75rem;

letter-spacing: var(--spacing);

display: inline-block;

transition: var(--transition);

font-size: 0.55rem;

border: 2px solid var(--clr-black);

cursor: pointer;

box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);

border-radius: var(--radius);

}

.btn--menu:hover {

color: var(--clr-white);

background: var(--clr-black);

}

.btn--menu:active {

background-color:gray;

}

.title {

text-align: center;

margin-bottom: 2rem;

}

.underline {

width: 5rem;

height: 0.25rem;

background: var(--clr-gold);

margin-left: auto;

margin-right: auto;

}

.btn-container {

margin-bottom: 4rem;

display: flex;

justify-content: center;

}

.filter-btn {

background: transparent;

border-color: var(--clr-gold);

font-size: 1rem;

text-transform: capitalize;

margin: 0 0.5rem;

letter-spacing: 1px;

border-radius: var(--radius);

padding: 0.375rem 0.75rem;

color: var(--clr-gold);

cursor: pointer;

transition: var(--transition);

}

.filter-btn:hover {

background: var(--clr-gold);

color: var(--clr-white);

}

.menu-item {

display: grid;

gap: 1rem 2rem;

max-width: 25rem;

animation-name:appear;

animation-duration: 4s;

}

.photo {

object-fit: cover;

height: 100px;

border: 0.25rem solid var(--clr-gold);

border-radius: var(--radius);

}

.item-info header {

display: flex;

justify-content: space-between;

border-bottom: 0.5px dotted var(--clr-grey-5);

}

.item-info h4 {

margin-bottom: 0.5rem;

}

.price {

color: var(--clr-gold);

}

.item-text {

margin-bottom: 0;

}

@media screen and (min-width: 768px) {

.menu-item {

grid-template-columns: 225px 1fr;

gap: 0 1.25rem;

max-width: 40rem;

}

.photo {

height: 175px;

}

}

@media screen and (min-width: 1200px) {

.section-center {

width: 95vw;

grid-template-columns: 1fr 1fr;

}

.photo {

height: 150px;

}

}

@keyframes appear {

from {opacity:0;}

to {opacity:1;}

}

The Javascript

Please copy and paste the JS snippet in the Design Section >> Custom Code

<script>

var sectionCenter= null

var container = null

window.selectedItems = []

window.total = 0;

function logging (e) {

const found = window.selectedItems.some(el => el.item === e.target.innerText)

if (found) {

window.selectedItems = window.selectedItems.filter(el => el.item !== e.target.innerText)

let item = e.target.closest(".menu-item")

item.style.backgroundColor= "rgba(255,255,255,0.1)"

}

else {

window.selectedItems.push({"item": e.target.innerText, "price": e.target.dataset.price, "photo": e.target.dataset.photo})

let item = e.target.closest(".menu-item")

item.style.backgroundColor= "rgba(0,0,0,0.3)"

}

let total = window.document.querySelector("#total").querySelector("h3")

let amount = window.selectedItems.reduce( (acc, curr, ind, _) =>{ return acc + (curr.price * 1) }, 0)

total.innerText = `Total: $ ${amount.toFixed(2)}`

window.total = amount.toFixed(2);

}

function addListener(elem) {

for (var i = 0; i < elem.length; i++) {

elem[i].addEventListener("click", logging);

};

}

function displayMenuItems(menuItems) {

let displayMenu = menuItems.map(function (item) {

// console.log(item);

return `<article class="menu-item">

<img src=${item.Photo} class="photo" alt=${item.Name} />

<div class="item-info">

<header>

<button class="btn--menu" data-price="${item.Price}" data-photo="${item.Photo}">${item.Name}</button>

<h4 class="price">$${item.Price}</h4>

</header>

<p class="item-text">

${item.Description}

</p>

</div>

</article>`;

});

displayMenu = displayMenu.join("");

sectionCenter.innerHTML = displayMenu;

let buttons = window.document.getElementsByClassName("btn--menu")

addListener(buttons)

}

function displayMenuButtons(menu) {

const categories = menu.reduce(

function (values, item) {

if (!values.includes(item.Category)) {

values.push(item.Category);

}

return values;

},

["all"]

);

const categoryBtns = categories

.map(function (category) {

return `<button class="filter-btn" type="button" data-id=${category}>

${category}

</button>`;

})

.join("");

container.innerHTML = categoryBtns;

const filterBtns = container.querySelectorAll(".filter-btn");

// filter items

filterBtns.forEach(function (btn) {

btn.addEventListener("click", function (e) {

const category = e.currentTarget.dataset.id;

const menuCategory = menu.filter(function (menuItem) {

// console.log(menuItem.category);

if (menuItem.Category === category) {

return menuItem;

}

});

if (category === "all") {

displayMenuItems(menu);

} else {

displayMenuItems(menuCategory);

}

});

});

}

const findMenu = () => {

let menus = window.document.querySelector(".menu")

if (menus) {

let menu = window.menu

sectionCenter = window.document.querySelector(".section-center");

container = window.document.querySelector(".btn-container");

displayMenuItems(menu);

displayMenuButtons(menu);

let buttons = window.document.getElementsByClassName("btn--menu")

addListener(buttons)

clearInterval(id)

}

}

let id = setInterval(findMenu, 1000)

</script>

Template

Download the template here

How did we do?

How to pass WordPress logged in user data to Landbot

Set the flow depending on the url path (for embedded landbots)

Contact