Skip to content

Commit e983277

Browse files
authored
Merge pull request #143 from khushi-purwar/lgm21
Blog Application
2 parents b49e944 + ef58771 commit e983277

File tree

8 files changed

+394
-0
lines changed

8 files changed

+394
-0
lines changed

Blog Application/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Blog Application
2+
3+
## Description
4+
Blog Application is an application where user can add a new blog, edit it, delete it as well as view other blogs and make changes in them.
5+
6+
Application uses LocalStorage, the localStorage object stores data with no expiration date. The data will not be deleted when the browser is closed, and will be available when the user comes again.
7+
8+
## Stacks Used
9+
* HTML & CSS
10+
* Bootstrap - A CSS Framework
11+
* JavaScript -> LocalStorage
12+
13+
## Use of Project
14+
15+
To understand the concept of the localstorage, how localstorage persist the data, even the tab is closed or user refersh the page.
16+
17+
## ScreenShot
18+
19+
<img src="https://github.com/khushi-purwar/Web-dev-mini-projects/blob/lgm21/Blog%20Application/Screenshots/ss1.png" />
20+
21+
Click on the `Add New`, UI looks like:
22+
23+
24+
<img src="https://github.com/khushi-purwar/Web-dev-mini-projects/blob/lgm21/Blog%20Application/Screenshots/ss2.png" />
25+
26+
Click on the `Open Blog`, UI looks like:
27+
28+
29+
<img src="https://github.com/khushi-purwar/Web-dev-mini-projects/blob/lgm21/Blog%20Application/Screenshots/ss3.png" />
30+
31+
## Live Demo
32+
33+
<img src="https://github.com/khushi-purwar/Web-dev-mini-projects/blob/lgm21/Blog%20Application/Screenshots/demo.gif" />

Blog Application/Screenshots/demo.gif

17.7 MB
Loading

Blog Application/Screenshots/ss1.png

390 KB
Loading

Blog Application/Screenshots/ss2.png

294 KB
Loading

Blog Application/Screenshots/ss3.png

630 KB
Loading

Blog Application/index.html

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
<!-- google fonts -->
9+
<link rel="preconnect" href="https://fonts.gstatic.com" />
10+
<link href="https://fonts.googleapis.com/css2?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&display=swap" rel="stylesheet" />
11+
<!-- icons -->
12+
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous" />
13+
<!-- bootstrap cdn -->
14+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous" />
15+
<!-- our stylesheet -->
16+
<link rel="stylesheet" href="style.css">
17+
<title>Blog App</title>
18+
</head>
19+
20+
<body onload="loadData()">
21+
<!--Navbar Modal -->
22+
<div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
23+
<div class="modal-dialog">
24+
<div class="modal-content">
25+
<div class="modal-header">
26+
<h5 class="modal-title" id="staticBackdropLabel">Add New Task</h5>
27+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
28+
</div>
29+
<div class="modal-body">
30+
<form>
31+
<div class="mb-3">
32+
<label for="imageurl" class="form-label">Image URL</label>
33+
<input type="url" class="form-control" id="imageurl" aria-describedby="emailHelp" placeholder="https://images.hello.com/hello.png">
34+
</div>
35+
<div class="mb-3">
36+
<label for="title" class="form-label">Title</label>
37+
<input type="text" class="form-control" id="title" placeholder="Artificial Intelligence">
38+
</div>
39+
<div class="mb-3">
40+
<label for="type" class="form-label">Type</label>
41+
<input type="text" class="form-control" id="type" placeholder="AI">
42+
</div>
43+
<div class="mb-3">
44+
<label for="description" class="form-label">Description</label>
45+
<textarea rows="4" class="form-control" id="description"></textarea>
46+
</div>
47+
</form>
48+
</div>
49+
<div class="modal-footer">
50+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
51+
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="saveChanges()">Save Changes</button>
52+
</div>
53+
</div>
54+
</div>
55+
</div>
56+
<!-- card modal -->
57+
<div class="modal fade" id="showblog" tabindex="-1" aria-labelledby="showTaskLabel" aria-hidden="true">
58+
<div class="modal-dialog modal-lg">
59+
<div class="modal-content">
60+
<div class="modal-body blog__modal__body"></div>
61+
<div class="modal-footer">
62+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
63+
</div>
64+
</div>
65+
</div>
66+
</div>
67+
</div>
68+
<!-- navbar -->
69+
<nav class="navbar navbar-expand-md navbar-light bg-light shadow-sm">
70+
<div class="container-fluid"> <a class="navbar-brand fw-bold text-primary" href="#">Blog</a>
71+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span>
72+
</button>
73+
<div class="collapse navbar-collapse" id="navbarSupportedContent">
74+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
75+
<li class="nav-item"> <a class="nav-link active" aria-current="page" href="#">Home</a>
76+
</li>
77+
</ul>
78+
<button type="button" class="btn btn-primary rounded-pill" data-bs-toggle="modal" data-bs-target="#staticBackdrop"><i class="fas fa-plus"></i> Add New</button>
79+
</div>
80+
</div>
81+
</nav>
82+
<div class="container">
83+
<section>
84+
<div class="row blog__container mt-5 mb-3 ">
85+
</div>
86+
</section>
87+
</div>
88+
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script>
89+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-Atwg2Pkwv9vp0ygtn1JAojH0nYbwNJLPhwyoVbhoPwBhjQPR5VtM2+xf0Uwh9KtT" crossorigin="anonymous"></script>
90+
<script src="script.js"></script>
91+
</body>
92+
</html>
93+
94+

