Appended elements getting removed by HTML - javascript

I am trying to create a TO DO List application. In this application when I click submit button of the formlayer, a new eventcard with all the data should be appended in the events div.
function reveal() {
document.getElementById("layer1").style.display = "block";
document.getElementById("formlayer").style.display = "block";
}
function hide() {
document.getElementById("layer1").style.display = "none";
document.getElementById("formlayer").style.display = "none";
}
function addEvent() {
document.getElementById("layer1").style.display = "none";
document.getElementById("formlayer").style.display = "none";
let title = document.getElementById("ftitle").value;
let time = document.getElementById("ftime").value;
let desc = document.getElementById("fdesc").value;
// this will create the card
let card = document.createElement("div");
card.classList.add("eventcard");
//title div
let cardtitle = document.createElement("div");
cardtitle.classList.add("eventtitle");
cardtitle.innerHTML = title;
card.appendChild(cardtitle);
//time div
let cardtime = document.createElement("div");
cardtime.classList.add("eventtime");
cardtime.innerHTML = time;
card.appendChild(cardtime);
//desc div
let carddesc = document.createElement("div");
carddesc.classList.add("eventdesc");
carddesc.innerHTML = desc;
card.appendChild(carddesc);
// del button
let cardbtn = document.createElement("button");
cardbtn.classList.add("delete");
cardbtn.innerHTML = "Delete";
card.appendChild(cardbtn);
// adding card to events
document.getElementById("events").appendChild(card);
}
<div id="container">
<div id="title">To Do List</div>
<div id="events">
<div class="eventcard">
<div class="eventtitle">Meeting</div>
<div class="eventtime">10:00 AM</div>
<div class="eventdesc">The meeting regarding the discussion of company sales</div>
<button class="delete">Delete</button>
</div>
</div>
<div id="addbtn" onclick="reveal()">Add Events</div>
</div>
<div id="layer1"></div>
<div id="formlayer">
<div id="text">Event Details</div>
<form>
<label for="Title">Event Title: </label>
<input type="text" name="ftitle" id="ftitle"> <br><br>
<label for="Time">Event Time: </label>
<input type="text" name="ftime" id="ftime"> <br><br>
<label for="Desc">Event Description: </label>
<input type="text" name="fdesc" id="fdesc"> <br>
<button class="fbut" onclick="addEvent()">Submit</button>
<button class="fbut" onclick="hide()">Close</button>
</form>
</div>
However, when I fill up the details in the form and click submit button, the new eventcard gets appended in the events div for a split second and that too with all the correct information and styling, but then automatically gets deleted for some reason. Why it is getting deleted? I have also tried placing the script in <head> and after the </body> hoping that would work but it doesn't.
Could you guys please point out where I am doing wrong? Also I am doing this on Firefox browser (it might be related).

Add type="button" to your two buttons in the form, otherwise they will submit the form. This will cause a reload, so you lose your dynamic updates.
<button type="button" class="fbut" onclick="addEvent()">Submit</button>
<button type="button" class="fbut" onclick="hide()">Close</button>

Related

How can I add elements in JS to certain divs

