Skip to content

Instantly share code, notes, and snippets.

@iancover
Last active February 5, 2022 12:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iancover/b0b4337aad9f4373c4d361b75be73f6c to your computer and use it in GitHub Desktop.
Save iancover/b0b4337aad9f4373c4d361b75be73f6c to your computer and use it in GitHub Desktop.
Shopping List example
// **** GLOBAL STATE OBJECT ****
var state = {
items: [],
};
// **** HTML TO RENDER ****
var listItemTemplate =
'<li>' +
'<span class="shopping-item js-shopping-item"></span>' +
'<div class="shopping-items-controls">' +
'<button class="js-shopping-item-toggle">' +
'<span class="button-label">Check</span>' +
'</button>' +
'<button class="js-shopping-item-delete">' +
'<span class="button-label">Delete</span>' +
'</button>' +
'</div>' +
'</li>';
// **** STATE MGMT ****
function addItem(state, item) {
state.items.push({
displayName: item,
checkedOff: false,
});
}
function getItem(state, itemIndex) {
return state.items[itemIndex];
}
function deleteItem(state, itemIndex) {
state.items.splice(itemIndex, 1);
}
function updateItem(state, itemIndex) {
state.items[itemIndex] = newItemState;
}
// **** DOM MANIPULATION ****
function renderItem(item, itemId, itemTemplate, itemDataAttr) {
var element = $(itemTemplate);
element.find('.js-shopping-item').text(item.displayName);
if (item.checkedOff) {
element.find('.js-shopping-item').addClass('shopping-item_checked');
}
element.find('.js-shopping-item-toggle');
element.attr(itemDataAttr, itemId);
return element;
}
function renderList(state, listElement, itemDataAttr) {
var itemsHTML = state.items.map(function (item, index) {
return renderItem(item, index, listItemTemplate, itemDataAttr);
});
listElement.html(itemsHTML);
}
// **** LISTENERS ****
function handleItemAdds(
formElement,
newItemIdentifier,
itemDataAttr,
listElement,
state
) {
formElement.submit(function (event) {
event.preventDefault();
var newItem = formElement.find(newItemIdentifier).val();
addItem(state, newItem);
renderList(state, listElement, itemDataAttr);
this.reset();
});
}
function handleItemDeletes(
formElement,
removeIdentifier,
itemDataAttr,
listElement,
state
) {
listElement.on('click', removeIdentifier, function (event) {
var itemIndex = parseInt($(this).closest('li').attr(itemDataAttr));
deleteItem(state, itemIndex);
renderList(state, listElement, itemDataAttr);
});
}
function handleItemToggles(
listElement,
toggleIdentifier,
itemDataAttr,
state
) {
listElement.on('click', toggleIdentifier, function (event) {
var itemId = $(event.currentTarget.closest('li')).attr(itemDataAttr);
var oldItem = getItem(state, itemId);
updateItem(state, itemId, {
displayName: oldItem.displayName,
checkedOff: !oldItem.checkedOff,
});
renderList(state, listElement, itemDataAttr);
});
}
// **** CONTROLLER ****
$(function () {
var formElement = $('#js-shopping-list-form');
var listElement = $('.js-shopping-list');
var newItemIdentifier = '#js-new-item';
var removeIdentifier = '.js-shopping-item-delete';
var itemDataAttr = 'data-list-item-id';
var toggleIdentifier = '.js-shopping-item-toggle';
handleItemAdds(
formElement,
newItemIdentifier,
itemDataAttr,
listElement,
state
);
handleItemDeletes(
formElement,
removeIdentifier,
itemDataAttr,
listElement,
state
);
handleItemToggles(listElement, toggleIdentifier, itemDataAttr, state);
});
<!DOCTYPE html>
<html lang="en">
<head>
<title>Shopping List</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.2.0/normalize.min.css">
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link rel="stylesheet" href="main.css">
</head>
<body>
<div class="container">
<h1>Shopping List</h1>
<form id="js-shopping-list-form">
<label for="shopping-list-entry">Add an item</label>
<input type="text" name="shopping-list-entry" id="shopping-list-entry" placeholder="e.g., broccoli">
<button type="submit">Add item</button>
</form>
<ul class="shopping-list">
</ul>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
* {
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
}
button,
input[type='text'] {
padding: 5px;
}
button:hover {
cursor: pointer;
}
#shopping-list-item {
width: 250px;
}
.container {
max-width: 600px;
margin: 0 auto;
}
.shopping-list {
list-style: none;
padding-left: 0;
}
.shopping-list > li {
margin-bottom: 20px;
border: 1px solid grey;
padding: 20px;
}
.shopping-item {
display: block;
color: grey;
font-style: italic;
font-size: 20px;
margin-bottom: 15px;
}
.shopping-item__checked {
text-decoration: line-through;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment