我正在使用香草JS,CSS,HTML创建一个简单的抽认卡应用。 作为一个JS菜鸟,尝试进行基本的CRUD时会受到DOM操作的打击(我获取了Rails API的持久性)。
我正在使用console.log和调试器盘旋,试图了解我的应用程序流程。不刷新浏览器会出现这些问题。创建时,将显示3个额外的“ fantom”卡。在编辑时也会遇到麻烦,因为它还会创建新卡。我还试图维护一个allFlashcard
数组。我的困惑是:a)如何使用一个功能来渲染新的或更新的卡,以及b)如何使用相同的表单进行创建和编辑。
代码-
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="src/style.css">
<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" />
<title>CrashCard</title>
</head>
<body>
<!-- header -->
<header>
<h1>Crash Card</h1>
<button id="modal-btn" class="button">Create Card</button>
</header>
<!-- modal -->
<div id="my-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
<!-- form -->
<form class="ui form" id="form">
<div class="field">
<label>Card Front</label>
<input type="text" name="card-front" placeholder="Card Front" id="card-front" value="">
</div>
<div class="field">
<label>Card Back</label>
<input type="text" name="card-back" placeholder="Card Back" id="card-back" value="">
</div>
<div class="field">
</div>
<button class="ui button" id="submit" type="submit">Create</button>
</form>
</div>
</div>
</div>
<!-- card -->
<div id="card-container">
</div>
<script type="text/javascript" src="src/script.js"></script>
</body>
</html>
CSS:
.container {
width: 200px;
height: 260px;
margin: 5px;
padding: 3px;
perspective: 800px;
display: inline-flex;
}
:root {
--modal-duration: 1s;
--modal-color: #b4bdc5;
}
/* body {
font-family: Arial, Helvetica, sans-serif;
background: #f4f4f4;
font-size: 17px;
line-height: 1.6;
display: flex;
height: 100vh;
align-items: center;
justify-content: center;
} */
.button {
background: #428bca;
padding: 1em 2em;
color: #fff;
border: 0;
border-radius: 5px;
cursor: pointer;
}
.button:hover {
background: #3876ac;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
height: 100%;
width: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
margin: 10% auto;
width: 60%;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.17);
animation-name: modalopen;
animation-duration: var(--modal-duration);
}
.modal-header h2,
.modal-footer h3 {
margin: 0;
}
.modal-header {
background: var(--modal-color);
padding: 15px;
color: #fff;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.modal-body {
padding: 10px 20px;
background: #fff;
}
.modal-footer {
background: var(--modal-color);
padding: 10px;
color: #fff;
text-align: center;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.close {
color: #ccc;
float: right;
font-size: 30px;
color: #fff;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
@keyframes modalopen {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
header {
display: flex;
align-items: center;
justify-content: center;
align-content: center;
text-align: center;
}
/* .sticky {
position: sticky;
top: 0;
background-color: white;
display: block;
z-index: 1;
} */
/* .parallax { */
/* The image used */
/* background-image: url("https://images.unsplash.com/photo-1556745750-68295fefafc5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=967&q=80 */
/* https://images.unsplash.com/photo-1556745750-68295fefafc5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=967&q=80 */
/*
/* Set a specific height */
/* min-height: 300px; */
/* Create the parallax scrolling effect */
/* background-attachment: fixed;
background-position: center;
background-repeat: no-repeat;
background-size:auto; */
/* } */
#card-container {
margin-top: 2em;
padding: 5%;
height: 100vh;
overflow-y: scoll;
}
.card {
justify-items: center;
padding: 2px;
width: 100%;
height: 100%;
transform-style: preserve-3d;
transition: transform 1s;
color: white;
position: relative;
}
.card h2 {
text-align: center;
margin: 0;
display: block;
width: 100%;
height: 100%;
backface-visibility: hidden;
position: absolute;
}
.card h4 {
text-align: center;
position: relative;
margin: 0;
display: block;
width: 100%;
height: 100%;
backface-visibility: hidden;
position: absolute;
overflow-y: scroll ;
}
.card .front {
display: flex;
align-items: center;
justify-content: center;
background: black;
color: white;
text-align: center;
z-index: 1;
}
.card .back {
background: rgb(241, 241, 248);
transform: rotateY( 180deg);
color: black;
text-align: center;
z-index: 2;
display:
}
.card.flipped {
transform: rotateY( 180deg);
}
Javascript:
document.addEventListener('DOMContentLoaded', () => {
console.log("DOM is loaded");
// set variables and DOM elements
let allFlashcards = [];
const form = document.getElementById('form');
const card = document.querySelector('#card-container');
const modal = document.querySelector('#my-modal');
const modalBtn = document.querySelector('#modal-btn');
const closeBtn = document.querySelector('.close');
// fetch all cards
fetch('http://localhost:9000/api/v1/flashcards')
.then(res => res.json())
.then(flashcards => {
allFlashcards = flashcards
renderCards()
});
// render cards to DOM
const renderCards = () => {
console.log("in rendercard")
return allFlashcards.map(c => {
return card.innerHTML += `
<div class="container">
<div class="card" data-card=${c.id}>
<i class="black delete icon" id="delete-card"></i>
<i class="black edit icon" id="edit-card"></i>
<h2 class="front" data-id=${c.id}>${c.body_front}</h>
<h4 class="back">${c.body_back}</h4>
</div>
</div>
`
})
};
// listen for card click and flip
card.addEventListener('click', e => {
if (e.target.dataset.id) {
return e.target.parentNode.classList.toggle('flipped')
} else if (Array.from(e.target.classList).includes('back')) {
return e.target.parentNode.classList.toggle('flipped')
}
});
// ****** MODAL SETUP ********
// Modal Events
modalBtn.addEventListener('click', openModal);
closeBtn.addEventListener('click', closeModal);
window.addEventListener('click', outsideClick);
// Open
function openModal() {
modal.style.display = 'block';
createCard()
}
// Close
function closeModal() {
modal.style.display = 'none';
}
// Close If Outside Click
function outsideClick(e) {
if (e.target == modal) {
modal.style.display = 'none';
}
}
// create new card
const createCard = () => {
console.log("in create")
form.addEventListener('submit', (e) => {
e.preventDefault();
debugger
console.log("I was cliked too")
const cardFront = document.getElementById('card-front').value
const cardBack = document.getElementById('card-back').value
fetch('http://localhost:9000/api/v1/flashcards', {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify({
body_front: cardFront,
body_back: cardBack
})
})
.then(res => res.json())
.then(newCard => {
allFlashcards.push(newCard)
closeModal()
})
});
};
// delete cards
card.addEventListener('click', e => {
const clickedCard = e.target.parentElement
const foundCard = allFlashcards.find(fcard => {
return fcard.id == clickedCard.dataset.card
})
console.log(foundCard)
if (e.target.id == "delete-card") {
console.log(card.parentElement)
card.lastElementChild.remove(clickedCard)
confirm("Do you want to delete this card?")
fetch(`http://localhost:9000/api/v1/flashcards/${foundCard.id}`, {
method: "DELETE"
})
originalCardIndex = allFlashcards.indexOf(foundCard)
allFlashcards.splice(originalCardIndex, 1)
console.log("in Delete:", allFlashcards)
};
});
// edit cards
card.addEventListener('click', e => {
if (e.target.id == "edit-card") {
openModal()
editCard()
}
});
const editCard = () => {
console.log("in editcard")
const cardFront = card.querySelector('.front').innerText
const cardBack = card.querySelector('.back').innerText
const clickedCard = e.target.parentElement
const foundCard = allFlashcards.find(fcard => {
return fcard.id == clickedCard.dataset.card
})
document.getElementById('card-front').value = cardFront
document.getElementById('card-back').value = cardBack
let submitBtn = document.getElementById('submit')
submitBtn.innerText = "Edit"
form.addEventListener('submit', e => {
e.preventDefault()
const editedCardFront = document.getElementById('card-front').value
const editedCardBack = document.getElementById('card-back').value
fetch(`http://localhost:9000/api/v1/flashcards/${foundCard.id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify({
body_front: editedCardFront,
body_back: editedCardBack
})
})
.then(res => res.json())
.then(editedCard => {
originalCardIndex = allFlashcards.indexOf(foundCard)
allFlashcards.splice(originalCardIndex, 1, editedCard)
console.log("in Edit:", allFlashcards)
});
});
};
}); // end DOMContentLoaded
总的来说,我希望获得一些有关如何正确配置此配置的建议。