How do I put the created input into the other div in situation I presented below? If I introduce divs in js like this - '<div class="monday_input"><input type="button" class="remove_button" value="-" onclick="removeMon(this)" /></div>' removing the whole element is not working for some reason in this specific case. Answering the question. No I cannot create div in parent in html because input won't magically suit to created div . Please help me somehow, thank you!
HTML:
<div class="day">
<div class="day_info">
<p>Monday</p>
</div>
<div class="add">
<div class="button" onclick="add_monday()">
<i class="fas fa-plus" id="plus"></i>
</div>
</div>
<div style="clear:both"></div>
</div>
<div id="mon">
<div style="clear:both"></div>
</div>
JavaScript:
Function to adding:
function add_monday() {
if (monday_sub_count < 5) {
monday_sub_count++;
{
const mon = document.createElement('div');
mon.className = 'subcategory';
mon.innerHTML = '<textarea name="monday'+monday_id_count+'" placeholder="Type anything you want here" class="subcategory_text"></textarea><input type="button" class="remove_button" value="-" onclick="removeMon(this)" />';
monday_id_count++;
document.getElementById('mon').appendChild(mon);
}
}
}
Function to removing:
function removeMon(mon) {
document.getElementById('mon').removeChild(mon.parentNode);
monday_sub_count--;
monday_id_count--;
};
with your own HTML
function add_monday() {
var monday_sub_count = 0;
var a;
while (monday_sub_count < 5) {
a = '<div><textarea name="monday'+monday_id_count+'" placeholder="Type anything you want here" class="subcategory_text"></textarea><input type="button" class="remove_button" value="-" onclick="removeMon(this)" /></div>';
monday_sub_count++;
$('#mon').append(a);
}
}
Here is working, "proper" version of your code. I think your problem may come from over-complicating the removal process.
function add_monday()
{
let monday_count = 0;
// Use DocumentFragment for marginal optimizations
let fragment = new DocumentFragment();
while(monday_count < 5)
{
let monday = document.createElement('div');
monday.classList.add('subcategory');
let textarea = document.createElement('textarea');
textarea.classList.add('subcategory_text');
textarea.name = "monday_"+monday_count;
textarea.placeholder = "Type anything you want here";
let removeBtn = document.createElement('input');
removeBtn.type = "button";
removeBtn.classList.add('remove_button');
removeBtn.value = "-";
removeBtn.addEventListener('click', removeMon.bind(null, removeBtn));
monday.append(textarea, removeBtn);
fragment.appendChild(monday);
monday_count++;
}
document.getElementById('mon').appendChild(fragment);
}
function removeMon(button)
{
button.parentElement.remove();
}
I simplified your script a little and changed your name attributes: Instead of assigning individual names I simply gave all textareas the name monday[]. When posting this to a PHP page the values will be pushed into an array with the same name and in case you want to harvest the values with JavaScript, then this can be done easily too.
function add_monday(){
$("#mon").append('<div><textarea name="monday[]" placeholder="Type anything you want here"></textarea><input type="button" value="-"/></div>'.repeat(5))
}
$("#mon").on("click","input[type=button]",function(){$(this).parent(). remove()})
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<div class="day">
<div class="day_info">
<p>Monday</p>
</div>
<div class="add">
<div class="button" onclick="add_monday()">
<i class="fas fa-plus" id="plus">click here to add fields</i>
</div>
</div>
<div style="clear:both"></div>
</div>
<div id="mon">
<div style="clear:both"></div>
</div>
And here a non-jQuery version:
const ad=document.querySelector(".alldays");
ad.innerHTML=
"Mon,Tues,Wednes,Thurs,Fri,Satur,Sun".split(",").map(d=>`
<div class="day">
<div class="day_info"><p>${d}day</p></div>
<div class="add">
<div class="button">
<i class="fas fa-plus" id="plus">click here to add fields</i>
</div>
</div>
<div style="clear:both"></div>
</div>
<div id="${d.toLowerCase().substr(0,3)}">
<div style="clear:both"></div>
</div>`).join("");
function add2day(el,n){
const wd=el.closest(".day"),
d=wd.querySelector("p").textContent.toLowerCase(),
html=`<textarea name="${d.toLowerCase()}[]" placeholder="Type anything you want here"></textarea><input type="button" value="-"/>`;
while (n--) {
let div= document.createElement("div");
div.innerHTML=html;
wd.nextElementSibling.appendChild(div);
}
}
ad.addEventListener("click",function(ev){
const el=ev.target;
switch(el.tagName){
case "INPUT": // remove field
el.parentNode.remove(); break;
case "I": // add new fields
add2day(el,3); break;
}
})
<div class="alldays"></div>
I extended the second script to make it work for any day of the week.

Remove appended element

