Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions README.md

This file was deleted.

41 changes: 41 additions & 0 deletions buildMethods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { getFormInput } from './formMethods.js';

export function buildConst(eventTarget, template) {
const formEl = eventTarget.closest('form');
const obj = getFormInput(formEl);
if (obj) {
const el = buildFragment(template);
return { formEl, obj, el };
}
}

export function buildFragment(arrayObj) {
const fr = new DocumentFragment();
arrayObj.forEach((obj) => {
fr.appendChild(buildElement(obj));
});
return fr;

function buildElement(obj) {
const el = document.createElement(obj.type);
if (obj.attributes) {
for (const key in obj.attributes) {
const a = document.createAttribute(key);
a.value = obj.attributes[key];
el.setAttributeNode(a);
}
}
if (obj.children) {
if (typeof obj.children[0] === 'string') {
const newContent = document.createTextNode(obj.children);
el.appendChild(newContent);
} else {
obj.children.forEach((element) => {
const child = buildElement(element);
el.appendChild(child);
});
}
}
return el;
}
}
21 changes: 21 additions & 0 deletions commentMethods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { buildConst } from './buildMethods.js';

export function addComment(eventTarget) {
const p = buildConst(eventTarget, [{ type: 'p' }]);
if (p) {
const { formEl, obj, el } = p;
el.querySelector('p').innerText = `${obj.comment} - Posted by: ${obj.name}`;
formEl.insertBefore(el, formEl.firstChild);
}
}

export function showComments(eventTarget) {
const cl = eventTarget.nextSibling.classList;
if (cl.contains('d-none')) {
cl.remove('d-none');
eventTarget.innerText = 'Hide Comments';
} else {
cl.add('d-none');
eventTarget.innerText = 'Show Comments';
}
}
73 changes: 73 additions & 0 deletions commentTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
export const commentTemplate = [
{
type: 'form',
attributes: { class: 'mt-2 d-none needs-validation comment-form' },
children: [
{
type: 'div',
attributes: { class: 'mb-2' },
children: [
{
type: 'input',
attributes: {
type: 'text',
name: 'name',
placeholder: 'Name',
class: 'form-control',
required: true,
},
},
{
type: 'div',
attributes: {
class: 'invalid-feedback',
},
children: ['Please provide a name.'],
},
{
type: 'div',
attributes: {
class: 'valid-feedback',
},
children: ['Looks good.'],
},
],
},
{
type: 'div',
attributes: { class: 'mb-2' },
children: [
{
type: 'input',
attributes: {
type: 'text',
name: 'comment',
placeholder: 'Comment',
class: 'form-control',
required: true,
},
},
{
type: 'div',
attributes: {
class: 'invalid-feedback',
},
children: ['Please provide a comment.'],
},
{
type: 'div',
attributes: {
class: 'valid-feedback',
},
children: ['Looks good.'],
},
],
},
{
type: 'button',
attributes: { class: 'btn btn-primary add-comment' },
children: ['Add Comment'],
},
],
},
];
23 changes: 23 additions & 0 deletions formMethods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export function getFormInput(formEl) {
if (!formEl.checkValidity()) {
formEl.classList.add('was-validated');
return;
} else {
const input = Array.from(formEl.querySelectorAll('.form-control')).reduce(
(struct, current) => {
struct[current.name] = current.value;
return struct;
},
{}
);
clearInput(formEl);
formEl.classList.remove('was-validated');
return input;
}

function clearInput(formEl) {
formEl.querySelectorAll('.form-control').forEach((input) => {
input.value = '';
});
}
}
71 changes: 71 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!DOCTYPE html>
<head>
<html lang="en">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>

