Skip to content

Instantly share code, notes, and snippets.

@markhillard
Last active December 15, 2021 01:08
Show Gist options
  • Save markhillard/067ac4a3b1f1cc7a0c635b6616b8dfc6 to your computer and use it in GitHub Desktop.
Save markhillard/067ac4a3b1f1cc7a0c635b6616b8dfc6 to your computer and use it in GitHub Desktop.
Etsy Shop

Etsy Shop

This is an Etsy shop featuring a responsive, column-based "masonry" layout written in pure CSS. Listings and pagination are handled via jQuery with an AJAX call to the Etsy API returning JSONP. In this example, I'm using a publicly available key to grab all active listings.

Simply update the AJAX url and api_key to pull listings from your own Etsy shop and display them on your website.

Docs: https://www.etsy.com/developers/documentation/

A Pen by Mark Hillard on CodePen.

License.

<div class="progress"></div>
<div class="wrapper">
<div class="loading"></div>
<div class="page">
<a href="#" class="prev"><span class="fa fa-chevron-left"></span></a>
<a href="#" class="set">1</a>
<a href="#" class="next" ><span class="fa fa-chevron-right"></span></a>
</div>
<div class="item-list"></div>
</div>
$(document).ready(function () {
// etsy variables
var url = 'https://openapi.etsy.com/v2/listings/active.js?includes=MainImage',
key = '59fzg9jg3iweatl909c85auj'; // update to your own API key
// pagination variables
var set,
page,
prev,
next,
limit = 15;
// get data
function getData(page) {
$.ajax({
url: url,
data: {
api_key: key,
limit: limit,
offset: page
},
dataType: 'jsonp',
success: function (data) {
if (data.ok) {
if (data.count > 0) {
// if hidden listings exist... *
if ($('.item.hidden').length > 0) {
// * remove hidden listings
$('.item.hidden').remove();
}
// first page
if (typeof page === 'undefined' || page < limit) {
$('.prev').hide();
} else {
$('.prev').show();
}
// pagination
set = data.pagination.effective_page;
prev = data.pagination.next_offset - limit - limit;
next = data.pagination.next_offset;
// for each listing... *
for (var i = 0; i < data.results.length; i++) {
// * assign listing variables
var item = data.results[i];
var url = item.url;
var image = item.MainImage.url_570xN;
var title = item.title;
var price = item.price;
var quantity = item.quantity;
// * indicate quantity level
var level;
if (quantity <= 10) {
level = 'low';
} else if (quantity > 10 && quantity <= 20) {
level = 'medium';
} else if (quantity > 20) {
level = 'high';
}
// * check for title character length and add ellipses
if (title.length > 50) {
title = title.substring(0, 50) + '...';
}
// * build html structure for each listing
var listing = $('<div class="item">\
<div class="item-image">\
<a href="' + url + '"><img src="' + image + '"/></a>\
</div>\
<div class="item-details">\
<p class="item-title">' + title + '</p>\
<p class="item-price">$' + price + '</p>\
<p class="item-quantity level-' + level + '">' + quantity + ' left</p>\
</div>\
</div>');
// * append listings and hide them
$('.item-list').append(listing).css({
'opacity': '0',
'transition': 'none'
});
// * load images
loadImages();
}
} else {
console.log('no results');
}
} else {
console.log(data.error);
}
}
});
}
// load images
function loadImages() {
// assign image variables
var images = $('.item img');
var count = images.length;
// initialize dynamic image loading
$(images).imagesLoaded().always(function (instance) {
// assign set
$('.set').text(set);
}).done(function (instance) {
// hide loading spinner
$('.loading').hide();
// fade in listings
$('.item-list').css({
'opacity': '1',
'transition': 'opacity 300ms ease'
});
}).fail(function () {
console.log('all images loaded, at least one is broken');
}).progress(function (instance, image) {
// triggered after each image has been loaded
if (image.isLoaded) {
$(image.img).addClass('loaded').delay(600).css('opacity', '1');
var loaded = $('.item-list img.loaded').length;
var width = 100 * (loaded / count);
$('.progress').css('width', width + '%');
$('.loading').show();
}
});
}
// pagination
$('.page a').on('click', function (e) {
// prevent default action
e.preventDefault();
// fade out and remove listings
$('.item-list').css({
'opacity': '0',
'transition': 'opacity 300ms ease'
}).delay(300).queue(function () {
$('.item').addClass('hidden');
$(this).dequeue();
});
// show loading spinner
$('.loading').show();
// reset progress bar width
$('.progress').css('width', '0');
// assign pagination variable
if ($(this).hasClass('prev')) {
page = prev;
} else if ($(this).hasClass('next')) {
page = next;
}
// get data
getData(page);
});
// get data on first load
getData(page);
});
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.imagesloaded/4.1.4/imagesloaded.pkgd.min.js"></script>
/* Global Styles */
@import url("//fonts.googleapis.com/css?family=Work+Sans:300,400,600");
html,body {
background:#e9e9e9;
color:#333;
font-family:"Work Sans", sans-serif;
line-height:1.4;
height:100%;
width:100%;
}
*,::before,::after {
box-sizing:border-box;
}
:focus {
outline:none;
}
.wrapper {
margin:0 auto;
padding:1rem 0;
width:calc(100% - 2rem);
}
/* Progress Bar */
.progress {
background:#31708f;
height:10px;
max-width:100%;
position:absolute;
top:0;
transition:width 300ms ease;
width:0;
}
/* Spinner */
@keyframes spin {
0% { transform:rotate(0deg); }
100% { transform:rotate(360deg); }
}
.loading {
animation:spin 1s infinite linear;
border-radius:50%;
border:.25rem solid rgba(255,255,255,.5);
border-top-color:#31708f;
height:30px;
position:absolute;
top:calc(1rem + 10px);
width:30px;
}
/* Pagination */
.page {
font-size:0;
position:relative;
text-align:right;
top:5px;
}
.page a {
color:#31708f;
display:inline-block;
font-size:2.025rem;
text-decoration:none;
transition:color 300ms ease;
}
.page a:hover {
color:#333;
}
.page .prev {
display:none;
}
.page .set {
font-weight:600;
margin:0 .5rem;
pointer-events:none;
position:relative;
top:-1px;
}
/* Shop Styles */
.item-list {
column-gap:1rem;
margin:1rem 0 0;
padding:0;
}
.item {
background-color:#fff;
border-radius:2px;
box-shadow:2px 2px 4px 0 #d1d1d1;
display:inline-block;
margin:0 0 1rem;
padding:1rem;
position:relative;
width:100%;
}
.item.hidden {
visibility:hidden;
}
.item-image {
background-color:#e9e9e9;
}
.item-image img {
border-radius:2px;
display:block;
height:auto;
opacity:0;
transition:opacity 1200ms ease;
width:100%;
}
.item-title {
font-size:.85rem;
margin:.5rem .25rem;
word-wrap:break-word;
}
.item-price {
color:#3c763d;
display:inline-block;
font-size:1.2rem;
font-weight:600;
margin:.5rem .25rem 0;
line-height:1;
}
.item-quantity {
border-radius:2px;
bottom:1rem;
color:#fff;
font-size:.75rem;
font-weight:600;
padding:.25rem .5rem;
position:absolute;
right:1rem;
}
/* Quantity Level */
[class^="level-"] {
color:#fff;
}
.level-low { background-color:#a94442; }
.level-medium { background-color:#8a6d3b; }
.level-high { background-color:#3c763d; }
/* Media Queries */
@media only screen and (min-width:480px) {
.item-list {
column-count:2;
}
}
@media only screen and (min-width:700px) {
.item-list {
column-count:3;
}
}
@media only screen and (min-width:900px) {
.item-list {
column-count:4;
}
}
@media only screen and (min-width:1200px) {
.item-list {
column-count:5;
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment