JSFiddle - React, Tailwind, and code Playground
HTML
<article class="layout">
<div class="shop">
<h1>Our articles</h1>
<p>Articles will be added to favorite list ordered by last added.</p>
<ul>
<li id="22">
<span>Article 1 (id: 22)</span>
<button class="shop-item__favorite-button" data-add-to-favorite="22" data-favorite-title="Meredith dress">
<span class="sr-only"
data-favorite="Add to my favorites"
data-favorite-added="Remove from my favorites"></span>
</button>
</li>
<li id="23">
<span>Article 2 (id: 23)</span>
<button class="shop-item__favorite-button" data-add-to-favorite="23" data-favorite-title="Meredith Skirt">
<span class="sr-only"
data-favorite="Add to my favorites"
data-favorite-added="Remove from my favorites"></span>
</button>
</li>
<li id="24">
<span>Article 3 (id: 24)</span>
<button class="shop-item__favorite-button" data-add-to-favorite="24" data-favorite-title="Josephine short">
<span class="sr-only"
data-favorite="Add to my favorites"
data-favorite-added="Remove from my favorites"></span>
</button>
</li>
<li id="25">
<span>Article 4 (id: 25)</span>
<button class="shop-item__favorite-button" data-add-to-favorite="25" data-favorite-title="Josephine shirt">
<span class="sr-only"
data-favorite="Add to my favorites"
data-favorite-added="Remove from my favorites"></span>
</button>
</li>
<li><span>To force scroll</span></li>
<li><span>To force scroll</span></li>
<li><span>To force scroll</span></li>
<li><span>To force scroll</span></li>
<li><span>To force scroll</span></li>
<li><span>To force scroll</span></li>
<li><span>To force scroll</span></li>
<li><span>To force scroll</span></li>
<li><span>To force...
JavaScript
const favoriteButtonAttr = 'data-add-to-favorite';
const isFavorite = 'data-is-favorite';
const listSelector = '[data-my-favorites]';
class FavoritesList {
constructor () {
this.storageName = 'favoritesList';
this.list = this.initList();
}
initList () {
if (window.localStorage.getItem(this.storageName)) {
// todo: repetitive parse?
const list = JSON.parse(window.localStorage.getItem(this.storageName));
updateHtmlList(list);
return list;
} else {
return [];
}
}
initButton(button) {
const id = parseInt(button.getAttribute(favoriteButtonAttr));
button.addEventListener('click', (event) => {
const button = event.target;
!inArray(id, this.list) ? this.list.push(id) : removeFromArray(id, this.list);
setState(id, this.list);
this.updateList();
})
function setState (id, list) {
return button.toggleAttribute(isFavorite, inArray(id, list));
}
setState(id, this.list);
return button;
}
updateList() {
setLocalStorage(this.storageName, this.list);
updateHtmlList(this.list);
}
}
function scrollTo(animate, to, duration, extraOffset, doneCallback) {
// When should we finish?
var finishAt = Date.now() + duration;
// Start
requestAnimationFrame(tick);
function tick() {
// How many frames left? (60fps = 16.6ms per frame)
var framesLeft = (finishAt - Date.now()) / 16.6;
// How far do we have to go?
var distance = to.getBoundingClientRect().top + (extraOffset || 0);
if (distance == 0) {
// Done (this shouldn't happen, belt & braces)
return;
}
// Adjust by one frame's worth
if (framesLeft <= 1) {
// Last call
animate.scrollTop += distance;
if (doneCallback)
doneCallback(to);
} else {
// Not the last, adjust and schedule next
var dir = distance < 0 ? -1 : 1;
animate.scrollTop += dir * Math.max(1, dir *...