From 2c453bdf40823cab8abc6bdc5b67b633e5b42f70 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:52:07 -0500 Subject: [PATCH 01/16] First commit --- index.html | 43 +++++++++++++++++++++++++++++++++++++++++++ main.js | 14 ++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 index.html create mode 100644 main.js diff --git a/index.html b/index.html new file mode 100644 index 00000000..359d33d2 --- /dev/null +++ b/index.html @@ -0,0 +1,43 @@ + + + + + + Document + + + + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + diff --git a/main.js b/main.js new file mode 100644 index 00000000..c35f3668 --- /dev/null +++ b/main.js @@ -0,0 +1,14 @@ +const main = document.querySelector('.main'); + +const obj = main.addEventListener(('click', (event) => { + if (event.target.) +})) + +function createPostElement(obj) { + const element = document.createElement('div').setAttribute() + return element.appendChild(document.createTextNode(`${obj.text} - Poster by: ${obj.name}`)); +} + +function appendToMain(domElement, htmlElement) { + return domElement.appendChild(htmlElement); +} From 22e89800f90ac75b63470128f705d6a3010ad0ed Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Mon, 3 Feb 2025 14:40:43 -0500 Subject: [PATCH 02/16] User can add posts. --- index.html | 21 ++++++++++++--------- main.js | 33 ++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/index.html b/index.html index 359d33d2..b33d89bb 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,6 @@ - - + + Document @@ -18,26 +18,29 @@ > -
- -
+
+

ReReddit

+
+
+
- + -
diff --git a/main.js b/main.js index c35f3668..0273890d 100644 --- a/main.js +++ b/main.js @@ -1,14 +1,37 @@ const main = document.querySelector('.main'); +const posts = document.querySelector('.posts'); +const postForm = document.querySelector('.post-form'); -const obj = main.addEventListener(('click', (event) => { - if (event.target.) -})) +main.addEventListener('click', (e) => { + e.preventDefault(); + if (e.target.classList.contains('add-post')) { + appendToMain(posts, createPostElement(getPostInput())); + clearInput(); + } +}) + +function getPostInput() { + return Array.from(postForm.querySelectorAll('input')).reduce((struct, current) => { + struct[current.name] = current.value; + return struct; + }, {}) +} + +function clearInput() { + postForm.querySelectorAll('input').forEach(input => { + input.value = ''; + }) +} function createPostElement(obj) { - const element = document.createElement('div').setAttribute() - return element.appendChild(document.createTextNode(`${obj.text} - Poster by: ${obj.name}`)); + const el = document.createElement('div'); + el.classList.add('py-3') + el.appendChild(document.createTextNode(`${obj.text} - Posted by: ${obj.name}`)); + return el; } function appendToMain(domElement, htmlElement) { return domElement.appendChild(htmlElement); } + + From 69547ef57107db0b7d9d95e084e6493ddc47d32a Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Mon, 3 Feb 2025 17:07:46 -0500 Subject: [PATCH 03/16] Comment element added. Comment and post elements rendered using single function and javascript object. --- commentEl.js | 43 +++++++++++++++++++++++++++++ index.html | 17 +++++++----- main.js | 76 ++++++++++++++++++++++++++++++++++++++++++---------- postEl.js | 33 +++++++++++++++++++++++ 4 files changed, 149 insertions(+), 20 deletions(-) create mode 100644 commentEl.js create mode 100644 postEl.js diff --git a/commentEl.js b/commentEl.js new file mode 100644 index 00000000..606e1ed3 --- /dev/null +++ b/commentEl.js @@ -0,0 +1,43 @@ +export const commentEl = [ + { + type: 'div', + attributes: { class: 'm-3 d' }, + children: [ + { + type: 'div', + attributes: { class: 'mb-3' }, + children: [ + { + type: 'input', + attributes: { + type: 'text', + name: 'name', + placeholder: 'name', + class: 'form-control', + }, + }, + ], + }, + { + type: 'div', + attributes: { class: 'mb-3' }, + children: [ + { + type: 'input', + attributes: { + type: 'text', + name: 'comment', + placeholder: 'comment', + class: 'form-control', + }, + }, + ], + }, + { + type: 'button', + attributes: { class: 'btn btn-primary' }, + children: ['Comment'], + }, + ], + }, +]; diff --git a/index.html b/index.html index b33d89bb..07d47eff 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Document - + ReReddit
-
+

