There are many ways to build a shopping cart. Most of shopping cart implementations are static. In case of you want to create a dynamic shopping cart using drag’n’drop interactions, then I will show you how to do it using JS and DragsterJS.

What is DragsterJS?

DragsterJS is a JavaScript library that provides a drag’n’drop interface for web applications and websites. It’s a tiny library with no dependencies.

By using it, you are able to build user-attractive and effective user interfaces that implement drag’n’drop interactions, to drag any element from any place and to drop it anywhere you allow to.

DragsterJS is used in many web projects. One of the most interesting ones is eZ Platform Enterprise Edition where it’s used to build landing page creator.

Shopping cart prototype requirements

The very first thing before we start developing any kind of feature is to define the minimum set of functionalities that make a feature usable. For the purposes of this tutorial the requirements will not be very complicated.

The list contains the following functionalities:

Display a list of draggable items, that can be dropped onto a shopping cart area,

Display a shopping cart area where items can be dropped,

After dropping an item, the list of selected items should update and a shopping cart should have a price recalculated.

These 3 requirements will be delivered with code samples presented later in the article.

Shopping cart prototype in action

Shopping cart HTML structure and the styling

Before we start implementing cart’s functionalities using JavaScript code we have to create the HTML structure. The code should implement a list of items and a shopping cart section.

At first, we’ll start with HTML code:

<div class="container">

<div class="items region region--drag-only">

<div class="item" data-id="1" data-price="3" data-name="Product 1">

<h3>Product 1</h3>

<img src="" alt="Product 1 - price: $3" />

<p>Product 1 description</p>

</div>

<div class="item" data-id="2" data-price="11.99" data-name="Product 2">

<h3>Product 2</h3>

<img src="" alt="Product 2 - price: $11.99" />

<p>Product 2 description</p>

</div>

<div class="item" data-id="3" data-price="8.59" data-name="Product 3">

<h3>Product 3</h3>

<img src="" alt="Product 3 - price: $8.59" />

<p>Product 3 description</p>

</div>

<div class="item" data-id="4" data-price="2.5" data-name="Product 4">

<h3>Product 4</h3>

<img src="" alt="Product 4 - price: $2.5" />

<p>Product 4 description</p>

</div>

</div>

<div class="shopping-cart">

<h3>Your items</h3>

<div class="shopping-cart__dropzone region"></div>

<div class="shopping-cart__summary">

<ul class="shopping-cart__items"></ul>

<div class="shopping-cart__total-price" data-total="0"></div>

</div>

</div>

</div>

The structure has been splitted into 2 separate sections: .items and .shopping-cart .

Important! Remember to add the DragsterJS library files to your project. For instance, the following way:

<script src="dragster.min.js"></script>

The good practice is to add this script (and any other JS scripts) before the closing tag of <body> .

Now it’s time to apply some CSS styling to the HTML structure we have just prepared:

* {

box-sizing: border-box;

} html {

font-size: 16px;

font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;

line-height: 1.4;

} body {

width: 80vw;

margin: auto;

color: #1e1e1e;

} .container {

display: flex;

flex-wrap: nowrap;

} .items {

flex: 1 1 25%;

} .shopping-cart {

flex: 1 1 75%;

} .item {

cursor: move;

background: #eee;

border: dashed 1px #ddd;

padding: .5rem;

border-radius: .25rem;

} .item h3,

.shopping-cart h3 {

margin: 0 0 .5rem;

} .item img {

font-style: italic;

font-size: .75rem;

} .item p {

margin: .5rem 0;

} .dragster-draggable+.dragster-draggable {

margin-top: .25rem;

} .shopping-cart {

padding-left: 1rem;

} .shopping-cart__dropzone {

height: 5rem;

border: 1px dashed #1e1e1e;

border-radius: .25rem;

position: relative;

} .shopping-cart__dropzone:after {

content: 'Drop here';

position: absolute;

top: 50%;

left: 50%;

transform: translate(-50%, -50%);

} .shopping-cart__dropzone:hover {

background: #eee;

} .shopping-cart__items {

margin: 0;

padding: 0;

list-style-position: inside;

} .shopping-cart__total-price {

border-top: 1px solid #1e1e1e;

padding-top: 1rem;

font-weight: 700;

opacity: 0;

} .dragster-temp {

transform: translateY(1rem);

}