Blog Application/script.js

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
// targeting the parent element
2+
const blogContainer = document.querySelector('.blog__container');
3+
const blogModal = document.querySelector(".blog__modal__body");
4+
// global
5+
let globalStore = [];
6+
7+
// -----------------------------------------------------
8+
// a function for creating a new card
9+
const newCard = ({
10+
id,
11+
imageUrl,
12+
blogTitle,
13+
blogType,
14+
blogDescription
15+
}) => `<div class="col-lg-4 col-md-6" id=${id}>
16+
<div class="card m-2">
17+
<div class="card-header d-flex justify-content-end gap-2">
18+
<button type="button" class="btn btn-outline-success" id="${id}" onclick="editCard.apply(this, arguments)"><i class="fas fa-pencil-alt" id="${id}" onclick="editCard.apply(this, arguments)"></i></button>
19+
<button type="button" class="btn btn-outline-danger" id="${id}" onclick="deleteCard.apply(this, arguments)"><i class="fas fa-trash-alt" id="${id}" onclick="deleteCard.apply(this, arguments)"></i></button>
20+
</div>
21+
<img
22+
src=${imageUrl}
23+
class="card-img-top" alt="...">
24+
<div class="card-body">
25+
<h5 class="card-title">${blogTitle}</h5>
26+
<p class="card-text">${blogDescription}</p>
27+
<span class="badge bg-primary">${blogType}</span>
28+
</div>
29+
<div class="card-footer text-muted">
30+
<button type="button" id="${id}" class="btn btn-outline-primary float-end" data-bs-toggle="modal"
31+
data-bs-target="#showblog" onclick="openBlog.apply(this, arguments)">Open Blog</button>
32+
</div>
33+
</div>
34+
</div>`;
35+
36+
// --------------------------------------------------
37+
const loadData = () => {
38+
39+
// access localstorage
40+
// localStorage.getItem("blog") === localStorage.blog
41+
const getInitialData = localStorage.blog; // if null, then
42+
if (!getInitialData) return;
43+
44+
// convert stringified-object to object
45+
const {
46+
cards
47+
} = JSON.parse(getInitialData);
48+
49+
// map around the array to generate HTML card and inject it to DOM
50+
cards.map((blogObject) => {
51+
const createNewBlog = newCard(blogObject);
52+
blogContainer.insertAdjacentHTML("beforeend", createNewBlog);
53+
globalStore.push(blogObject);
54+
});
55+
};
56+
57+
const updateLocalStorage = () => {
58+
localStorage.setItem("blog", JSON.stringify({
59+
cards: globalStore
60+
}))
61+
}
62+
63+
// function for save changes----------------------------------------
64+
65+
// create a function which will trigerred on clicking on save changes in the modal
66+
const saveChanges = () => {
67+
const blogData = {
68+
id: `${Date.now()}`, // generating a unique id for each card
69+
imageUrl: document.getElementById('imageurl').value,
70+
blogTitle: document.getElementById('title').value,
71+
blogType: document.getElementById('type').value,
72+
blogDescription: document.getElementById('description').value
73+
};
74+
75+
const createNewBlog = newCard(blogData);
76+
blogContainer.insertAdjacentHTML("beforeend", createNewBlog);
77+
78+
globalStore.push(blogData);
79+
80+
// API -> add t localStorage
81+
updateLocalStorage()
82+
// provide some unique identification, i.e key, here key is "blog",
83+
84+
};
85+
86+
// function for deleting a card -------------------
87+
88+
const deleteCard = (event) => {
89+
// id
90+
event = window.event;
91+
const targetID = event.target.id;
92+
const tagname = event.target.tagName; // BUTTON OR I
93+
94+
// assign the same id of card to button also
95+
96+
// search the globalStore, remove the object which matches with the id
97+
globalStore = globalStore.filter((blogObject) => blogObject.id !== targetID);
98+
99+
updateLocalStorage();
100+
101+
// access DOM to remove them
102+
103+
if (tagname === "BUTTON") {
104+
// task__container
105+
return blogContainer.removeChild(
106+
event.target.parentNode.parentNode.parentNode // col-lg-4
107+
);
108+
}
109+
110+
// else
111+
// blog__container
112+
return blogContainer.removeChild(
113+
event.target.parentNode.parentNode.parentNode.parentNode // col-lg-4
114+
);
115+
};
116+
117+
// function for editing
118+
const editCard = (event) => {
119+
120+
event = window.event;
121+
const targetID = event.target.id;
122+
const tagname = event.target.tagName;
123+
124+
let parentElement;
125+
if (tagname === "BUTTON") {
126+
parentElement = event.target.parentNode.parentNode;
127+
128+
} else {
129+
parentElement = event.target.parentNode.parentNode.parentNode;
130+
}
131+
132+
let blogTitle = parentElement.childNodes[5].childNodes[1];
133+
let blogDescription = parentElement.childNodes[5].childNodes[3];
134+
let blogType = parentElement.childNodes[5].childNodes[5];
135+
let submitBtn = parentElement.childNodes[7].childNodes[1];
136+
// console.log(taskTitle, taskDescription, taskType);
137+
138+
// setAttributes
139+
blogTitle.setAttribute("contenteditable", "true");
140+
141+
blogDescription.setAttribute("contenteditable", "true");
142+
blogType.setAttribute("contenteditable", "true");
143+
submitBtn.setAttribute(
144+
"onclick",
145+
"saveEditChanges.apply(this, arguments)"
146+
);
147+
submitBtn.innerHTML = "Save Changes";
148+
149+
// modal removed
150+
submitBtn.removeAttribute("data-bs-toggle");
151+
submitBtn.removeAttribute("data-bs-target");
152+
153+
}
154+
155+
const saveEditChanges = (event) => {
156+
157+
event = window.event;
158+
const targetID = event.target.id;
159+
const tagname = event.target.tagName;
160+
161+
let parentElement;
162+
if (tagname === "BUTTON") {
163+
parentElement = event.target.parentNode.parentNode;
164+
165+
} else {
166+
parentElement = event.target.parentNode.parentNode.parentNode;
167+
}
168+
169+
let blogTitle = parentElement.childNodes[5].childNodes[1];
170+
let blogDescription = parentElement.childNodes[5].childNodes[3];
171+
let blogType = parentElement.childNodes[5].childNodes[5];
172+
let submitBtn = parentElement.childNodes[7].childNodes[1];
173+
174+
const updatedData = {
175+
176+
blogTitle: blogTitle.innerHTML,
177+
blogDescription: blogDescription.innerHTML,
178+
blogType: blogType.innerHTML,
179+
}
180+
181+
// console.log(updatedData);
182+
globalStore = globalStore.map((blog) => {
183+
if (blog.id === targetID) {
184+
return {
185+
id: blog.id,
186+
imageUrl: blog.imageUrl,
187+
blogTitle: updatedData.blogTitle,
188+
blogType: updatedData.blogType,
189+
blogDescription: updatedData.blogDescription,
190+
};
191+
}
192+
return blog; // important statement
193+
});
194+
195+
updateLocalStorage();
196+
197+
blogTitle.setAttribute("contenteditable", "false");
198+
199+
blogDescription.setAttribute("contenteditable", "false");
200+
blogType.setAttribute("contenteditable", "false");
201+
202+
// modal added
203+
submitBtn.setAttribute("onclick", "openBlog.apply(this, arguments)");
204+
submitBtn.setAttribute("data-bs-toggle", "modal");
205+
submitBtn.setAttribute("data-bs-target", "#showblog");
206+
207+
submitBtn.innerHTML = "Open Blog";
208+
209+
}
210+
211+
const htmlModalContent = ({
212+
id,
213+
blogTitle,
214+
blogDescription,
215+
imageUrl,
216+
blogType
217+
}) => {
218+
const date = new Date(parseInt(id));
219+
return ` <div id=${id}>
220+
<img
221+
src=${imageUrl}
222+
alt="bg image"
223+
class="img-fluid place__holder__image mb-3 p-4"
224+
/>
225+
<div class="text-sm text-muted ">Created on ${date.toDateString()}</div>
226+
<h2 class="my-5 mt-5" style="display:inline;">${blogTitle}</h2>
227+
<span class="badge bg-primary">${blogType}</span>
228+
<p class="lead mt-2">
229+
${blogDescription}
230+
</p></div>`;
231+
};
232+
233+
const openBlog = (event) => {
234+
235+
event = window.event;
236+
const targetID = event.target.id;
237+
238+
const getBlog = globalStore.filter(({
239+
id
240+
}) => id === targetID);
241+
// console.log(getBlog[0]);
242+
blogModal.innerHTML = htmlModalContent(getBlog[0]);
243+
};
244+
245+

Blog Application/style.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
* {
2+
margin: 0;
3+
padding: 0;
4+
box-sizing: border-box; /* box-model*/
5+
}
6+
7+
body {
8+
font-family: "Poppins", sans-serif;
9+
background-color: #ebf2ff;
10+
}
11+
12+
.card-img-top {
13+
padding: 15px;
14+
height: 300px;
15+
width: auto;
16+
}
17+
18+
.place__holder__image {
19+
width: 100%;
20+
}
21+
22+

0 commit comments

Comments
 (0)