When I type something in the first box, and click Next, the word I typed is inserted on my page. However, if I click Back and then click Next again, it is printed a second time.
I want the keyword appended after I've clicked Next to be deleted if I click Back so that if I click Next again it is printed only once (and updated if I have made any edit).
document.addEventListener("DOMContentLoaded", function() {
var stepOne = document.getElementsByClassName("step-1");
var stepTwo = document.getElementsByClassName("step-2");
var nextButton = document.getElementsByClassName("next");
var backButton = document.getElementsByClassName("back");
nextButton[0].onclick = function() {
stepOne[0].style.display = "none";
stepTwo[0].style.display = "block";
var inputKeyword = document.getElementById("inputJobTitle").value;
var newKeyword = document.createElement("p");
newKeyword.setAttribute("id", "retrievedField-1");
newKeyword.setAttribute("class", "retrievedFieldName");
newKeyword.innerText = inputKeyword;
newKeyword.setAttribute("id", "retrievedField-1");
newKeyword.setAttribute("class", "retrievedFieldName");
document.getElementById("retrievedFields").appendChild(newKeyword);
}
backButton[0].onclick = function() {
stepOne[0].style.display = "block";
stepTwo[0].style.display = "none";
}
})
.step-1 {
display: block;
}
.step-2 {
display: none;
}
<!--STEP 1-->
<div class="main step-1">
<div class="tab" id="tab-1">
<div class="inputFields">
<p id="jobtitle" class="inputFieldName">Job title</p>
<input type="search" id="inputJobTitle" class="inputBar" />
<p class="and">AND</p>
</div>
<div class="button">
<button class="next">Next ></button>
</div>
</div>
</div>
<!--STEP 2-->
<div class="main step-2">
<div class="tab" id="tab-1">
<div class="inputFields">
<div id="retrievedFields"></div>
<input type="search" class="inputBarAlternative" />
<div class="add">
<button class="addButton">+ Add Keyword</button>
<p id="addKeyword">
Add a skill or keyword that must be in your search results
</p>
</div>
</div>
<div class="buttons">
<button class="back">Back</button>
<button class="next">Next</button>
</div>
</div>
</div>
</body>
Each new click on the next button will trigger an append. So you just have to do the opposite of an append on the click on back. Just add this line in your onclick:
document.getElementById("retrievedFields").removeChild(document.getElementById("retrievedFields").lastElementChild);
You could also check to see if the element exists before you create it..
nextButton[0].onclick = function() {
stepOne[0].style.display = "none";
stepTwo[0].style.display = "block";
var inputKeyword = document.getElementById("inputJobTitle").value;
var newKeyword = document.getElementById("retrievedField-1")
if (!newKeyword) {
newKeyword = document.createElement("p");
newKeyword.setAttribute("id", "retrievedField-1");
newKeyword.setAttribute("class", "retrievedFieldName");
document.getElementById("retrievedFields").appendChild(newKeyword);
}
newKeyword.innerText = inputKeyword;
}

Prevent buttons with same class from being clicked simultaneously