There is a couple of things worth to pay attention on. The first thing is a usage of system fonts to display text in the prototype. The font displayed in our prototype will differs depending on the operating system a user is using when playing around with our shopping cart. Recently, it became a trend on technical websites to use it. The usage of system fonts has been applied using the following CSS code sample: font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;

The next thing worth to pay attention on is a layout built using Flexbox CSS system. The layout has been built by applying display: flex; to the container and properties like: flex: 1 1 25% to the items list container.

DragsterJS implementation

DragsterJS has a lot of features, like a possibility of doing extra actions while dragging and dropping items, updating items containers’ height, cloning elements, etc.

The extra actions can be applied using callbacks provided within the DragsterJS instance configuration.

The first thing you have to do is to create an instance of DragsterJS and provide a required configuration object. If you don’t provide any configuration, a DragsterJS instance will take default values.

For the sake of this prototype we will implement DragsterJS the following way:

const dragster = new Dragster({

// Drag'n'drop regions selector

regionSelector: '.region',

// Draggable elements CSS selector

elementSelector: '.region--drag-only .item',

// CSS class name of region where elements can be only dragged from

// It will be impossible to drop any elements there

dragOnlyRegionCssClass: 'region--drag-only',

// clone elements flag; informs whether elements should be cloned after dropping it

cloneElements: true,

// a callback to be invoked when any element is dropped onto a drop region

onAfterDragDrop: afterDropCallback

});

I believe the code explanation made in code comments is sufficient enough. I explained there, why each property has been used. Because of them, our prototype will start working.

The interesting part of the configuration is a usage of: cloneElements: true and dragOnlyRegionCssClass: 'region--drag-only' properties.

When they are both applied, then you are able to define which elements will be cloned after dropping and you are able to define which regions contain only drag-only elements. It means, than you will not be able to drop any elements in the defined region.

The full code sample required for our prototype to work is as follows:

(function() {

const droppedItems = {};

const dropzone = document.querySelector('.shopping-cart__dropzone');

const itemsList = document.querySelector('.shopping-cart__items');

const totalPrice = document.querySelector('.shopping-cart__total-price');

const ITEM_SELECTOR = '.item';

const updateTotalPriceLabel = () => {

let total = 0;



Object.keys(droppedItems).forEach(key => {

const item = droppedItems[key];



total = total + (item.count * parseFloat(item.price));

});



totalPrice.innerHTML = `$${total.toFixed(2)}`;

totalPrice.style.opacity = '1';

};

const renderItems = () => {

let fragment = document.createDocumentFragment();



Object.keys(droppedItems).forEach(key => {

const item = droppedItems[key];

let element = document.createElement('li');



element.innerHTML = `${item.name} (${item.count}) - $${item.count * parseFloat(item.price)}`;



fragment.append(element);

});



itemsList.innerHTML = '';

itemsList.append(fragment);

};

const clearDropzone = () => dropzone.innerHTML = '';

const updateItemsData = (items) => {

items.forEach(item => {

if (droppedItems[item.id]) {

droppedItems[item.id].count = droppedItems[item.id].count + 1;

} else {

droppedItems[item.id] = item;

}

});

};

const createItemsListItem = (element) => {

return {

id: element.dataset.id,

name: element.dataset.name,

price: element.dataset.price,

count: 1

};

};

const afterDropCallback = () => {

updateItemsData([...dropzone.querySelectorAll(ITEM_SELECTOR)].map(createItemsListItem));

clearDropzone();

renderItems();

updateTotalPriceLabel();

};

const dragster = new Dragster({

regionSelector: '.region',

elementSelector: '.region--drag-only .item',

dragOnlyRegionCssClass: 'region--drag-only',

cloneElements: true,

onAfterDragDrop: afterDropCallback

});

})();

Summary

I hope I inspired you to start using DragsterJS: the drag’n’drop interface library in your projects. When you use the library you are able to build rich user interfaces that work no matter the device or screen resolution.

The DragsterJS library was built to allow implementing interfaces for mobile devices, tablets and desktops. It will just works.

In case of having any suggestions or improvement proposals don’t hesitate to create a new issue in the Issues tab on Github or just create a pull request to DragsterJS repository.

I also write a blog in Polish where you can find a lot of tutorials and articles about web development. You might need some kind of translator, but the code examples there are self-explanatory.