Hey guys in this post, we will create a Javascript flashcard project for beginners. Follow this tutorial till the end to understand the javascript concepts. we will create this project step by step. The final version of the app is live on the internet.
Table of Contents
Topics covered
This application involves the following topics –
- Object-Oriented JavaScript
- JavaScript Constructor Functions
- JavaScript Prototypal Inheritance
- JavaScript CSS Manipulation
- JavaScript DOM Manipulation
Complete source code
The below image shows you the project structure –
Add styles to the application
Create a main.css
file and add the following content –
:root {
--mainWhite: #f5f5f5;
--mainDark: #333333;
--mainOrange: #ff8552;
--mainGreen: #297373;
}
body {
background: var(--mainWhite);
color: var(--mainDark);
}
.show-btn {
border-color: var(--mainOrange);
color: var(--mainOrange);
background: transparent;
}
.show-btn:hover {
background: var(--mainOrange);
color: var(--mainDark);
}
.question-card {
display: none;
position: relative;
background: transparent;
border-color: var(--mainOrange);
}
.feedback {
display: none;
}
textarea {
border-radius: 0.25rem;
border-color: var(--mainOrange);
}
.close-btn {
position: absolute;
top: 0;
right: 2%;
font-size: 2rem;
color: var(--mainOrange);
cursor: pointer;
}
.close-btn:hover {
color: var(--mainGreen);
}
.submitBtn {
border: 0.2rem solid var(--mainGreen);
color: var(--mainGreen);
background: transparent;
}
.submitBtn:hover {
background: var(--mainGreen);
color: var(--mainWhite);
}
.edit-flashcard {
border: 0.2rem solid var(--mainGreen);
color: var(--mainGreen);
background: transparent;
}
.edit-flashcard:hover {
background: var(--mainGreen);
color: var(--mainDark);
}
.delete-flashcard {
border: 0.2rem solid var(--mainOrange);
color: var(--mainOrange);
background: transparent;
}
.delete-flashcard:hover {
background: var(--mainOrange);
color: var(--mainDark);
}
.answer {
display: none;
}
.showItem {
display: block;
}
Add scripts to the application
Create an app.js
file and add the following content –
//event listeners - will be invoked after DOM Content is loaded
function eventListeners(){
const showBtn = document.getElementById("show-btn");
const questionCard = document.querySelector(".question-card");
const closeBtn = document.querySelector(".close-btn");
const form = document.getElementById("question-form");
const feedback = document.querySelector(".feedback");
const questionInput = document.getElementById("question-input");
const answerInput = document.getElementById("answer-input");
const questionList = document.getElementById("questions-list");
//let data = [];
let id;
//new ui instance
const ui = new UI();
//retrieve questions from local storage
let data = ui.retrieveLocalStorgage();
if (data.length > 0){
id = (data[(data.length-1)].id)+1;
} else {
id = 1;
}
data.forEach(function(question){
ui.addQuestion(questionList, question);
})
//show question form
showBtn.addEventListener('click', function(){
ui.showQuestion(questionCard);
});
//hide question form
closeBtn.addEventListener('click', function(){
ui.hideQuestion(questionCard);
});
//add question
form.addEventListener('submit', function(event){
event.preventDefault();
const questionValue = questionInput.value;
const answerValue = answerInput.value;
if(questionValue === '' || answerValue === ''){
feedback.classList.add('showItem', 'alert-danger');
feedback.textContent = 'cannot add empty values';
setTimeout(function(){
feedback.classList.remove('alert-danger', 'showItem');
}, 3000)
} else {
const question = new Question(id, questionValue, answerValue);
data.push(question);
ui.addToLocalStorage(data);
id++;
ui.addQuestion(questionList, question)
ui.clearFields(questionInput, answerInput);
}
});
//work with a question
questionList.addEventListener('click', function(event){
event.preventDefault();
if(event.target.classList.contains('delete-flashcard')){
let id = event.target.dataset.id;
questionList.removeChild(event.target.parentElement.parentElement.parentElement);
// rest of data
let tempData = data.filter(function(item){
return item.id !== parseInt(id);
});
data = tempData;
ui.addToLocalStorage(data);
} else if (event.target.classList.contains('show-answer')){
event.target.nextElementSibling.classList.toggle('showItem');
} else if (event.target.classList.contains('edit-flashcard')){
//delete question from DOM
let id = event.target.dataset.id;
questionList.removeChild(event.target.parentElement.parentElement.parentElement);
//show question in question card
ui.showQuestion(questionCard);
//find specific question clicked
const tempQuestion = data.filter(function(item){
return item.id === parseInt(id);
});
// rest of data
let tempData = data.filter(function(item){
return item.id !== parseInt(id);
});
data = tempData;
questionInput.value = tempQuestion[0].title;
questionInput.value = tempQuestion[0].answer;
}
});
}
//Contructor function responsible for the display
function UI(){
//show question card
UI.prototype.showQuestion = function(element){
element.classList.add('showItem');
}
//hide question card
UI.prototype.hideQuestion = function(element){
element.classList.remove('showItem');
}
//add question
UI.prototype.addQuestion = function(element, question){
const div = document.createElement('div');
div.classList.add('col-md-4');
div.innerHTML = `<div class="card card-body flashcard my-3">
<h4 class="text-capitalize">${question.title}</h4>
<a href="#" class="text-capitalize my-3 show-answer">Show/Hide Answer</a>
<h5 class="answer mb-3">${question.answer}</h5>
<div class="flashcard-btn d-flex justify-content-between">
<a href="#" id="edit-flashcard" class=" btn my-1 edit-flashcard text-uppercase" data-id="${question.id}">edit</a>
<a href="#" id="delete-flashcard" class=" btn my-1 delete-flashcard text-uppercase" data-id="${question.id}">delete</a>
</div>
</div>`;
element.appendChild(div);
}
//add to Local Storage
UI.prototype.addToLocalStorage = function(data){
localStorage.clear();
const dataJSON = JSON.stringify(data);
localStorage.setItem('flash-questions', dataJSON)
}
//retrieve from localStorage
UI.prototype.retrieveLocalStorgage = function(){
let savedQuestions = localStorage.getItem('flash-questions');
if (savedQuestions){
const savedQuestionsParsed = JSON.parse(savedQuestions);
return savedQuestionsParsed;
} else {
return savedQuestions = [];
}
}
//clear fields
UI.prototype.clearFields = function(question, answer){
question.value = '';
answer.value = '';
}
}
//Constructor function responsible for each question
function Question(id, title, answer){
this.id = id;
this.title = title;
this.answer = answer;
}
// dom event listener to run when content is loaded
document.addEventListener('DOMContentLoaded', function(){
eventListeners();
})
Add HTML to the application
Create an index.html
file and add the following content –
<!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">
<!-- bootstrap css -->
<link rel="stylesheet" href="./css/bootstrap.min.css">
<!-- main css -->
<link rel="stylesheet" href="./css/main.css">
<!-- google fonts -->
<link href="https://fonts.googleapis.com/css?family=Courgette" rel="stylesheet">
<!-- font awesome -->
<link rel="stylesheet" href="./css/all.css">
<title>Flashcard Project</title>
<style>
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-11 col-lg-6 my-3">
<h3 class="text-capitalize">flashcards</h3>
<button class="btn text-capitalize my-2 show-btn" id="show-btn">add question</button>
<div class="card card-body my-3 question-card">
<!-- close btn -->
<a href="#" class="close-btn mt-0">
<i class="fas fa-window-close"></i>
</a>
<!-- end of close btn -->
<div class="feedback alert w-75 text-capitalize">
customer feedback
</div>
<form id="question-form">
<!-- single input -->
<h5 class="text-capitalize">question</h5>
<div class="form-group">
<textarea class="w-100" id="question-input" rows="3"></textarea>
</div>
<!-- end of single input -->
<!-- single input -->
<h5 class="text-capitalize">answer</h5>
<div class="form-group">
<textarea class="w-100" id="answer-input" rows="3"></textarea>
</div>
<!-- end of single input -->
<button type="submit" class="btn submitBtn text-capitalize w-50">save</button>
</form>
</div>
</div>
</div>
<div class="row px-2" id="questions-list">
<!-- <div class="col-md-4"> -->
<!--Template for card data-->
<!-- <div class="card card-body flashcard my-3">
<h4 class="text-capitalize">question title?</h4>
<a href="#" class="text-capitalize my-3 show-answer">show/hide answer</a>
<h5 class="answer mb-3">question answer</h5>
<div class="flashcard-btn d-flex justify-content-between">
<a href="#" id="edit-flashcard" class=" btn my-1 edit-flashcard text-uppercase" data-id="">edit</a>
<a href="#" id="delete-flashcard" class=" btn my-1 delete-flashcard text-uppercase">delete</a>
</div> -->
</div>
</div>
</div>
</div>
<!-- jquery -->
<script src="./js/jquery-3.3.1.min.js"></script>
<!-- bootstrap js -->
<script src="./js/bootstrap.bundle.min.js"></script>
<!-- script js -->
<script src="./js/app.js"></script>
</body>
</html>
Screenshots
Download the complete source code from github repository
Original source https://jsbeginners.com/flashcard-javascript-oop-project/
That’s it for this post. Hope you liked it, if you did then please share this with your friends and colleagues. Also, share this post in your social media profiles. Thanks, I will see you in the next post.