I have two buttons that have the same class name and have same functionality but different inputs must be added, so I used document.querySelectorAll() and the forEach() method to get them, but now when I click one, the other gets clicked too. Is there a way I can prevent this without having two addEventListener for both buttons? Enable it to click only one button at a time.
My code:
let inputElements = document.querySelectorAll('.inputElement');
const submitBtn = document.querySelectorAll('.submitBtn');
const backersElement = document.querySelector('.number-of-backers');
let donationsMade = [];
function calculateBamboo() {
inputElements.forEach(inputElement => {
const inputValue = parseFloat(inputElement.value);
if (inputValue < 25 || inputValue === '') return alert('Pledge must be at least $25.');
donationsMade.push(inputValue);
const donationsTotal = donationsMade.reduce((a, b) => a += b);
pledgedAmount.textContent = `$${donationsTotal}`;
backersElement.textContent = donationsMade.length;
return donationsTotal;
})
}
submitBtn.forEach(button => {
button.addEventListener('click', calculateBamboo);
})
It's not actually "clicking" both buttons. What's happening is the following:
Your calculateBamboo() functions loops through all the inputElements: inputElements.forEach(); and you're executing your logic for all the inputs. So, no matter which button you press, calculateBamboo() is processing each input.
Passing target input using data-attributes will help you to identify which input belongs to clicked button
Also, #JerryBen is right, you don't need to add event listener to each button but instead, we can wrap all the buttons in one element, add event listener to it and identify which element was clicked.
const wrapper = document.getElementById('wrapper');
const pledgedAmount = document.querySelector('.backed-users');
const backersElement = document.querySelector('.number-of-backers');
wrapper.addEventListener('click', calculateBamboo);
let donationsMade = [];
function calculateBamboo(event) {
/* Here you can access to the event argument,
which contains target: the clicked element*/
const el = event.target;
if (el.nodeName !== 'BUTTON' || !el.classList.contains('submitBtn')) {
return;
}
// Get target input from button's data-attr
const targetInput = el.dataset.input;
const inputElement = document.querySelector(`input[data-input="${targetInput}"]`);
// Continue with the code you had...
const inputValue = parseFloat(inputElement.value) || 0;
if (inputValue < 25 || inputValue === '') return alert('Pledge must be at least $25.');
donationsMade.push(inputValue);
const donationsTotal = donationsMade.reduce((a, b) => a += b);
pledgedAmount.textContent = `$${donationsTotal}`;
backersElement.textContent = donationsMade.length;
return donationsTotal;
}
<div class="backed">
<h1 class="backed-users">0</h1>
</div>
<div class="backers">
<h1 class="number-of-backers">0</h1>
</div>
<hr>
<div id="wrapper">
<div class=".pledge-edition">
<div class="pledge">
<section class="pledgeTwo"></section>
<div>
<h2>Bamboo Stand</h2>
Pledge $25 or more
<div>
<h2>101</h2>
<div>left</div>
</div>
</div>
</div>
<p>
You get an ergonomic stand made of natural bamboo. You've helped us launch our promotional campaign, and you’ll be added to a special Backer member list.
</p>
<div class="pledge-amount">
<p>Enter your pledge</p>
<div>
<input class="inputElement bambooInputElement" data-input="1" placeholder="$25" min="25" type="number">
<button class="submitBtn bambooBtn" data-input="1">Continue</button>
</div>
</div>
</div>
<div class=".pledge-edition">
<div class="pledge">
<section class="pledgeThree"></section>
<div>
<h2>Black Edition Stand</h2>
Pledge $75 or more
<div>
<h2>64</h2>
<div>left</div>
</div>
</div>
</div>
<p>
You get a Black Special Edition computer stand and a personal thank you. You’ll be added to our Backer member list. Shipping is included.
</p>
<div class="pledge-amount">
<p>Enter your pledge</p>
<div>
<input class="inputElement bambooInputElement" data-input="2" placeholder="$75" min="75" type="number">
<button class="submitBtn blackEditionBtn" data-input="2" placeholder="$75" min="75">Continue</button>
</div>
</div>
</div>
</div>
Adding event listeners to each button is considered a bad practice. Instead, use a single event listener to rule them all:
Wrap the buttons in a div element
Add click event only to the div wrapper
Pass an event object as an argument to the handler function
The event handler function will use the event.target to identify which specific button was clicked:
function calculateBamboo(evt){ const inputValue = parseFloat(evt.target.value) }
I hope this helps 🙄
My current JS
function calculateBamboo(target) {
let inputElement = document.querySelector(`input[data-input="${target}"]`);
donationsMade.push(inputElement);
const donationsTotal = donationsMade.reduce((a, b) => a += b);
backersElement.textContent = donationsMade.length;
pledgedAmount.textContent = `$${donationsTotal}`;
successElement.style.display = 'block';
return donationsTotal;
}
submitBtn.forEach(button => {
const target = button.dataset.input;
button.addEventListener('click', calculateBamboo.bind(target));
})
HTML buttons and input
<!-- this is how my buttons are placed -->
<div id="wrapper">
<div class=".pledge-edition">
<div class="pledge">
<section class="pledgeTwo"></section>
<div>
<h2>Bamboo Stand</h2>
Pledge $25 or more
<div>
<h2>101</h2>
<div>left</div>
</div>
</div>
</div>
<p>
You get an ergonomic stand made of natural bamboo. You've helped us launch our promotional campaign, and
you’ll be added to a special Backer member list.
</p>
<div class="pledge-amount">
<p>Enter your pledge</p>
<div>
<input class="inputElement bambooInputElement" data-input="1" placeholder="$25" min="25" type="number">
<button class="submitBtn bambooBtn" data-input="1">Continue</button>
</div>
</div>
</div>
<div class=".pledge-edition">
<div class="pledge">
<section class="pledgeThree"></section>
<div>
<h2>Black Edition Stand</h2>
Pledge $75 or more
<div>
<h2>64</h2>
<div>left</div>
</div>
</div>
</div>
<p>
You get a Black Special Edition computer stand and a personal thank you. You’ll be added to our Backer
member list. Shipping is included.
</p>
<div class="pledge-amount">
<p>Enter your pledge</p>
<div>
<input class="inputElement bambooInputElement" data-input="2" placeholder="$75" min="75" type="number">
<button class="submitBtn blackEditionBtn" data-input="2" placeholder="$75" min="75">Continue</button>
</div>
</div>
</div>
HTML for when the values are entered
<div class="backed">
<h1 class="backed-users">0</h1>
</div>
<div class="backers">
<h1 class="number-of-backers">0</h1>
</div>

List Items are showing up inside my submit button in to-do app

