Creating a back button to use in an array - javascript

So I'm currently working on a little project website, which basically has changes the background color when pressing a button. But I am also trying to implement a "back" button to go backwards in the color array which I created and in which all color codes that are being generated will be saved. But as you may see when testing the project the back button doesn't quite work and I get that thats due to the "i" variable not being saved when calling the "prevColor" function´.
function prevColor() {
(i == randomColors.length ? bodyStyle.backgroundColor = randomColors[i-2] : bodyStyle.backgroundColor = randomColors[i-1])
};
This is the part that doesnt work out for me, but feel free to check the whole code on jsfiddle.net through the link provided below, or through the snippet (click to Open).
https://jsfiddle.net/jamal000/kobmajyx/
var button = document.querySelector("#change");
var buttonStyle = button.style;
var bodyStyle = document.querySelector("body").style;
var p = document.querySelector("p");
var i = 0;
var randomColors = [];
var hue;
var HSLcolor;
var back = document.querySelector("#back");
button.addEventListener("click", randomHSL);
back.addEventListener("click", prevColor);
function randomHSL(){
hue = Math.floor((Math.random() * 360) + 1);
HSLcolor = `HSL(${hue}, 80%, 70%)`;
bodyStyle.backgroundColor = HSLcolor;
randomColors.push(`${HSLcolor}`);
p.textContent = "Color: " + randomColors[i];
i++;
p.style.color, buttonStyle.borderColor = `HSL(${hue + 180}, 80%, 70%)`;
p.style.color = buttonStyle.borderColor;
};
function prevColor(){
(i == randomColors.length ? bodyStyle.backgroundColor = randomColors[i-2] : bodyStyle.backgroundColor = randomColors[i-1])
};
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
transition: all 0.5s ease;
}
#change {
height: 40.6px;
width: 200px;
outline: none;
font-family: Raleway;
font-size: 15px;
font-weight: 600;
text-decoration: none;
border: 2px solid darkgrey;
border-radius: 10px;
background-color: #f1f1f1;
color: black;
box-shadow: 5px 7px 40px rgba(0,0,0,0.5);
}
#change:hover {
color: #666666;
box-shadow: 7px 10px 40px rgba(0,0,0,0.4);
transition: all 0.5s ease;
}
:active {
border-color: #666666;
transition: all 0.5s ease;
}
p {
font-family: Raleway;
font-weight: 500;
}
#back {
outline: none;
margin-right: 5px;
background-color: inherit;
border: 2px solid #2f2f2f;
border-radius: 5px;
}
#back:hover {
border-color: #aaa;
transition: all 0.5s ease;
}
#back:active {
transition: all 0.2s ease;
color: #aaa;
background-color: inherit;
}
div {
margin-right: 38.172px;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Color Toggle</title>
<link rel="stylesheet" href="style.css" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Raleway:500" rel="stylesheet">
</head>
<body>
<p>Color: White</p>
<div>
<button id="back"> ◄ </button>
<button id="change">Click for a surprise!!!</button>
</div>
<script src="toggle.js"></script>
</body>
</html>

Firstly I want to clarify the UI
<p id="color"></p>
<p id="current"></p>
<button id="back"> ◀ </button>
<button id="new">Click for a surprise!!!</button>
<button id="next"> ▶ </button>
If we want to go back and forward, let's have separate buttons. And one more button for the surprise that will push new color to the array and set the index to that new color.
Then I would extract all DOM manipulations into a single method, let's call it applyColor. Also, I would have a default color value as the first item of the colors array. And index set to 0 appropriately. So the script should can push new color to the colors array, shift color index and update the UI by the applyColor() calls.
const colors = ['white'];
let index = 0;
function applyColor() {
const color = colors[index];
bodyStyle.backgroundColor = color;
colorElement.textContent = "Color: " + color;
currentElement.textContent = (index + 1) + " / " + colors.length;
}
function generateColor() { // bound with click on "new" button
const hue = Math.floor((Math.random() * 360) + 1);
const HSLcolor = `HSL(${hue}, 80%, 70%)`;
colors.push(HSLcolor);
index = colors.length - 1;
applyColor();
}
function prevColor(){ // bound with click on "back" button
index -= index > 0 ? 1 : 0;
applyColor();
}
function nextColor(){ // bound with click on "next" button
index += index < colors.length - 1 ? 1: 0;
applyColor();
}
applyColor();
Here's the updated demo: https://jsfiddle.net/dhilt/fz2dktxu/36/

You are not updating the i value in the prevColor() method. Try this:
function prevColor() {
if (randomColors.length > 0 && i > 0) {
i--;
bodyStyle.backgroundColor = randomColors[i]
console.log(randomColors, i)
}
};

PLease check the link. I have updated your code by declaring the variables.
var HSLcolor;
var back = document.querySelector("#back");
> var randomColors = [];var i =0;
https://jsfiddle.net/kobmajyx/6/

You have to start separating your presentation function from event handling, processing.
Your code should be something like https://jsfiddle.net/ryx2gn01/19/
you have 2 actions : prevColor and newColor both change the state of the aplication and trigger a change on presentation:
one internal processing function that would be the generation of your color (randomHSL)
one presentation function that sets the values where you want them (setColor)
Now you have an array of hues (randomColors), and when you press "Suprise me" it should add a new hue value to the array and set the current index (variavel i) to the last color (because you want to show the color you just pushed into the array), so i = randomColors.length - 1
Upon pressing "Previous Color" you have 2 cases, your index is already in the beginning of the list (i <= 0) or is not the first (i > 0). In the first case you don't want to change your index, but on the second case you want to decrease by one, there fore the if...then...else you see on the function prevColor. Finally after change the state you call the function responsible for displaying the results setColor(i) passing the current index.
Hope you enjoy my solution, its not perfect but hope it helps you

Related

How to delete unique item from local storage array? (Nothing has worked so far)

I am totally stuck.
I am working on the Library project from The Odin Project's JavaScript course, and I am trying to use localStorage so that the user can save their "library" to, well, their local storage.
I've never been able to 100% successfully use localStorage before. It seems simple enough, but for some reason, there was some kind of block in my brain about how to utilize it correctly. This time, however, I was able to get my program to be able to 1) save the user's data (via a button click), 2) load it correctly into the individual "cards" on the page, and 3) delete the entire storage (via another button click). I am extremely excited about this... it feels so good to not know how to do something and then get it to work.
The one thing that has me totally confused is being able to delete a unique item from the localStorage array. When you click the "New Book" button on my page, a modal comes up, and you're required to enter a book's title, author, # of pages, and whether or not you have read it. There's also an option to set the background color. Clicking Submit will then generate a "card" with the book's information on it.
Each card has an X button in the corner. When the X button is clicked, the card is deleted from the page. I want it so that it (meaning that card's book's object) is also deleted from the local storage array (preferably without the user having to press the "Save to local storage" button again, although it isn't a dealbreaker).
Each time the user creates a new book, the book's details are saved as an object into an array called libraryBooks (which I then stringify so I can use localStorage). From my understanding, in order to be able to delete individual parts of the array, I need to use code that is something like this:
libraryBooks.splice(libraryBooks.indexOf(item), 1);
I think my issue is that I don't know how to get the item's index number. I'm not sure how to make it so that the program follows the instructions "When this X button is clicked, find that book's object in the array" if that makes sense. At this point, I've tried several things, and I'm just totally confused.
So yeah! Any help would be wonderful. I've gone through and commented my code to try to help clarify what everything does. I've included the entire thing so that I don't potentially leave something important out related to my problem, and so that it works in the code snippet. But in case it doesn't, here are links to the code repo and the live demo:
Live demo
Code repo
// Link DOM elements
const newBookBtn = document.querySelector('.newbook'); // New Book button
const showBooks = document.querySelector('.show-books'); // div container
const cardClose = document.querySelectorAll('.cardclose'); // button to close card
const openEls = document.querySelectorAll("[data-open]"); // for popup boxes
const closeEls = document.querySelectorAll("[data-close]"); // for popup boxes
const submitBtn = document.querySelector('.submitbtn'); // Submit button (in popup boxes)
const formBoxes = document.querySelectorAll('.form-box'); // Form box within popup box
const formRadio1 = document.querySelector('#bookreadyes'); // Form radio buttons within popup box
const isVisible = "is-visible"; // for popup boxes
const colorDropdown = document.querySelector('select'); // Color-picking dropdown in popup boxes
let libraryBooks = [];
// localstorage buttons (save and delete)
const saveStorage = document.getElementById('save-storage');
const deleteStorage = document.getElementById('delete-storage');
// Button event listeners
saveStorage.addEventListener('click', updateLocalStorage);
deleteStorage.addEventListener('click', deleteLocalStorage);
submitBtn.addEventListener('click', addBookToLibrary);
// If the 'books' key is empty, simply set libraryBooks to empty array.
if (localStorage.getItem('books') === null) {
libraryBooks = [];
// Otherwise, set library books array to get items from the 'books' key
} else {
const booksFromStorage = JSON.parse(localStorage.getItem('books'));
libraryBooks = booksFromStorage;
}
// The Book constructor
function Book(title, author, pages, read) {
this.title = title
this.author = author
this.pages = pages
this.read = read
this.info = function() {
return `${this.title} by ${this.author}, ${this.pages} pages, ${read}`;
}
}
// Add book to library function.
function addBookToLibrary() {
let bookTitle = document.querySelector('#book-title');
let bookAuthor = document.querySelector('#book-author');
let bookPages = document.querySelector('#book-pages');
let bookReadYes = document.querySelector('#bookreadyes')
let bookReadNo = document.querySelector('#bookreadno');
let alertWords = document.querySelector('.alertwords'); // Alert if form elements are empty
let bookRead;
if (bookReadYes.checked) {
bookRead = 'Read';
} else if (bookReadNo.checked) {
bookRead = 'Not read';
}
// Creating a new book object via the Book constructor
let newBook = new Book(bookTitle.value, bookAuthor.value, bookPages.value, bookRead);
// If any form elements are empty, throw error and don't submit book. If none of them are empty, proceed.
if (bookTitle.value.length === 0 || bookAuthor.value.length === 0 || bookPages.value.length === 0) {
alertWords.textContent = 'Please fill in all fields.';
} else {
alertWords.textContent = '';
document.querySelector('.modal.is-visible').classList.remove(isVisible); // Closes the modal
formBoxes.forEach(formBox => {
formBox.value = ""; // Sets the form values so they're blank the next time the New Book button is pressed
});
formRadio1.checked = true; // Set the radio buttons so that the "Yes" button is automatically selected (otherwise, the user's last choice will be selected)
// Push the new book object into libraryBooks array
libraryBooks.push(newBook);
// The rest of the lines of code in this function create the actual book card on page
const newCard = document.createElement('div');
const newCardTitle = document.createElement('h4');
const newCardAuthor = document.createElement('p');
const newCardPages = document.createElement('p');
const newCardRead = document.createElement('span');
newCardTitle.setAttribute('class', 'title-style');
newCardAuthor.setAttribute('class', 'author-style');
newCardPages.setAttribute('class', 'pages-style');
newCardRead.setAttribute('class', 'read-style');
newCard.classList.add('isVisible', 'cardbox', colorPicker());
showBooks.appendChild(newCard);
for (let i = 0; i < libraryBooks.length; i++) {
newCardTitle.innerHTML = `${libraryBooks[i].title}`;
let closeBtn = "<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>";
newCardTitle.innerHTML += closeBtn;
newCardAuthor.innerHTML = `by ${libraryBooks[i].author}`;
newCardPages.innerHTML = `<strong>Pages</strong>: ${libraryBooks[i].pages}`;
newCardRead.innerHTML = `<strong>Status</strong>: ${libraryBooks[i].read}`;
}
newCard.appendChild(newCardTitle);
newCard.appendChild(newCardAuthor);
newCard.appendChild(newCardPages);
newCard.appendChild(newCardRead);
}
}
// Stuff for popup capability
for (const el of openEls) {
el.addEventListener("click", function() {
const modalId = this.dataset.open;
document.getElementById(modalId).classList.add(isVisible);
});
}
for (const el of closeEls) {
el.addEventListener("click", function() {
this.parentElement.parentElement.parentElement.classList.remove(isVisible);
});
}
document.addEventListener("click", e => {
if (e.target == document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
// Keyboard shortvut for modal: ESC key to close
document.addEventListener("keyup", e => {
// if we press the ESC
if (e.key == "Escape" && document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
cardClose.forEach(card => {
card.addEventListener('click', function() {
this.parentNode.parentNode.removeChild(this.parentNode.parentNode);
return false;
});
})
// Switch function for setting the background color of the book's card
function colorPicker() {
switch (colorDropdown.value) {
case 'red':
return 'cardback-red';
break;
case 'orange':
return 'cardback-orange';
break;
case 'yellow':
return 'cardback-yellow';
break;
case 'green':
return 'cardback-green';
break;
case 'blue':
return 'cardback-blue';
break;
case 'purple':
return 'cardback-purple';
break;
case 'dark':
return 'cardback-dark';
break;
case 'grey':
return 'cardback-grey';
break;
default:
return 'cardback-white';
break;
}
}
// Update local storage
function updateLocalStorage() {
localStorage.setItem('books', JSON.stringify(libraryBooks));
}
// Delete local storage
function deleteLocalStorage() {
window.localStorage.clear();
showBooks.textContent = "";
}
// Get localStorage data and set it to the variable "data"
const data = JSON.parse(localStorage.getItem('books'));
// Load the saved local storage objects into cards (almost identical to addBookToLibrary())
function loadLocalStorage(array, book) {
let bookTitle;
let bookAuthor;
let bookPages;
let bookRead;
for (let i = 0; i < array.length; i++) {
bookTitle = book.title;
bookAuthor = book.author;
bookPages = book.pages;
bookRead = book.read;
}
// Create book card on page
const newCard = document.createElement('div');
const newCardTitle = document.createElement('h4');
const newCardAuthor = document.createElement('p');
const newCardPages = document.createElement('p');
const newCardRead = document.createElement('span');
newCardTitle.setAttribute('class', 'title-style');
newCardAuthor.setAttribute('class', 'author-style');
newCardPages.setAttribute('class', 'pages-style');
newCardRead.setAttribute('class', 'read-style');
newCard.classList.add('isVisible', 'cardbox', colorPicker());
showBooks.appendChild(newCard);
for (let i = 0; i < array.length; i++) {
newCardTitle.innerHTML = `${bookTitle}`;
let closeBtn = "<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>";
newCardTitle.innerHTML += closeBtn;
newCardAuthor.innerHTML = `by ${bookAuthor}`;
newCardPages.innerHTML = `<strong>Pages</strong>: ${bookPages}`;
newCardRead.innerHTML = `<strong>Status</strong>: ${bookRead}`;
}
newCard.appendChild(newCardTitle);
newCard.appendChild(newCardAuthor);
newCard.appendChild(newCardPages);
newCard.appendChild(newCardRead);
}
// Required in order to load saved books onto page
for (let i = 0; i < data.length; i++) {
loadLocalStorage(data, data[i]);
}
* {
margin: 0;
padding: 0;
}
body,
html {
display: flex;
align-items: center;
flex-direction: column;
background-color: #cedee9;
height: 100vh;
padding: 10px;
font-family: 'Rubik', sans-serif;
}
body {
max-height: 100%;
}
input {
padding: 5px;
}
label {
margin-bottom: 5px;
margin-top: 10px;
font-size: .9rem;
}
input[type='text'] {
font-size: .75rem;
font-family: 'Poppins', sans-serif;
}
.radio-option1 {
margin-right: 5px;
}
h4 {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.2rem;
}
.close {
background: none;
font-size: 1.5rem;
border: 0;
margin-left: 10px;
transition: 0.5s ease;
}
.close:hover {
cursor: pointer;
color: rgb(58, 84, 140);
}
.submitbtn {
border: 1px solid rgb(58, 84, 140);
color: white;
background-color: rgb(58, 84, 140);
padding: 8px 20px;
margin: 0 auto;
width: 100%;
border-radius: 5px;
transition: 0.5s ease;
}
.submitbtn:hover {
cursor: pointer;
background-color: rgb(40, 54, 85);
border-color: rgb(40, 54, 85);
}
.book-form {
display: flex;
flex-direction: column;
width: 200px;
}
/* Testing this comment */
input {
margin-bottom: 10px;
}
p {
margin-top: 5px;
}
.bookcard {
width: 200px;
display: flex;
justify-content: flex-start;
flex-direction: column;
}
.cardbox {
background-color: white;
width: 250px;
margin: 10px;
padding: 12px 20px 20px 20px;
box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;
}
.alertwords {
color: #df0a0a;
}
.show-books {
display: flex;
flex-wrap: wrap;
}
.title-style {
margin-bottom: -5px;
}
.author-style {
font-size: .9rem;
margin-bottom: 20px;
}
.pages-style,
.read-style {
font-size: .9rem;
}
option {
font-family: 'Poppins', sans-serif;
font-size: .8rem;
}
select {
padding: 5px;
font-family: 'Poppins', sans-serif;
font-size: .8rem;
}
#option-red,
.cardback-red {
background-color: rgb(241, 191, 191);
}
#option-orange,
.cardback-orange {
background-color: #ffcb9a;
}
#option-yellow,
.cardback-yellow {
background-color: #fffda1;
}
#option-green,
.cardback-green {
background-color: #9cd6af;
}
#option-blue,
.cardback-blue {
background-color: #a1d3f0;
}
#option-purple,
.cardback-purple {
background-color: #e6c1ff;
}
#option-grey,
.cardback-grey {
background-color: #cfcfcf;
}
#option-dark,
.cardback-dark {
background-color: #282f52;
color: white;
}
/* RESET RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
* {
padding: 0;
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
.close-modal {
cursor: pointer;
background: transparent;
border: none;
outline: none;
font-size: inherit;
}
.btn-group {
text-align: center;
}
.open-modal {
font-weight: bold;
background: steelblue;
color: white;
padding: 0.75rem 1.75rem;
margin-bottom: 1rem;
border-radius: 5px;
border: 0;
transition: 0.5s ease;
}
.open-modal:hover {
background-color: rgb(40, 54, 85);
cursor: pointer;
}
.open-modal:active {
background-color: rgb(40, 54, 85);
}
.open-modal:focus {
background-color: rgb(40, 54, 85);
}
/* MODAL
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
background: rgba(0, 0, 0, 0.781);
cursor: pointer;
visibility: hidden;
opacity: 0;
transition: all 0.35s ease-in;
}
.modal.is-visible {
visibility: visible;
opacity: 1;
}
.modal-dialog {
position: relative;
max-width: 800px;
max-height: 80vh;
border-radius: 5px;
background: white;
overflow: auto;
cursor: default;
}
.modal-dialog>* {
padding: 1rem;
}
.modal-header,
.modal-footer {
font-weight: 700;
background: #a8c0f2;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 1.3rem;
}
.modal-header .close-modal {
font-size: 1.5rem;
}
.modal p+p {
margin-top: 1rem;
}
/* ANIMATIONS
–––––––––––––––––––––––––––––––––––––––––––––––––– */
[data-animation] .modal-dialog {
opacity: 0;
transition: all 0.5s var(--bounceEasing);
}
[data-animation].is-visible .modal-dialog {
opacity: 1;
transition-delay: 0.2s;
}
[data-animation="slideInOutDown"] .modal-dialog {
transform: translateY(100%);
}
[data-animation="slideInOutTop"] .modal-dialog {
transform: translateY(-100%);
}
[data-animation="slideInOutLeft"] .modal-dialog {
transform: translateX(-100%);
}
[data-animation="slideInOutRight"] .modal-dialog {
transform: translateX(100%);
}
[data-animation="zoomInOut"] .modal-dialog {
transform: scale(0.2);
}
[data-animation="rotateInOutDown"] .modal-dialog {
transform-origin: top left;
transform: rotate(-1turn);
}
[data-animation="mixInAnimations"].is-visible .modal-dialog {
animation: mixInAnimations 2s 0.2s linear forwards;
}
[data-animation="slideInOutDown"].is-visible .modal-dialog,
[data-animation="slideInOutTop"].is-visible .modal-dialog,
[data-animation="slideInOutLeft"].is-visible .modal-dialog,
[data-animation="slideInOutRight"].is-visible .modal-dialog,
[data-animation="zoomInOut"].is-visible .modal-dialog,
[data-animation="rotateInOutDown"].is-visible .modal-dialog {
transform: none;
}
#keyframes mixInAnimations {
0% {
transform: translateX(-100%);
}
10% {
transform: translateX(0);
}
20% {
transform: rotate(20deg);
}
30% {
transform: rotate(-20deg);
}
40% {
transform: rotate(15deg);
}
50% {
transform: rotate(-15deg);
}
60% {
transform: rotate(10deg);
}
70% {
transform: rotate(-10deg);
}
80% {
transform: rotate(5deg);
}
90% {
transform: rotate(-5deg);
}
100% {
transform: rotate(0deg);
}
}
/* FOOTER
–––––––––––––––––––––––––––––––––––––––––––––––––– */
footer {
display: flex;
align-items: center;
justify-content: center;
}
.page-footer {
position: fixed;
bottom: 0;
}
.page-footer span {
color: #e31b23;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Library</title>
<script src="https://kit.fontawesome.com/2b4114baf6.js" crossorigin="anonymous"></script>
<!-- Google fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Noto+Sans:ital,wght#0,400;0,700;1,400;1,700&family=Open+Sans:ital,wght#0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&family=Poppins:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto+Slab:wght#100;200;300;400;500;600;700;800;900&family=Roboto:ital,wght#0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Rubik:ital,wght#0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Source+Sans+Pro:ital,wght#0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700;1,900&display=swap"
rel="stylesheet">
<!-- CSS Stylesheet -->
<link href="styles.css" rel="stylesheet">
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<h1>Library</h1>
<div class="btn-group">
<button type="button" class="open-modal" data-open="modal1">
New Book
</button>
</div>
<div class="btn-group">
<button class="open-modal" id="save-storage">
Save local storage
</button>
<button class="open-modal" id="delete-storage">
Delete local storage
</button>
</div>
<!-- Library books container -->
<div class="show-books">
</div>
<!-- Modal code (popup box for new book form) -->
<div class="modal" id="modal1" data-animation="slideInOutLeft">
<div class="modal-dialog">
<header class="modal-header">
Book Details
<button class="close-modal" aria-label="close modal" data-close>
✕
</button>
</header>
<section class="modal-content">
<form class="bookcard">
<label>Title:</label>
<input type="text" class="form-box" id="book-title" placeholder="Fight Club">
<label>Author:</label>
<input type="text" class="form-box" id="book-author" placeholder="Chuck Palahniuk">
<label># of Pages:</label>
<input type="number" class="form-box" id="book-pages" placeholder="208" min="1" max="14000">
<label>Have you read this book?:</label>
<div class="radiobox">
<label class="radio-option1"><input type="radio" name="read" value="yes" id="bookreadyes" class='form-radio' checked> Yes</label>
<label class="radio-option2"><input type="radio" name="read" value="no" id="bookreadno" class='form-radio'> No</label>
</div>
<label>Select card color (optional):</label>
<select class="colorpicker">
<option value='default' selected disabled>Please select one</option>
<option value="white">White (Default)</option>
<option value="blue" id="option-blue">Blue</option>
<option value="purple" id="option-purple">Purple</option>
<option value="green" id="option-green">Green</option>
<option value="grey" id="option-grey">Grey</option>
<option value="red" id="option-red">Red</option>
<option value="orange" id="option-orange">Orange</option>
<option value="yellow" id="option-yellow">Yellow</option>
<option value="dark" id="option-dark">Dark mode</option>
</select>
<p class="alertwords"></p>
</form>
</section>
<footer class="modal-footer">
<button class="submitbtn">Submit</button>
</footer>
</div>
</div>
<footer class="page-footer">
<small>Made with <span>❤</span> by Sara Dunlop
</small>
</footer>
<!-- JS script -->
<script src="script2.js"></script>
</body>
</html>
Well, I made a good couple changes to your script and it works
The main problem was that your close button wasn't given a listener that would be able to remove the book it's related to, solved that and also for fun added logic to automatically save to localStorage
The code's below but there's also a link to a working example
// Link DOM elements
const newBookBtn = document.querySelector('.newbook'); // New Book button
const showBooks = document.querySelector('.show-books'); // div container
const cardClose = document.querySelectorAll('.cardclose'); // button to close card
const openEls = document.querySelectorAll("[data-open]"); // for popup boxes
const closeEls = document.querySelectorAll("[data-close]"); // for popup boxes
const submitBtn = document.querySelector('.submitbtn'); // Submit button (in popup boxes)
const formBoxes = document.querySelectorAll('.form-box'); // Form box within popup box
const formRadio1 = document.querySelector('#bookreadyes'); // Form radio buttons within popup box
const isVisible = "is-visible"; // for popup boxes
const colorDropdown = document.querySelector('select'); // Color-picking dropdown in popup boxes
let libraryBooks = [], automaticallyUpdate = true; //change automaticallyUpdate to false to prevent automatic saving(on changes)
// localstorage buttons (save and delete)
const saveStorage = document.getElementById('save-storage');
const deleteStorage = document.getElementById('delete-storage');
// Button event listeners
saveStorage.addEventListener('click', updateLocalStorage);
deleteStorage.addEventListener('click', deleteLocalStorage);
submitBtn.addEventListener('click', addBookToLibrary);
// The Book constructor
function Book({title, author, pages, read, color}) {
this.title = String(title)
this.author = String(author)
this.pages = Number(pages)
this.read = read?"Read":"Not Read"
this.color = String(color)
this.info = function() {
return `${this.title} by ${this.author}, ${this.pages} pages, ${this.read} with color ${this.color}`;
}
}
//returns your close button with a listener that actually removes the book
//"<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>";
function closeBar(book){
let btn=document.createElement('button')
btn.className='close-default'
btn.innerHTML='x' //wow almost forgot this
btn.addEventListener('click',()=>{
$(btn).parent().parent().remove()
console.log(book,libraryBooks,libraryBooks.indexOf(book))
libraryBooks.splice(libraryBooks.indexOf(book),1)
if(automaticallyUpdate){updateLocalStorage()} //automatic saving
})
return btn //the button is returned to be placed in its arrangement
}
// Add book to library function.
function addBookToLibrary() {
let color = colorDropdown.value;
let bookTitle = document.querySelector('#book-title');
let bookAuthor = document.querySelector('#book-author');
let bookPages = document.querySelector('#book-pages');
let bookRead = document.querySelector('#bookreadyes').checked; //true if checked, false if not checked
let alertWords = document.querySelector('.alertwords'); // Alert if form elements are empty
// Creating a new book object via the Book constructor
let newBook = new Book({title:bookTitle.value, author:bookAuthor.value, pages:bookPages.value, read:bookRead, color});
// If any form elements are empty, throw error and don't submit book. If none of them are empty, proceed.
if (bookTitle.value.length === 0 || bookAuthor.value.length === 0 || bookPages.value.length === 0) {
alertWords.textContent = 'Please fill in all fields.';
} else {
alertWords.textContent = '';
document.querySelector('.modal.is-visible').classList.remove(isVisible); // Closes the modal
formBoxes.forEach(formBox => {
formBox.value = ""; // Sets the form values so they're blank the next time the New Book button is pressed
});
formRadio1.checked = true; // Set the radio buttons so that the "Yes" button is automatically selected (otherwise, the user's last choice will be selected)
// Push the new book object into libraryBooks array
libraryBooks.push(newBook);
// The rest of the lines of code in this function create the actual book card on page
const newCard = document.createElement('div');
const newCardTitle = document.createElement('h4');
const newCardAuthor = document.createElement('p');
const newCardPages = document.createElement('p');
const newCardRead = document.createElement('span');
newCardTitle.setAttribute('class', 'title-style');
newCardAuthor.setAttribute('class', 'author-style');
newCardPages.setAttribute('class', 'pages-style');
newCardRead.setAttribute('class', 'read-style');
newCard.classList.add('isVisible', 'cardbox', colorPicker(newBook.color));
showBooks.appendChild(newCard);
const {title,author,pages,read}=newBook
newCardTitle.innerHTML = `${title}`;
let closeBtn = closeBar(newBook)
newCardTitle.appendChild(closeBtn);
newCardAuthor.innerHTML = `by ${author}`;
newCardPages.innerHTML = `<strong>Pages</strong>: ${pages}`;
newCardRead.innerHTML = `<strong>Status</strong>: ${read}`;
newCard.appendChild(newCardTitle);
newCard.appendChild(newCardAuthor);
newCard.appendChild(newCardPages);
newCard.appendChild(newCardRead);
if(automaticallyUpdate){updateLocalStorage()} //automatic saving
}
}
// Stuff for popup capability
for (const el of openEls) {
el.addEventListener("click", function() {
const modalId = this.dataset.open;
document.getElementById(modalId).classList.add(isVisible);
});
}
for (const el of closeEls) {
el.addEventListener("click", function() {
this.parentElement.parentElement.parentElement.classList.remove(isVisible);
});
}
document.addEventListener("click", e => {
if (e.target == document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
// Keyboard shortvut for modal: ESC key to close
document.addEventListener("keyup", e => {
// if we press the ESC
if (e.key == "Escape" && document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
cardClose.forEach(card => {
card.addEventListener('click', function() {
this.parentNode.parentNode.removeChild(this.parentNode.parentNode);
return false;
});
})
// Switch function for setting the background color of the book's card
let colors = {red:1, orange:1, yellow:1, green:1, blue:1, purple:1, dark:1, grey:1}
//the above variable saves a lot of lines in the colorPicker function
function colorPicker(color) {
if(!colors[color]){return 'cardback-white'}
return 'cardback-'+color
}
// Update local storage
function updateLocalStorage() {
localStorage.setItem('books', JSON.stringify(libraryBooks));
}
// Delete local storage
function deleteLocalStorage() {
window.localStorage.clear();
showBooks.textContent = "";
}
// Get localStorage data and set it to the variable "data"
const data = JSON.parse(localStorage.getItem('books'))||[]
// the "||" in case there was nothing stored yet and it prevents an error from reading map from null
.map(book=>new Book( book )) //convert localStorage data to a list of "Book"s
// Load the saved local storage objects into cards (almost identical to addBookToLibrary())
function loadLocalStorage({title,author,pages,read,color}) {
// EDIT: the for loops in this function are seemingly useless
var newBook=arguments[0]
libraryBooks.push(newBook)
// Create book card on page
const newCard = document.createElement('div');
const newCardTitle = document.createElement('h4');
const newCardAuthor = document.createElement('p');
const newCardPages = document.createElement('p');
const newCardRead = document.createElement('span');
newCardTitle.setAttribute('class', 'title-style');
newCardAuthor.setAttribute('class', 'author-style');
newCardPages.setAttribute('class', 'pages-style');
newCardRead.setAttribute('class', 'read-style');
newCard.classList.add('isVisible', 'cardbox', colorPicker(color));
showBooks.appendChild(newCard);
newCardTitle.innerHTML = `${title}`;
const closeBtn = closeBar(newBook);
newCardTitle.appendChild(closeBtn);
newCardAuthor.innerHTML = `by ${author}`;
newCardPages.innerHTML = `<strong>Pages</strong>: ${pages}`;
newCardRead.innerHTML = `<strong>Status</strong>: ${read}`;
newCard.appendChild(newCardTitle);
newCard.appendChild(newCardAuthor);
newCard.appendChild(newCardPages);
newCard.appendChild(newCardRead);
}
// Required in order to load saved books onto page
for(let i = 0; i < data.length; i++) {
loadLocalStorage(data[i]);
}
Here is the simple fix for you
let closeBtn = `<button type='button' class='close-default' onclick='libraryBooks.splice(libraryBooks.findIndex((book) => book.title === "${bookTitle}" && book.author === "${bookAuthor}"), 1);'>x</button>`;
i is getting from your array list
Just in case you don't understand what is template literals, I share the link here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
I also added an extra fix for your deleteLocalStorage. You need to clean up your libraryBooks array too.
function deleteLocalStorage() {
libraryBooks = [] //here is the fix to clean up your array
window.localStorage.clear();
showBooks.textContent = "";
}
You can check my fixes here
https://jsfiddle.net/otywdekm/4/
I checked out your demo , and what I think you can do is, while saving a book object you can pass in an property called id to identify the exact book from the collection.
So when you click on the x button you will get the details of that book which now has an id and you can filter it out.
Something as follows -
const filteredBooks = existingBooks.filter((el) => el.id !== book.id);
localstorage.setItem('books', filteredBooks);
Hope that works for you.
You Problem in delete specific book card
libraryBooks.splice(libraryBooks.indexOf(item), 1);
Thats not correct because
indexOf() compares searchElement to elements of the Array using strict
equality (the same method used by the ===, or triple-equals,
operator).
libraryBooks.indexOf always results in -1
the solution you can add index when you create book in book constructor and increment it in every book
[
{
index: 0
author: "00"
pages: "200"
read: "Read"
title: "00"
}
]
when create new object set index++ to able get the object directly
and when delete
function deleteBook(book){
libraryBooks.splice(book.index , 1)
}

'backgroundColor' not working with javascript

I'm creating a tab menu like this:
function clear_selected() //sets all columns color black
{
var parent = document.querySelector("#container")
var items = document.querySelectorAll(".item")
var n = items.length;
for (var i = 0; i < n; i++)
items[i].style.backgroundColor = "";
}
function plus(itself) //adds another column
{
var parent = itself.parentElement;
var n = parent.childElementCount;
clear_selected();
var n = parent.querySelectorAll(".item").length;
var page = document.createElement("button");
page.className = "item";
page.style.backgroundColor = "blue";
page.textContent = "column"
page.onclick = function() {
clear_selected();
this.style.backgroundColor = "blue";
};
var temp = document.createElement("span");
temp.className = "del"
temp.innerHTML = "×"
temp.onclick = function() { //it's suppose to remove a column and color default as blue
document.querySelector("#main_item").style.backgroundColor = "blue" //THIS LINE ISN'T WORKING
this.parentElement.remove();
};
page.appendChild(temp);
parent.insertBefore(page, parent.childNodes[n]);
}
function see(el) {
clear_selected();
el.style.backgroundColor = "blue";
}
#container {
display: flex;
width: 100%;
height: 50px;
text-align: center;
background-color: yellow;
}
.item {
background-color: black;
color: white;
border: none;
outline: none;
cursor: pointer;
margin: 0.1rem;
padding: 0.1rem;
max-width: 100%;
}
.del {
background-color: red;
display: inline-block;
cursor: pointer;
border-radius: 50%;
width: 0.7rem;
margin-left: 2rem;
}
<div id="container">
<button class="item" id="main_item" style="background-color:blue;" onclick="see(this)">default column </button>
<button class="item" onclick="plus(this)">+</button>
</div>
but when I press the 'x' to remove a column, I want the default column to color blue, but the line of code which is suppose to achieve that isn't working
document.querySelector("#main_item").style.backgroundColor = "blue"
Before pressing 'x':
After pressing 'x' on the last column:
What it SHOULD look like:
I've losing sleep over this, can someone PLEASE tell me why isn't it working?
When you click on the "X", both of your onclick handlers are getting called, including the one that runs clear_selected, which sets the background color to "".
You can fix this by using stopPropagation on the event passed into the onclick function for the "x". That will stop the click event from going up the chain to the parent element of the "x".
temp.onclick = function(e) {
document.querySelector("#main_item").style.backgroundColor = "blue"
this.parentElement.remove();
e.stopPropagation();
};

How to use getElementsByClassName() for adding both grids and display one after another?

I Have Tried to add grid-2 when we press a key, but it's not working as I expected.
When it is displaying grid-2 I want the grid to disappear (I don't know how to do).
(background):-
I have searched and found there is a visibility property in CSS, but don't know how to apply to the whole grid and undo the visibility property and make them visible.
I have tried to add the grid-2 by getElementById but both the grids are appearing at a time.
(don't know how to make them appear one after another).
let curr_div_on = 0,curr_div_off = 0;
const key = document.getElementsByClassName('keys');
function setPlayingOn() {
key[curr_div_on % 4].classList.add("playing");
curr_div_on = (curr_div_on + 1) % 4;
}
function setPlayingOff() {
key[curr_div_off % 4].classList.remove("playing");
curr_div_off = (curr_div_off + 1) % 4;
}
setInterval(setPlayingOn, 500);
setTimeout(() => setInterval(setPlayingOff, 500), 500);
document.addEventListener('keypress', function(){
if(curr_div_on ==1){
var element = document.getElementsByClassName("grid-2");
element.classList.add("grid");
}
})
.grid{
display: grid;
grid-template-columns: auto auto;
grid-gap:10px;
}
.key{
padding: 20px;
border: 1px solid;
background-color: #2196F3;
text-align:center;
}
.playing{
transform: scale(1,1);
border-color: #ffc600;
box-shadow: 0 0 1rem #ffc600;
}
<html>
<div class='grid'>
<div class='key'>ABCD</div>
<div class='key'>EFGH</div>
<div class='key'>IJKL</div>
<div class='key'>LMNO</div>
</div>
<div class='grid-2'>
<div class='button'>A</div>
<div class='button'>B</div>
<div class='button'>C</div>
<div class='button'>D</div>
</div>
</html>
I think the code is wrong in these two points:
const key = document.getElementsByClassName('keys'); --> The selector should be 'key'
var element = document.getElementsByClassName("grid-2"); --> The result is a list (DomTokenList), so you should take the first element (var element = document.getElementsByClassName("grid-2")[0];)

changing button color with javascript

i want my button to change color on click [suppose black],but on page refresh i dont want it to change its color back to original color.
<html>
<head>
<style>
.button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
</style>
</head>
<body>
<h2>CSS Buttons</h2>
<input type="button" class="button" value="Input Button">
</body>
</html>
<html>
<head>
<script>
/* On load of the page we are calling this function which will
give us the color name stored in our local storage */
function changeColor() {
var color = localStorage.getItem("colorName");
document.getElementById("color").style.backgroundColor = color;
}
/*This function will change the color of button on click*/
var i = -1;
function colorArray() {
var arr = new Array();
arr[0] = "Red";
arr[1] = "Orange";
arr[2] = "Green";
arr[3] = "Blue";
i++;
if (i > arr.length - 1) {
i = 0;
}
/* We have an array with the color names, you can add any
number of colors in this array */
/* After fetching the color from array we are storing
it in the local storage of our browser for future use*/
localStorage.setItem("colorName", arr[i]);
document.getElementById("color").style.backgroundColor = arr[i];
}
</script>
</head>
<body onload="changeColor()">
<button onclick="colorArray()" id="color"> Change Color </button>
</body>
</html>
Click here for Live Demo and complete source code
You can use local storage to preserve the color after the page refresh.
window.onload = function(){
var btn = document.getElementById("btn");
if(localStorage["bg-color"]){
btn.style.backgroundColor = localStorage.getItem("bg-color");
};
btn.onclick = function(){
if(!localStorage["bg-color"]){
localStorage.setItem("bg-color", "yellow");
};
btn.style.backgroundColor = localStorage.getItem("bg-color");
};
// remove from storage localStorage.removeItem("bg-color")
};
When you get an element with JavaScript, you can access a bunch of properties of this element.
There is two solutions to modify his properties :
modify the css property (myElement.style.myProperty = 'myNewValue')
modify the class list (myElement.classList.add('myClassName'))
Update after author edit :
You could call a function onclick="..." on the button.
The function will add a class to this button to change his style.
Then store on your localStorage, or on a session, or on a cookie or in your database the state of the button with a boolean (clicked = true for example).
Next time you visit the page, add a check onload to add this class on this button if clicked == true.
You could use cookies to store the state of the color between page reloads, here is a basic example.
function setColor(color){
document.querySelector('#button').style.background = color
document.cookie = `color=${color};`
}
function getColor() {
return document.cookie.split('; ') // ['color=blue']
.reduce((acc, x) => {
const [ key, value ] = x.split('=') // ['color', 'blue']
acc[key] = value // { color: 'blue' }
return acc
})
.color
}
// run the functions on page load
window.addEventLister('DOMContentLoaded', function(){
setColor(getColor())
})
setColor('#bada55')
.button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
<h2>CSS Buttons</h2>
<input id="button" type="button" class="button" value="Input Button">

How to do automoving from one page to another page and back to first page where pagination is there in table in html

I am trying to add automoving with pagination for table in HTML.
For eg i have table with 100 rows and then i am doing pagination for it with 10 records on one page.So there will be 10 pages with 10 records each.
I nedd to do it automatically so that user dont have to specifically click on the page and see.
I used paging.js
with below code
<style type="text/css">
.paging-nav {
text-align: right;
padding-top: 2px;
}
.paging-nav a {
margin: auto 1px;
text-decoration: none;
display: inline-block;
padding: 1px 7px;
background: #91b9e6;
color: white;
border-radius: 3px;
}
.paging-nav .selected-page {
background: #187ed5;
font-weight: bold;
}
.paging-nav,
#tableData {
'width: 400px;
margin: 0 auto;
font-family: Arial, sans-serif;
}
</style>
$(document).ready(function() {
$('#file').paging({limit:10});
});
Here file is id of table which is to be paginated. How can i go about changing page of table automatically? All answers are welcome.
Here is an example for how to start with it..
beware this is just an example, do not copy! for your own protection
//create
el = $("<table></table>");
for (var i = 1; i !== 100; i++) {
el.append("<tr><td>" + i + "</td><td>buuurp</td></tr>");
}
el.data("intStart", 1);
el.appendTo("body");
//fuction
function showRow(j = 10) { //default var j = 10; you can call showRow(50) for 50 as example
var i = $("table").data().intStart; //grabs data that hold the number of currect first element to view
$("tr").hide();
// hides all
$("tr").filter(function(index) {
return index >= i - 1 && index < i + j - 1; //jquery has 0-9 we count in 1-10 so -1
}).show(); //shows the one needed
$("table").data().intStart += j; //addes 'j' to the counter
if ($("table").data().intStart > $("table").children("tr").lenght) {
clearInterval(timer); //if we reach the end we can kill this
}
}
showRow();
var timer = setInterval(function() {
showRow();
}, 3000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Categories

Resources