<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"
></script>
</head>
<body>
<div class="main d-flex flex-column p-5">
<h1>ReReddit</h1>
<div class="posts">
</div>
<form class="post-form needs-validation">
<h3>Add a post</h3>
<div class="mb-2">
<input
type="text"
class="form-control"
name="title"
placeholder="Post Title"
required
/>
<div class="invalid-feedback">
Please provide a post title.
</div>
<div class="valid-feedback">
Looks good!
</div>
</div>
<div class="form-floating mb-2">
<textarea class="form-control" placeholder="Write your post here..." name="text" style="height: 100px" required></textarea>
<div class="invalid-feedback">
Post must be between 5 and 200 characters.
</div>
<div class="valid-feedback">
Looks good!
</div>
</div>
<div class="mb-2">
<input
type="text"
class="form-control"
name="name"
placeholder="Name"
required
/>
<div class="invalid-feedback">
Please provide a name.
</div>
<div class="valid-feedback">
Looks good!
</div>
</div>
<button class="btn btn-primary add-post">Add Post</button>
</form>
</div>
<script type="module" src="./main.js"></script>
</body>
</html>
21 changes: 21 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { addPost, deletePost } from './postMethods.js';
import { showComments, addComment } from './commentMethods.js';

const main = document.querySelector('.main');
const posts = document.querySelector('.posts');

main.addEventListener('click', (e) => {
e.preventDefault();
if (e.target.classList.contains('add-post')) {
addPost(e.target, posts);
}
if (e.target.classList.contains('delete-btn')) {
deletePost(e.target);
}
if (e.target.classList.contains('show-comments')) {
showComments(e.target);
}
if (e.target.classList.contains('add-comment')) {
addComment(e.target);
}
});
19 changes: 19 additions & 0 deletions postMethods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { commentTemplate } from './commentTemplate.js';
import { postTemplate } from './postTemplate.js';
import { buildConst, buildFragment } from './buildMethods.js';

export function addPost(eventTarget, appendTo) {
const p = buildConst(eventTarget, postTemplate);
if (p) {
const { _, obj, el } = p;
el.querySelector('.card-title').innerText = `${obj.title}`;
el.querySelector('.card-subtitle').innerText = `Posted by: ${obj.name}`;
el.querySelector('.card-text').innerText = `${obj.text}`;
el.querySelector('.card-body').appendChild(buildFragment(commentTemplate));
appendTo.insertBefore(el, appendTo.firstChild);
}
}

export function deletePost(eventTarget) {
eventTarget.closest('.card').remove();
}
39 changes: 39 additions & 0 deletions postTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export const postTemplate = [
{
type: 'div',
attributes: { class: 'card mb-3' },
children: [
{
type: 'div',
attributes: { class: 'card-body' },
children: [
{
type: 'h5',
attributes: { class: 'card-title' },
},
{
type: 'h6',
attributes: { class: 'card-subtitle mb-2 text-body-secondary' },
},
{ type: 'p', attributes: { class: 'card-text' } },
{
type: 'a',
attributes: {
class: 'card-link delete-btn',
style: 'cursor: pointer',
},
children: ['Delete Post'],
},
{
type: 'a',
attributes: {
class: 'card-link show-comments',
style: 'cursor: pointer',
},
children: ['Show Comments'],
},
],
},
],
},
];
32 changes: 32 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Simple Post & Comment App

This project provides a basic structure for creating, displaying, and managing posts and comments dynamically using JavaScript.

## How to Run

Code is split into different files and uses import and export statements. The code uses `<script type="module">` and therefore requires a server to run. If you try to access directly it will not work because CORS will block the module imports.

- Clone the repo
- Use live server extension for VSCode
- OR use node to run a server with `npx http-server .`

## Features

- Add new posts with a title, author, and text
- Delete posts
- Show/hide comments for each post
- Add comments to posts

## How It Works

- Clicking **Add Post** creates a new post from the input fields.
- Clicking **Delete** removes a post.
- Clicking **Show Comments** toggles visibility of the comments section.
- Clicking **Add Comment** adds a comment to the corresponding post.

## File Structure

- `index.html` - Main HTML file
- `script.js` - Handles post and comment interactions
- `commentEl.js` - Template for comment elements
- `postEl.js` - Template for post elements