My List items are showing up inside of my submit button when I submit a new task.
EXPECTATION: when the submit button is clicked a new task should show up in a task list inside the .
ACTUAL: submit button is clicked, a new task is made inside of the submit button.
<body>
<div class="container">
<h1>TO DO LIST</h1>
<form id="taskForm">
<input id="taskInput"></input>
<button type="button" id="taskButton" onclick="taskList()">
Click Here</buton>
</form>
</div>
<div>
<ul id="taskLister"></ul>
</div>
<script src=script.js></script>
</body>
function taskList() {
let item = document.getElementById("taskInput").value;
let text = document.createTextNode(item);
let newTask = document.createElement("li");
let deleteTask = document.createElement("button");
deleteTask.style.cssText = 'height: 30px; width: 60px;';
deleteTask.innerText = 'Delete';
newTask.appendChild(text);
deleteTask.appendChild(newTask);
document.getElementById("taskLister").appendChild(newTask);
}
Firstable the <input> HTML element has no closing tag and you have misspelled the closing tag name </button>
function taskList() {
let item = document.getElementById("taskInput").value;
let text = document.createTextNode(item);
let newTask = document.createElement("li");
let deleteTask = document.createElement("button");
deleteTask.style.cssText = 'height: 30px; width: 60px;';
deleteTask.innerText = 'Delete';
newTask.appendChild(text);
deleteTask.appendChild(newTask);
document.getElementById("taskLister").appendChild(newTask);
}
<div class="container">
<h1>TO DO LIST</h1>
<form id="taskForm">
<input id="taskInput">
<button type="button" id="taskButton" onclick="taskList()">Click Here</button>
</form>
</div>
<div>
<ul id="taskLister"></ul>
</div>
I thought you want to add a remove button to each list item so here is an example
function taskList() {
// using inner HTML is much clear and gives a cleaner code
document.getElementById("taskLister").innerHTML += `
<li>${document.querySelector("#taskInput").value}<button onclick="this.parentElement.remove()">×</button></li>
`;
}
<div class="container">
<h1>TO DO LIST</h1>
<form id="taskForm">
<input id="taskInput">
<button type="button" id="taskButton" onclick="taskList()">Click Here</button>
</form>
</div>
<div>
<ul id="taskLister"></ul>
</div>

Is it possible with vanilla js to dynamically add a button element next to a text node?

For a todo list, I'm trying to dynamically add a button as a child of a list element and a sibling of a text node as below.
<ul>
<li>Hello world <button>X</button></li>
</ul>
The unordered list is in the HTML file but the list, text and button need to be inserted dynamically from the JS file. Is this possible without wrapping the text in a p tag?
Here's the code:
const todoInputEl = document.querySelector(".todo__input");
const todoListEl = document.querySelector(".todo__list");
const todoItemEls = document.querySelector(".todo__item");
const deleteItemEl = document.querySelector(".done");
function addListItem() {
todoInputEl.addEventListener("keypress", function(e) {
if (e.keyCode === 13) {
let newListItem = createListItem(todoInputEl.value);
todoListEl.insertBefore(newListItem, todoListEl.childNodes[0]);
todoInputEl.value = "";
}
})
}
function createListItem(text) {
const deleteButton = document.createElement("button");
const newListElement = document.createElement("li");
const newParaElement = document.createElement("p");
deleteButton.setAttribute("type", "button");
deleteButton.classList.add("delete");
deleteButton.innerHTML = "×";
newListElement.appendChild(newParaElement);
newListElement.setAttribute("class", "todo__item");
newParaElement.setAttribute("class", "todo__p");
newParaElement.textContent = text;
newParaElement.parentNode.insertBefore(deleteButton, deleteButton.nextElementSibling);
return newListElement;
}
addListItem();
<main>
<div class="container">
<div class="todo">
<div class="todo__header">
<h1 class="todo__title">to dos</h1>
<label for="todo input">
<input type="text" class="todo__input" placeholder="enter a thing to do">
</label>
</div>
<div class="todo__body">
<ul class="todo__list">
</ul>
</div>
</div>
</div>
</main>
As you see, it works if the text is inside a p tag, but I can't get it to work insert to the same place if it's just a plain text node. I looked extensively for examples of this being done, but haven't found any yet.
DOM manipulation can be expensive (jQuery DOM manipulations - performance comparation?). Using innerHTML sidesteps this and in my opinion makes things simpler.
const todoInputEl = document.querySelector(".todo__input");
const todoListEl = document.querySelector(".todo__list");
const todoItemEls = document.querySelector(".todo__item");
const deleteItemEl = document.querySelector(".done");
function addListItem() {
todoInputEl.addEventListener("keypress", function(e) {
if (e.keyCode === 13) {
todoListEl.innerHTML += createListItem(todoInputEl.value);
}
})
}
function createListItem(text) {
let listTemplate = "<li>{{ToDo}} <button type='button' class='delete'>×</button></li>";
return listTemplate.replace("{{ToDo}}", text);
}
addListItem();
<main>
<div class="container">
<div class="todo">
<div class="todo__header">
<h1 class="todo__title">to dos</h1>
<label for="todo input">
<input type="text" class="todo__input" placeholder="enter a thing to do">
</label>
</div>
<div class="todo__body">
<ul class="todo__list">
</ul>
</div>
</div>
</div>
</main>

Categories

Resources