Add a post

+
-
+
+ +
+
+ diff --git a/main.js b/main.js index 0273890d..055c32f0 100644 --- a/main.js +++ b/main.js @@ -1,3 +1,6 @@ +import { commentEl } from './commentEl.js'; +import { postEl } from './postEl.js'; + const main = document.querySelector('.main'); const posts = document.querySelector('.posts'); const postForm = document.querySelector('.post-form'); @@ -5,33 +8,78 @@ const postForm = document.querySelector('.post-form'); main.addEventListener('click', (e) => { e.preventDefault(); if (e.target.classList.contains('add-post')) { - appendToMain(posts, createPostElement(getPostInput())); + posts.appendChild(createPostEl(getPostInput())); clearInput(); } -}) + if (e.target.classList.contains('delete-btn')) { + + } + if (e.target.classList.contains('show-comments')) { + showComments(e.target); + } +}); function getPostInput() { - return Array.from(postForm.querySelectorAll('input')).reduce((struct, current) => { - struct[current.name] = current.value; - return struct; - }, {}) + return Array.from(postForm.querySelectorAll('.form-control')).reduce( + (struct, current) => { + struct[current.name] = current.value; + return struct; + }, + {} + ); } function clearInput() { - postForm.querySelectorAll('input').forEach(input => { + postForm.querySelectorAll('.form-control').forEach((input) => { input.value = ''; - }) + }); } -function createPostElement(obj) { - const el = document.createElement('div'); - el.classList.add('py-3') - el.appendChild(document.createTextNode(`${obj.text} - Posted by: ${obj.name}`)); +function createPostEl(obj) { + const el = buildFragment(postEl); + el.querySelector('.card-title').innerText = `${obj.title}`; + el.querySelector('.card-subtitle').innerText = `Posted by: ${obj.name}`; + el.querySelector('.card-text').innerText = `${obj.text}`; + el.lastChild.appendChild(buildFragment(commentEl)); return el; } -function appendToMain(domElement, htmlElement) { - return domElement.appendChild(htmlElement); +function showComments(eventTarget) { + if (eventTarget.classList.contains('d-none')) + console.log(event) } +function buildFragment(arrayObj) { + const fr = new DocumentFragment(); + + arrayObj.forEach((obj) => { + fr.appendChild(buildElement(obj)); + }); + + 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; + } + + return fr; +} diff --git a/postEl.js b/postEl.js new file mode 100644 index 00000000..553cb427 --- /dev/null +++ b/postEl.js @@ -0,0 +1,33 @@ +export const postEl = [ + { + 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' }, + children: ['delete'], + }, + { + type: 'a', + attributes: { class: 'card-link show-comments' }, + children: ['comments'], + }, + ], + }, + ], + }, +]; From dac2ef4d8b21618cbcea8ce12eb567b70fec6414 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Mon, 3 Feb 2025 17:56:48 -0500 Subject: [PATCH 04/16] Comments being posted but not in the correct place in the DOM. Therefore show hide does not currently work on comments. --- commentEl.js | 14 +++++++------- main.js | 34 +++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/commentEl.js b/commentEl.js index 606e1ed3..3e566c4a 100644 --- a/commentEl.js +++ b/commentEl.js @@ -1,18 +1,18 @@ export const commentEl = [ { - type: 'div', - attributes: { class: 'm-3 d' }, + type: 'form', + attributes: { class: 'mt-2 d-none comment-form' }, children: [ { type: 'div', - attributes: { class: 'mb-3' }, + attributes: { class: 'mb-2' }, children: [ { type: 'input', attributes: { type: 'text', name: 'name', - placeholder: 'name', + placeholder: 'Name', class: 'form-control', }, }, @@ -20,14 +20,14 @@ export const commentEl = [ }, { type: 'div', - attributes: { class: 'mb-3' }, + attributes: { class: 'mb-2' }, children: [ { type: 'input', attributes: { type: 'text', name: 'comment', - placeholder: 'comment', + placeholder: 'Comment', class: 'form-control', }, }, @@ -35,7 +35,7 @@ export const commentEl = [ }, { type: 'button', - attributes: { class: 'btn btn-primary' }, + attributes: { class: 'btn btn-primary add-comment' }, children: ['Comment'], }, ], diff --git a/main.js b/main.js index 055c32f0..5ab070b9 100644 --- a/main.js +++ b/main.js @@ -8,19 +8,31 @@ const postForm = document.querySelector('.post-form'); main.addEventListener('click', (e) => { e.preventDefault(); if (e.target.classList.contains('add-post')) { - posts.appendChild(createPostEl(getPostInput())); + posts.appendChild(createPostEl(getFormInput(postForm))); clearInput(); } 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); + } }); -function getPostInput() { - return Array.from(postForm.querySelectorAll('.form-control')).reduce( +function addComment(eventTarget) { + const formEl = eventTarget.closest('form'); + const parent = eventTarget.closest('.card-body') + const obj = getFormInput(formEl); + const comment = buildFragment([{type: 'p'}]); + comment.querySelector('p').innerText = `${obj.comment} - Posted by: ${obj.name}`; + parent.insertBefore(comment, formEl); +} + +function getFormInput(formElement) { + return Array.from(formElement.querySelectorAll('.form-control')).reduce( (struct, current) => { struct[current.name] = current.value; return struct; @@ -40,13 +52,21 @@ function createPostEl(obj) { el.querySelector('.card-title').innerText = `${obj.title}`; el.querySelector('.card-subtitle').innerText = `Posted by: ${obj.name}`; el.querySelector('.card-text').innerText = `${obj.text}`; - el.lastChild.appendChild(buildFragment(commentEl)); + el.querySelector('.card-body').appendChild(buildFragment(commentEl)); return el; } function showComments(eventTarget) { - if (eventTarget.classList.contains('d-none')) - console.log(event) + const cl = eventTarget.nextSibling.classList; + if (cl.contains('d-none')) { + cl.remove('d-none'); + } else { + cl.add('d-none'); + } +} + +function deletePost(eventTarget) { + eventTarget.closest('.card').remove(); } function buildFragment(arrayObj) { From b4bc1e5c3199a2747dda87cd0129445c4b0ebcf1 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Mon, 3 Feb 2025 17:59:46 -0500 Subject: [PATCH 05/16] User can post and add comments, comments can be shown or hidden, post can be deleted. --- main.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main.js b/main.js index 5ab070b9..1688820f 100644 --- a/main.js +++ b/main.js @@ -24,11 +24,10 @@ main.addEventListener('click', (e) => { function addComment(eventTarget) { const formEl = eventTarget.closest('form'); - const parent = eventTarget.closest('.card-body') const obj = getFormInput(formEl); const comment = buildFragment([{type: 'p'}]); comment.querySelector('p').innerText = `${obj.comment} - Posted by: ${obj.name}`; - parent.insertBefore(comment, formEl); + formEl.insertBefore(comment, formEl.firstChild); } function getFormInput(formElement) { From 278652269c583207b4c374179317f6d582363215 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Tue, 4 Feb 2025 11:49:20 -0500 Subject: [PATCH 06/16] Add readme --- commentEl.js | 2 +- index.html | 4 ++-- main.js | 11 +++++++---- postEl.js | 8 ++++---- readme.md | 24 ++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 readme.md diff --git a/commentEl.js b/commentEl.js index 3e566c4a..4665209f 100644 --- a/commentEl.js +++ b/commentEl.js @@ -36,7 +36,7 @@ export const commentEl = [ { type: 'button', attributes: { class: 'btn btn-primary add-comment' }, - children: ['Comment'], + children: ['Add Comment'], }, ], }, diff --git a/index.html b/index.html index 07d47eff..8e5e59ff 100644 --- a/index.html +++ b/index.html @@ -23,7 +23,7 @@

ReReddit

-

Add a post

+

Add a post

Add a post placeholder="Name" />
- +
diff --git a/main.js b/main.js index 1688820f..ffeabc00 100644 --- a/main.js +++ b/main.js @@ -9,7 +9,6 @@ main.addEventListener('click', (e) => { e.preventDefault(); if (e.target.classList.contains('add-post')) { posts.appendChild(createPostEl(getFormInput(postForm))); - clearInput(); } if (e.target.classList.contains('delete-btn')) { deletePost(e.target); @@ -31,17 +30,19 @@ function addComment(eventTarget) { } function getFormInput(formElement) { - return Array.from(formElement.querySelectorAll('.form-control')).reduce( + const input = Array.from(formElement.querySelectorAll('.form-control')).reduce( (struct, current) => { struct[current.name] = current.value; return struct; }, {} ); + clearInput(formElement); + return input; } -function clearInput() { - postForm.querySelectorAll('.form-control').forEach((input) => { +function clearInput(formElement) { + formElement.querySelectorAll('.form-control').forEach((input) => { input.value = ''; }); } @@ -59,8 +60,10 @@ 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' } } diff --git a/postEl.js b/postEl.js index 553cb427..8490968a 100644 --- a/postEl.js +++ b/postEl.js @@ -18,13 +18,13 @@ export const postEl = [ { type: 'p', attributes: { class: 'card-text' } }, { type: 'a', - attributes: { class: 'card-link delete-btn' }, - children: ['delete'], + attributes: { class: 'card-link delete-btn', style: 'cursor: pointer' }, + children: ['Delete Post'], }, { type: 'a', - attributes: { class: 'card-link show-comments' }, - children: ['comments'], + attributes: { class: 'card-link show-comments', style: 'cursor: pointer' }, + children: ['Show Comments'], }, ], }, diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..d63f53ea --- /dev/null +++ b/readme.md @@ -0,0 +1,24 @@ +# Simple Post & Comment App + +This project provides a basic structure for creating, displaying, and managing posts and comments dynamically using JavaScript. + +## 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 From 893a25f2cbd6cf438bb935c4636413088c8221c0 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:28:57 -0500 Subject: [PATCH 07/16] Separated files. --- buildMethods.js | 39 +++++++++++++ commentMethods.js | 18 ++++++ commentEl.js => commentTemplate.js | 2 +- formMethods.js | 17 ++++++ main.js | 94 ++---------------------------- postMethods.js | 16 +++++ postEl.js => postTemplate.js | 12 +++- 7 files changed, 104 insertions(+), 94 deletions(-) create mode 100644 buildMethods.js create mode 100644 commentMethods.js rename commentEl.js => commentTemplate.js (96%) create mode 100644 formMethods.js create mode 100644 postMethods.js rename postEl.js => postTemplate.js (70%) diff --git a/buildMethods.js b/buildMethods.js new file mode 100644 index 00000000..d294cdd2 --- /dev/null +++ b/buildMethods.js @@ -0,0 +1,39 @@ +import { getFormInput } from './formMethods.js'; + +export function buildConst(eventTarget, template) { + const formEl = eventTarget.closest('form'); + const obj = getFormInput(formEl); + 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; + } +} \ No newline at end of file diff --git a/commentMethods.js b/commentMethods.js new file mode 100644 index 00000000..c2adcbf7 --- /dev/null +++ b/commentMethods.js @@ -0,0 +1,18 @@ +import { buildConst } from "./buildMethods.js"; + +export function addComment(eventTarget) { + const { formEl, obj, el } = buildConst(eventTarget, [{type: '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' + } +} \ No newline at end of file diff --git a/commentEl.js b/commentTemplate.js similarity index 96% rename from commentEl.js rename to commentTemplate.js index 4665209f..f40c9e84 100644 --- a/commentEl.js +++ b/commentTemplate.js @@ -1,4 +1,4 @@ -export const commentEl = [ +export const commentTemplate = [ { type: 'form', attributes: { class: 'mt-2 d-none comment-form' }, diff --git a/formMethods.js b/formMethods.js new file mode 100644 index 00000000..8ab36910 --- /dev/null +++ b/formMethods.js @@ -0,0 +1,17 @@ +export function getFormInput(formElement) { + const input = Array.from(formElement.querySelectorAll('.form-control')).reduce( + (struct, current) => { + struct[current.name] = current.value; + return struct; + }, + {} + ); + clearInput(formElement); + return input; + + function clearInput(formElement) { + formElement.querySelectorAll('.form-control').forEach((input) => { + input.value = ''; + }); + } +} \ No newline at end of file diff --git a/main.js b/main.js index ffeabc00..006d3d90 100644 --- a/main.js +++ b/main.js @@ -1,14 +1,13 @@ -import { commentEl } from './commentEl.js'; -import { postEl } from './postEl.js'; +import { addPost, deletePost } from './postMethods.js'; +import { showComments, addComment } from './commentMethods.js'; const main = document.querySelector('.main'); const posts = document.querySelector('.posts'); -const postForm = document.querySelector('.post-form'); main.addEventListener('click', (e) => { e.preventDefault(); if (e.target.classList.contains('add-post')) { - posts.appendChild(createPostEl(getFormInput(postForm))); + addPost(e.target, posts); } if (e.target.classList.contains('delete-btn')) { deletePost(e.target); @@ -19,89 +18,4 @@ main.addEventListener('click', (e) => { if (e.target.classList.contains('add-comment')) { addComment(e.target); } -}); - -function addComment(eventTarget) { - const formEl = eventTarget.closest('form'); - const obj = getFormInput(formEl); - const comment = buildFragment([{type: 'p'}]); - comment.querySelector('p').innerText = `${obj.comment} - Posted by: ${obj.name}`; - formEl.insertBefore(comment, formEl.firstChild); -} - -function getFormInput(formElement) { - const input = Array.from(formElement.querySelectorAll('.form-control')).reduce( - (struct, current) => { - struct[current.name] = current.value; - return struct; - }, - {} - ); - clearInput(formElement); - return input; -} - -function clearInput(formElement) { - formElement.querySelectorAll('.form-control').forEach((input) => { - input.value = ''; - }); -} - -function createPostEl(obj) { - const el = buildFragment(postEl); - 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(commentEl)); - return el; -} - -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' - } -} - -function deletePost(eventTarget) { - eventTarget.closest('.card').remove(); -} - -function buildFragment(arrayObj) { - const fr = new DocumentFragment(); - - arrayObj.forEach((obj) => { - fr.appendChild(buildElement(obj)); - }); - - 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; - } - - return fr; -} +}); \ No newline at end of file diff --git a/postMethods.js b/postMethods.js new file mode 100644 index 00000000..7c9edc6c --- /dev/null +++ b/postMethods.js @@ -0,0 +1,16 @@ +import { commentTemplate } from './commentTemplate.js'; +import { postTemplate } from './postTemplate.js'; +import { buildConst, buildFragment } from './buildMethods.js'; + +export function addPost(eventTarget, appendTo) { + const { _, obj, el } = buildConst(eventTarget, postTemplate); + 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(); +} diff --git a/postEl.js b/postTemplate.js similarity index 70% rename from postEl.js rename to postTemplate.js index 8490968a..98027829 100644 --- a/postEl.js +++ b/postTemplate.js @@ -1,4 +1,4 @@ -export const postEl = [ +export const postTemplate = [ { type: 'div', attributes: { class: 'card mb-3' }, @@ -18,12 +18,18 @@ export const postEl = [ { type: 'p', attributes: { class: 'card-text' } }, { type: 'a', - attributes: { class: 'card-link delete-btn', style: 'cursor: pointer' }, + attributes: { + class: 'card-link delete-btn', + style: 'cursor: pointer', + }, children: ['Delete Post'], }, { type: 'a', - attributes: { class: 'card-link show-comments', style: 'cursor: pointer' }, + attributes: { + class: 'card-link show-comments', + style: 'cursor: pointer', + }, children: ['Show Comments'], }, ], From 7eabd365618faec1fdafed85e6aa74799ae921a8 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:41:55 -0500 Subject: [PATCH 08/16] Delete README.md --- README.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 20395898..00000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## Project Reddit - -This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. - -If you have any questions about this project or the program in general, visit [parsity.io](https://parsity.io/) or email hello@parsity.io. From 7054eaaba6cad31158d2ffb5f7e3fc0ecee724b2 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:43:18 -0500 Subject: [PATCH 09/16] Update readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index d63f53ea..b7917dda 100644 --- a/readme.md +++ b/readme.md @@ -21,4 +21,4 @@ This project provides a basic structure for creating, displaying, and managing p - `index.html` - Main HTML file - `script.js` - Handles post and comment interactions - `commentEl.js` - Template for comment elements -- `postEl.js` - Template for post elements +- `postEl.js` - Template for post elements \ No newline at end of file From 7925dbe2809c0601eac6a8351acfea450748e41c Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:44:23 -0500 Subject: [PATCH 10/16] Readme --- readme.md | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/readme.md b/readme.md index b7917dda..20395898 100644 --- a/readme.md +++ b/readme.md @@ -1,24 +1,5 @@ -# Simple Post & Comment App +## Project Reddit -This project provides a basic structure for creating, displaying, and managing posts and comments dynamically using JavaScript. +This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. -## 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 \ No newline at end of file +If you have any questions about this project or the program in general, visit [parsity.io](https://parsity.io/) or email hello@parsity.io. From 703feda36be5693b2d1cffd86ca9868ebec89fa8 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:46:05 -0500 Subject: [PATCH 11/16] Readme --- readme.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 20395898..d63f53ea 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,24 @@ -## Project Reddit +# Simple Post & Comment App -This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. +This project provides a basic structure for creating, displaying, and managing posts and comments dynamically using JavaScript. -If you have any questions about this project or the program in general, visit [parsity.io](https://parsity.io/) or email hello@parsity.io. +## 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 From 7d3151b92ed3856a44cfea2eb3d83b476bce1c7d Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:33:53 -0500 Subject: [PATCH 12/16] Added some simple client side validation --- .vscode/settings.json | 3 +++ buildMethods.js | 2 ++ commentMethods.js | 17 ++++++++++------- commentTemplate.js | 32 +++++++++++++++++++++++++++++++- formMethods.js | 43 ++++++++++++++++++++++++++++++------------- index.html | 24 ++++++++++++++++++++++-- main.js | 2 +- postMethods.js | 15 +++++++++------ 8 files changed, 108 insertions(+), 30 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..6b665aaa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} diff --git a/buildMethods.js b/buildMethods.js index d294cdd2..35230220 100644 --- a/buildMethods.js +++ b/buildMethods.js @@ -3,8 +3,10 @@ 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) { diff --git a/commentMethods.js b/commentMethods.js index c2adcbf7..4917875e 100644 --- a/commentMethods.js +++ b/commentMethods.js @@ -1,18 +1,21 @@ -import { buildConst } from "./buildMethods.js"; +import { buildConst } from './buildMethods.js'; export function addComment(eventTarget) { - const { formEl, obj, el } = buildConst(eventTarget, [{type: 'p'}]); - el.querySelector('p').innerText = `${obj.comment} - Posted by: ${obj.name}`; - formEl.insertBefore(el, formEl.firstChild); + 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' + eventTarget.innerText = 'Hide Comments'; } else { cl.add('d-none'); - eventTarget.innerText = 'Show Comments' + eventTarget.innerText = 'Show Comments'; } -} \ No newline at end of file +} diff --git a/commentTemplate.js b/commentTemplate.js index f40c9e84..74680ba2 100644 --- a/commentTemplate.js +++ b/commentTemplate.js @@ -1,7 +1,7 @@ export const commentTemplate = [ { type: 'form', - attributes: { class: 'mt-2 d-none comment-form' }, + attributes: { class: 'mt-2 d-none needs-validation comment-form' }, children: [ { type: 'div', @@ -14,8 +14,23 @@ export const commentTemplate = [ 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.'], + }, ], }, { @@ -29,7 +44,22 @@ export const commentTemplate = [ name: 'comment', placeholder: 'Comment', 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.'], }, ], }, diff --git a/formMethods.js b/formMethods.js index 8ab36910..a4fe9cf2 100644 --- a/formMethods.js +++ b/formMethods.js @@ -1,17 +1,34 @@ -export function getFormInput(formElement) { - const input = Array.from(formElement.querySelectorAll('.form-control')).reduce( - (struct, current) => { - struct[current.name] = current.value; - return struct; - }, - {} - ); - clearInput(formElement); - return input; +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(formElement) { - formElement.querySelectorAll('.form-control').forEach((input) => { + function clearInput(formEl) { + formEl.querySelectorAll('.form-control').forEach((input) => { input.value = ''; }); } -} \ No newline at end of file +} + +export function validateInput(eventTarget) { + const formEl = eventTarget.closest('form'); + const arr = []; + formEl.querySelectorAll('.form-control').forEach((input) => { + if (input.value.length < 5 || input.value.length > 200) { + arr.push(false); + } + }); + return arr.every((v) => v); +} diff --git a/index.html b/index.html index 8e5e59ff..dc03f42d 100644 --- a/index.html +++ b/index.html @@ -22,7 +22,7 @@

ReReddit

-
+

Add a post

Add a post class="form-control" name="title" placeholder="Post Title" + required /> +
+ Please provide a post title. +
+
+ Looks good! +
- + +
+ Post must be between 5 and 200 characters. +
+
+ Looks good! +
Add a post class="form-control" name="name" placeholder="Name" + required /> +
+ Please provide a name. +
+
+ Looks good! +
diff --git a/main.js b/main.js index 006d3d90..e095eeb8 100644 --- a/main.js +++ b/main.js @@ -18,4 +18,4 @@ main.addEventListener('click', (e) => { if (e.target.classList.contains('add-comment')) { addComment(e.target); } -}); \ No newline at end of file +}); diff --git a/postMethods.js b/postMethods.js index 7c9edc6c..83b5419c 100644 --- a/postMethods.js +++ b/postMethods.js @@ -3,12 +3,15 @@ import { postTemplate } from './postTemplate.js'; import { buildConst, buildFragment } from './buildMethods.js'; export function addPost(eventTarget, appendTo) { - const { _, obj, el } = buildConst(eventTarget, postTemplate); - 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); + 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) { From eaa297875211ee108d286eb42a1b20132cb6f5d4 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:40:26 -0500 Subject: [PATCH 13/16] Small change to comment template --- commentTemplate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commentTemplate.js b/commentTemplate.js index 74680ba2..2e4845dc 100644 --- a/commentTemplate.js +++ b/commentTemplate.js @@ -52,7 +52,7 @@ export const commentTemplate = [ attributes: { class: 'invalid-feedback', }, - children: ['Please provide a name.'], + children: ['Please provide a comment.'], }, { type: 'div', From 68742584df0784b592327e851278c95517737d0c Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Wed, 5 Feb 2025 17:05:06 -0500 Subject: [PATCH 14/16] Deleted unnecessary function. --- formMethods.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/formMethods.js b/formMethods.js index a4fe9cf2..1387dfce 100644 --- a/formMethods.js +++ b/formMethods.js @@ -21,14 +21,3 @@ export function getFormInput(formEl) { }); } } - -export function validateInput(eventTarget) { - const formEl = eventTarget.closest('form'); - const arr = []; - formEl.querySelectorAll('.form-control').forEach((input) => { - if (input.value.length < 5 || input.value.length > 200) { - arr.push(false); - } - }); - return arr.every((v) => v); -} From 9531ae91b539590efd61153177ae7073a2239daa Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:32:10 -0500 Subject: [PATCH 15/16] Update index.html --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index dc03f42d..abfe79af 100644 --- a/index.html +++ b/index.html @@ -66,6 +66,6 @@

Add a post

- + From d8d176ac86504876012cc4599fdc28fa621bc2a6 Mon Sep 17 00:00:00 2001 From: Tom Winskell <129330818+tomwinskell@users.noreply.github.com> Date: Thu, 6 Feb 2025 11:05:51 -0500 Subject: [PATCH 16/16] Updated readme to explain how to run the project. --- .vscode/settings.json | 3 --- readme.md | 8 ++++++++ 2 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 6b665aaa..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "liveServer.settings.port": 5501 -} diff --git a/readme.md b/readme.md index d63f53ea..129a8dc3 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,14 @@ 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 `