I'm trying to create a to-do list app with jquery or bootstrap. Just css grid and vanilla js.
I'm currently having an issue floating my 'delete' buttons of list items to the right. The first one floats all the way right white the others kind of stagger.
Below is my code:
var addItemButton = document.getElementById('addItem')
var onEnter = document.getElementById('newNote')
//below event listener adds an item to the list on click
addItemButton.addEventListener('click', function() {
let item = document.getElementById('newNote').value
let node = document.createElement("LI")
let textnode = document.createTextNode(item)
node.appendChild(textnode)
if (item) {
document.getElementById('list-body').appendChild(node)
}
let node2 = document.createElement('BUTTON')
let textnode2 = document.createTextNode('Delete')
node2.appendChild(textnode2)
node.appendChild(node2)
node2.addEventListener('click', function() {
node2.parentNode.parentNode.removeChild(node)
});
document.getElementById('newNote').value = ''
});
onEnter.addEventListener('keyup', function(event) {
if (event.keyCode === 13) {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
addItemButton.click();
}
})
function applyButton() { //onload for dummy data or data from db
let getListObjects = document.querySelectorAll("li")
for (let i = 0; i < getListObjects.length; i++) {
let node2 = document.createElement('BUTTON')
let textnode2 = document.createTextNode('Delete')
node2.appendChild(textnode2)
getListObjects[i].appendChild(node2)
let y = getListObjects[i].querySelector('button')
y.addEventListener('click', function() {
y.parentNode.parentNode.removeChild(getListObjects[i])
});
}
}
.container {
display: grid;
height: 100%;
grid-gap: 5px;
grid-template-columns: repeat(3, 1fr)
}
.container2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
background-color: grey;
border: 1px solid grey;
}
#main-grid {
grid-column: 2;
}
#newNote {
height: 25px;
}
#inputIdForGrid {
justify-content: left;
align-items: center;
display: flex;
padding-left: 0.3em;
padding-top: 0.5em;
padding-bottom: 0.5em;
}
button {
margin: 10px;
padding: 10px 10px;
background-color: green;
border: none;
color: white;
font-size: 14px;
}
#addItem {
margin-left: 1em;
padding: 0.5em;
color: white;
font-size: 1.5em;
float: right;
}
ul {
list-style-type: none;
padding: 0px;
margin: 0px;
}
li {
height: 50px;
line-height: 50px;
}
li:nth-child(2n+0) {
background-color: grey;
}
li>button {
background-color: red;
}
h1 {
text-align: center
}
<body onload="applyButton()">
<h1>Vanilla JS ToDo List - No Jquery, No Bootstrap</h1>
<div class='container'>
<div id='main-grid'>
<div class="container2">
<div id='inputIdForGrid'><input type='text' placeholder="Enter
List Items Here" id='newNote'> </div>
<div> <a href='#'><i class="fas fa-plus-circle" id='addItem'
type='submit' ></i></a></div>
</div>
<ul id='list-body'>
<li>Walk Dog</li>
<li>Buy Apples</li>
<li>Hit Gym and Lift Bro</li>
<li>Stretch</li>
</ul>
</div>
</div>
</body>
I really am unsure how to solve this issue currently, I've never faced this problem before.
Have you tried:
button {
float:right;
}
I think the problem has something to do with the button margin.
The margin (10px) is bigger than the row so it will push the bottom buttons to the left.
Just change the margin: 10px; to margin: 7px 10px 7px 10px;
button {
margin: 7px 10px 7px 10px;
padding: 10px 10px;
background-color: green;
border: none;
color: white;
font-size: 14px;
float: right;
}
Use flexbox on parent element and justify-content: flex-end.
Related
What I want to do is when I click the task it will have a line through the text means that I'm done with the task. but the add event listener function for this is not working, I'm working with the javascript toggle and that's all I can think of right now to achieve this functionality.
Is there another way to do this? I searched on the internet and it seems complicated when I'm trying to figure it out.
const addBtn = document.querySelector("#push");
const taskInput = document.querySelector("#taskInput");
const taskOutput = document.querySelector("#tasks");
addBtn.addEventListener("click", function() {
let newTasks = taskInput.value;
if (newTasks.length == 0) {
alert("Please enter a task");
} else {
taskOutput.innerHTML += `<div class="task">
<span id="taskname">${newTasks} </span>
<button class="delete" id="deleteButton"><i class="fa-solid fa-trash"></i> </button>
</div>
`;
//delete
let deleteBtn = document.querySelector("#deleteButton");
deleteBtn.addEventListener("click", function() {
this.parentNode.remove();
});
//line through
let theTask = document.querySelectorAll(".task");
theTask.addEventListener("click", function() {
this.classList.toggle("completed");
});
}
});
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background: linear-gradient( 90deg, rgba(241, 206, 221, 1) 0%, rgba(124, 184, 254, 1) 100%);
display: flex;
justify-content: center;
align-items: center;
font-family: 'Kumbh Sans', sans-serif;
}
.container {
border: 2px solid white;
width: 50%;
min-width: 450px;
margin: auto;
padding: 30px 40px;
}
#new-task {
position: relative;
background-color: white;
padding: 30px 20px;
border-radius: 1em;
}
#new-task input {
width: 70%;
height: 45px;
font-family: 'Manrope', sans-seif;
font-size: 1.2em;
border: 2px solid #d1d3d4;
padding: 12px;
color: #111111;
font-weight: 500;
position: relative;
border-radius: 5px;
}
#new-task input:focus {
outline: none;
border-color: violet;
}
#new-task button {
font-family: 'Manrope', sans-seif;
position: relative;
float: right;
width: 25%;
height: 45px;
border-radius: 5px;
font-weight: bold;
font-size: 16px;
border: none;
background-color: violet;
color: white;
cursor: pointer;
}
#tasks {
background-color: white;
padding: 30px 20px;
margin-top: 50px;
border-radius: 10px;
width: 100%;
}
.task {
background-color: white;
height: 50px;
padding: 5px 10px;
margin-top: 10px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 2px solid violet;
cursor: pointer;
}
.task span {
font-size: 18px;
font-weight: 400;
}
.task button {
background-color: violet;
color: white;
height: 100%;
width: 40px;
border: none;
border-radius: 5px;
outline: none;
cursor: pointer;
}
.task button:hover {
background-color: red;
}
.completed {
text-decoration: line-through;
}
<body>
<div class="container">
<div id="new-task">
<input type="text" name="" id="taskInput" placeholder="Task to be done" />
<button id="push">ADD</button>
</div>
<div id="tasks"></div>
</div>
<script src="/script.js"></script>
</body>
querySelectorAll will return the list of nodes matching the selector tasks. So you have to iterate through each of those nodes and add your listener. See the below code snippet
let theTasks = document.querySelectorAll(".task");
theTasks.forEach((task) => {
task.addEventListener("click", function() {
this.classList.toggle("completed");
});
});
theTask is a list of nodes. Trying to attach event listener on this list is causing issues.
Also, you will be inserting lots of buttons with same id deleteButton and spans with same id taskname which is incorrect and can cause undefined behavior.
For theTask fix, you may want to do something like:
let theTasks = [...document.querySelectorAll(".task")];
theTasks.forEach(task => {
task.addEventListener("click", function() {
this.classList.toggle("completed");
})
});
Using innerHTML to create manipulate the DOM for an application like a todo list is probably not a good idea. The answers to Advantages of createElement over innerHTML? give good explanations why.
It is worth noting that in the innerHTML code, the span and the button are created with an id and so all of these elements will have the same id. It is also probably not a good idea to have duplicate ids on one page. Why are duplicate ID values not allowed in HTML? explains why.
Also, adding event listeners to every new task is also probably not a good idea. What is DOM Event delegation? gives a good explanation why.
Finally, the Difference between HTMLCollection, NodeLists, and arrays of objects and Document.querySelectorAll() explain how to get lists of elements that can be manipulated.
So, I have rewritten the task creation code in the addBtn.addEventListener to show one way how this could be done with document.createElement().
And I have created a separate event listener on the Tasks container div, which handles both task deletion and task completion.
I also added the following CSS so that clicking on a trash can icon is handled by the parent button. Without this CSS, clicking on an icon would not delete the task.
div#tasks i {
pointer-events: none;
}
To make the todo list more visible in the code snippet below, I reduced the heights, margins, and paddings of some of the elements in the CSS.
I also added a link to the font awesome icon library.
const addBtn = document.querySelector("#push");
const taskInput = document.querySelector("#taskInput");
const taskOutput = document.querySelector("#tasks");
taskOutput.addEventListener("click", function(event) {
if (event.target && event.target.nodeName === "SPAN") {
event.target.classList.toggle("completed");
}
if (event.target && event.target.nodeName === "BUTTON") {
event.target.parentNode.remove();
}
});
addBtn.addEventListener("click", function() {
let newTasks = taskInput.value;
if (newTasks.length == 0) {
alert("Please enter a task");
} else {
// Create a task DIV
const newTaskElement = document.createElement("div");
newTaskElement.classList.add("task");
// Create a SPAN with the task name
const newTaskNameElement = document.createElement("span");
const taskTextnode = document.createTextNode(newTasks);
newTaskNameElement.appendChild(taskTextnode);
// Create a BUTTON with a TRASH CAN ICON
const newTaskDeleteButton = document.createElement("button");
const deleteImageElement = document.createElement("i");
deleteImageElement.classList.add("fa-solid", "fa-trash");
newTaskDeleteButton.appendChild(deleteImageElement);
// Append the SPAN and the BUTTON to the task DIV
newTaskElement.appendChild(newTaskNameElement);
newTaskElement.appendChild(newTaskDeleteButton);
// Append the task DIV to the TASK LIST DIV
taskOutput.appendChild(newTaskElement);
}
});
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background: linear-gradient( 90deg, rgba(241, 206, 221, 1) 0%, rgba(124, 184, 254, 1) 100%);
font-family: 'Kumbh Sans', sans-serif;
}
/* ADDED TO MAKE SURE THAT THE TRASH ICON DOES NOT PROCESS CLICKS */
div#tasks i {
pointer-events: none;
}
.container {
border: 2px solid white;
width: 50%;
min-width: 450px;
margin: auto;
padding: 3px 4px;
}
#new-task {
position: relative;
background-color: white;
padding: 6px 4px;
border-radius: 1em;
}
#new-task input {
width: 70%;
height: 45px;
font-family: 'Manrope', sans-seif;
font-size: 1.2em;
border: 2px solid #d1d3d4;
padding: 12px;
color: #111111;
font-weight: 500;
position: relative;
border-radius: 5px;
}
#new-task input:focus {
outline: none;
border-color: violet;
}
#new-task button {
font-family: 'Manrope', sans-seif;
position: relative;
float: right;
width: 25%;
height: 45px;
border-radius: 5px;
font-weight: bold;
font-size: 16px;
border: none;
background-color: violet;
color: white;
cursor: pointer;
}
#tasks {
background-color: white;
padding: 6px 4px;
margin-top: 5px;
border-radius: 10px;
width: 100%;
min-height: 50px;
}
.task {
background-color: white;
height: 50px;
padding: 5px 10px;
margin-top: 10px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 2px solid violet;
cursor: pointer;
}
.task span {
font-size: 18px;
font-weight: 400;
}
.task button {
background-color: violet;
color: white;
height: 100%;
width: 40px;
border: none;
border-radius: 5px;
outline: none;
cursor: pointer;
}
.task button:hover {
background-color: red;
}
.completed {
text-decoration: line-through;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" rel="stylesheet" />
<div class="container">
<div id="new-task">
<input type="text" name="" id="taskInput" placeholder="Task to be done" />
<button id="push">ADD</button>
</div>
<div id="tasks"></div>
</div>
"use strict";
const diaryInput = document.querySelector(".writing");
const titleInput = document.querySelector(".title");
const submitButton = document.querySelector(".dsubmit");
const indexList = document.querySelector(".womb");
const inputTime = document.querySelector(".clock");
const selectAll = document.querySelector(".select");
const unselectAll = document.querySelector(".unselect");
const deleteIndex = document.querySelector(".delete");
submitButton.addEventListener("click", transferDiary);
selectAll.addEventListener("click", selectAllindex);
unselectAll.addEventListener("click", unselectAllindex);
deleteIndex.addEventListener("click", deleteDiaryIndex);
let layout_no = 0;
function transferDiary(e) {
e.preventDefault();
const indexLayout = document.createElement("div");
indexLayout.classList.add("indexlayout");
indexLayout.id = "indexlayout-" + layout_no;
const diaryIndex = document.createElement("div");
diaryIndex.classList.add("invisible");
diaryIndex.innerText = diaryInput.value;
saveLocalDiary(diaryInput.value);
const diaryTitle = document.createElement("div");
diaryTitle.classList.add("index");
const m = new Array(
"JAN.",
"FEB.",
"MAR.",
"APR.",
"MAY",
"JUN.",
"JUL.",
"AUG",
"SEPT.",
"OCT.",
"NOV.",
"DEC."
);
const years = date.getFullYear();
const months = date.getMonth();
const d = new Array(
"Sun.",
"Mon.",
"Tues.",
"wed.",
"Thurs.",
"Fri.",
"Sat."
);
const days = date.getDay();
const today = date.getDate();
diaryTitle.innerHTML = `<input type='checkbox' name='indexchild' value='' class="indexchild" >${m[months]} ${today} ${years} ${d[days]} -${titleInput.value}</>`;
indexLayout.appendChild(diaryTitle);
diaryTitle.appendChild(diaryIndex);
indexList.appendChild(indexLayout);
const wayBackHome = document.getElementById("indexlayout-" + layout_no);
wayBackHome.addEventListener("click", bringBackIndex);
layout_no++;
}
function selectAllindex(e) {
e.preventDefault();
const checkboxes = document.querySelectorAll("input[type=checkbox]");
for (const cb of checkboxes) {
cb.checked = true;
}
}
function unselectAllindex(e) {
e.preventDefault();
const checkboxes = document.querySelectorAll("input[type=checkbox]");
for (const cb of checkboxes) {
cb.checked = false;
}
}
function deleteDiaryIndex(i) {
i.preventDefault();
const checkboxes = document.querySelectorAll("input[type=checkbox]");
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
removeDiv(checkboxes[i]);
removeLocalDiary(checkboxes[i]);
}
}
}
function removeDiv(d) {
console.log(d.parentElement.parentElement);
console.log(d.parentElement.parentElement[1]);
d.parentElement.parentElement.remove();
}
function bringBackIndex(e) {
e.preventDefault();
const bringEssence = e.target.getElementsByClassName("invisible");
diaryInput.value = bringEssence[0].innerHTML.replace(
/\s?(<br\s?\/?>)\s?/g,
"\r\n"
);
}
function saveLocalDiary(todo) {
//check ---- HEy Do I already have thing in localstorage?
let diary;
if (localStorage.getItem("diary") === null) {
diary = [];
} else {
diary = JSON.parse(localStorage.getItem("diary"));
}
diary.push(todo);
localStorage.setItem("diary", JSON.stringify(diary));
}
function removeLocalDiary(todo) {
let diary;
if (localStorage.getItem("diary") === null) {
diary = [];
} else {
diary = JSON.parse(localStorage.getItem("diary"));
}
const todoIndex = todo.children.innerText;
diary.splice(diary.indexOf(todoIndex), 1);
localStorage.setItem("diary", JSON.stringify(diary));
}
:root {
--page1-color: #20bf6b;
--page2-color: #f7b731;
--page3-color: #4b7bec;
--text-color: white;
}
html {
font-size: 62.5%;
/* 10px=1rem */
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: sans-serif;
}
.page3 {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: var(--page3-color);
width: 70%;
min-width: 600px;
box-shadow: 9px 9px 9px rgb(126, 124, 124),9px 9px 9px rgb(126, 124, 124);
}
.dlayout {
min-height: 90vh;
max-height: 90vh;
border: 1px solid var(--text-color);
display: flex;
}
.dtitle {
min-height: 10%;
border-bottom: 1px solid var(--text-color);
}
.dindex {
min-width: 10vw;
max-width: 15vw;
border-right: 1px solid var(--text-color);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.diary {
min-width: 50vw;
display: flex;
flex-direction: column;
}
.submit {
display: flex;
flex-direction: row;
height: 10%;
border-bottom: 1px solid var(--text-color);
}
.submitime {
padding: 25px 0 0 15px;
color: var(--text-color);
min-width: 50%;
font-size: 3rem;
font-weight: 300;
}
.submitarea {
padding: 20px 0 0 0;
min-width: 40%;
color: var(--text-color);
font-size: 3rem;
letter-spacing: 3px;
font-weight: 300;
}
.title {
background-color: var(--page3-color);
border: 1px dashed var(--text-color);
font-size: 3rem;
outline: none;
height: 40px;
width: 200px;
letter-spacing: 3px;
font-weight: 300;
}
.dsubmit {
margin-top: 10px;
height: 30px;
width: 30px;
background-color: var(--page3-color);
border: none;
border-radius:50%;
cursor: pointer;
}
.dtitle {
color: var(--text-color);
text-align: center;
display: table;
font-size: 3rem;
font-weight: 300;
letter-spacing: 20px;
/* ์๊ฐ */
}
p {
display: table-cell;
vertical-align: middle;
}
.writing {
background-color: var(--page3-color);
border: none;
height: 90%;
color: var(--text-color);
font-size: 2.5rem;
letter-spacing: 3px;
font-weight: 100;
}
textarea::placeholder {
color: var(--text-color);
opacity: 0.5;
}
textarea{
resize: none;
}
.indexlayout {
color: var(--text-color);
text-align: center;
font-size: 1.5rem;
border: 1px solid var(--text-color);
margin:5px;
}
.invisible {
display:none ;
}
.womb{
overflow: scroll;
}
.selector{
color: var(--text-color);
width: 100%;
border:none;
border-top: 1px solid var(--text-color);
min-height: 5%;
background-color: var(--page3-color);
outline: none;
font-size: 3rem;
letter-spacing: 8px;
font-weight: 300;
}
.index{
word-break: break-all;
}
.indexchild{
}
.selector:hover{
color:var(--page3-color);
background-color: var(--text-color);
}
.dsubmit:hover{
box-shadow: -3px -3px 3px rgb(172, 172, 172), 3px 3px 3px rgb(237, 237, 237),;
transition: 0.3s;
}
.indexlayout:hover{
color:var(--page3-color);
background-color: var(--text-color);
}
<section class="page3">
<div class="dlayout">
<form id="indexo" name="indexi" class="dindex">
<div class="dtitle"><p>Diary</p></div>
<div class="womb"></div>
<div class="buttons">
<button class="selector select" value="selectall">Select All</button>
<button class="selector unselect" value="unselectall">Unselect All</button>
<button class="selector delete" value="delete">Delete</button>
</div>
</form>
<div class="diary">
<div class="submit">
<div class="submitime clock"></div>
<form class="submitarea">
Title: <input type="text" class="title" />
<button class="dsubmit"><img src="./arrow-circle-right-solid.svg" alt=""></button>
</form>
</div>
<textarea class="writing" placeholder=" shit something..."></textarea>
</div>
</div>
</section>
Hello, I am struggling code above. It is a quite simple diary, the right side has a textarea
tag so you can write your diary and also has a transfer button so you push it, left side is gonna have an index of what you wrote. and I also added a checkbox on the index so you can select/unselect/delete them all at once. if perfectly works fine until I added another function. I added Eventlistener(handler is click) on the index(div .indexlayout), when you click the index it is gonna bring back what you wrote into the textarea tag.
const wayBackHome = document.getElementById("indexlayout-" + layout_no);
wayBackHome.addEventListener("click", bringBackIndex);
It works also okay. so I was quite satisfied. However, a little while later, I found that checkboxes don't work ever since added a new function!!
to be specific, select/unselect All buttons still work and I can check checkboxes with those, but I cannot select checkboxes individually :/
I think it is because I added an event on div which is a parent of a checkbox.
But, not gonna lie, I had no idea how to solve this. I tried "wayBackHome.innerText" or even added event to other div but it didn't work. Could you tell me what I missed and How do I fix it please?
thx!
I am working on an application to search for cities. It returns a card with the flag, capital, population and region; however, it works only until I try to add an eventListener to the input search, after which, it does nothing.
Am I calling the search incorrectly? should I maybe do a search function?
P. S: I started coding ~6 months ago and sorry if it is a not well-constructed code.
const APICALL = 'https://restcountries.eu/rest/v2/';
const form = document.querySelector('.recherche')
const input = document.querySelector('.inpRecherche');
const searchBtn = document.querySelector('.searchBtn');
const affichage = document.querySelector('.affichage');
let cities = [];
//API call
async function dataApi(cities) {
const call = await fetch(APICALL);
const data = await call.json();
cities = data;
//console.log(cities);
createCard(cities);
}
dataApi(cities)
function createCard(cities) {
for (let i = 0; i < cities.length; i++) {
const cardHTML = `
<div class="carte">
<img src="${[cities[i].flag]}" alt="flag" class="avatar">
<h2>${[cities[i].name]}</h2>
<ul class="cont-infos">
<li class="capital">Capital : ${[cities[i].capital]}</li>
<li class="population">Population: ${[cities[i].population]}</li>
<li class="subregion">Region : ${[cities[i].subregion]}</li>
</ul>
</div>
`;
affichage.innerHTML = cardHTML;
}
}
searchBtn.addEventListener('click', dataApi);
form.addEventListener('submit', (e) => {
e.preventDefault()
if (e.value > 0) {
//console.log('hello');
dataApi([cities[i]]);
input.value = "";
}
})
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
height: 100vh;
background-image: url("Background.jpg");
font-family: Arial, Helvetica, sans-serif;
background-size: cover;
}
h1 {
font-size: 35px;
text-align: center;
margin: 30px;
}
.inpRecherche {
display: block;
width: 250px;
padding: 3px;
font-size: 12px;
}
form,
.affichage {
display: flex;
width: 100%;
height: auto;
align-items: center;
justify-content: center;
border-radius: 2px;
}
.affichage {
position: absolute;
justify-content: center;
border: 2px solid whitesmoke;
border-radius: 5px;
width: 300px;
height: 300px;
margin: 20px 480px;
}
.searchBtn {
height: 25px;
padding: 2px 5px;
background-color: #2896F6;
box-shadow: none;
cursor: pointer;
color: whitesmoke;
font-weight: bold;
border-radius: 3px solid white;
}
.searchBtn:hover {
background-color: #167CD4;
}
/* a partir d'ici c'est ร crรฉer
*/
.card {
width: 500px;
height: auto;
position: relative;
margin-top: 200px;
padding-bottom: 20px;
border-radius: 5px;
background-color: beige;
}
.avatar {
height: 150px;
width: 150px;
margin-top: 80px;
border-radius: 50%;
position: absolute;
left: 50%;
transform: translate(-50%, -50%);
background-color: #000;
border: 1px solid black;
}
h2 {
margin-top: 100px;
text-align: center;
font-size: 25px;
}
.cont-infos {
border-top: 1px dashed black;
margin-top: 20px;
list-style-type: none;
}
li {
padding: 10px 20px;
text-align: justify;
font-size: 22px;
}
li:nth-child(1) {
margin-top: 15px;
}
<h1>City finder ๐๐</h1>
<form class="recherche">
<input type="text" class="inpRecherche" placeholder="Rechercher une ville">
<button class="searchBtn">Chercher</button>
</form>
<div class="affichage">
</div>
The first thing that comes to my attention is the argument you're passing to the dataApi function. When you pass it to the event listener, the argument it receives is an event object (you're just naming it "cities"), and then you're trying to overwrite it with data from the API call instead of updating the global cities array.
Hi Lizeth and welcome to StackOverflow.
I'm not completely sure of what you are trying to do, but right now, your function createCard is not looping over all the cities you receive from the API, but writing over and over the content of cardHTML. To display all the cities, you can add a +in front of the = and
function createCard(cities) {
for (let i = 0; i < cities.length; i++) {
const cardHTML = `
<div class="carte">
<img src="${[cities[i].flag]}" alt="flag" class="avatar">
<h2>${[cities[i].name]}</h2>
<ul class="cont-infos">
<li class="capital">Capital : ${[cities[i].capital]}</li>
<li class="population">Population: ${[cities[i].population]}</li>
<li class="subregion">Region : ${[cities[i].subregion]}</li>
</ul>
</div>
`;
affichage.innerHTML += cardHTML;
}
}
And change your css classes to
.affichage {
justify-content: center;
border: 2px solid whitesmoke;
border-radius: 5px;
width: 90vw;
height: auto;
margin: 20px auto 0 auto;
display: flex;
flex-flow: row wrap;
}
.avatar {
height: 150px;
width: 150px;
margin: 10px auto;
border-radius: 50%;
position: initial;
background-color: #000;
border: 1px solid black;
margin: 10px auto;
}
But, be warned, building html this way can be very dangerous, see XSS attacks.
And to search for a city using the input, Yes, you need to build a search function yourself, and display the result. Regex could be a way to do it
So I'm working on this simple project. It's a To-Do List. Every task has to buttons, namely, done and delete (These are images). And there are two divisions, one will store the pending tasks and the other will stored the completed tasks.
I want that when the user clicks on the done button (tick image) the task should be removed from the "Pending Tasks" division and shifted to the "Completed Tasks" division. I tried to add this functionality by adding an event listener to the images. The event listens for a single click. But the code is not working properly. For the first task, I've to click twice to remove it. The same is the case with other tasks as well. Which means every single task is added twice to the "Completed Tasks" division.
Also, I've added functionality to add new tasks. But the buttons (images) don't work on these new tasks. Basically, event listening is not working on them. Kindly help with the issue. I've added the code below.
(I've not added any functionality for deletion)
var completedTasks = document.querySelector(".Completed-Tasks");
var pendingTasks = document.querySelector(".Pending-Tasks");
var addTaskButton = document.querySelector(".Add-Task-Button");
var doneButtons = document.querySelectorAll(".Tick");
var deleteButtons = document.querySelectorAll(".Cross");
doneButtons.forEach(checkClickEvent);
console.log(doneButtons.length);
function checkClickEvent(button, index) {
button.addEventListener("click", function() {
let task = button.parentNode.parentNode;
pendingTasks.removeChild(pendingTasks.childNodes[index]);
let doneTaskHTML = `<div class="Task Done"><p class="Task-Text">${task.children[0].textContent}</p><div class="Task-Operations"><img src="./Cross.png" alt="Delete" class="Operation"></div></div>`;
completedTasks.insertAdjacentHTML("beforeend", doneTaskHTML);
});
}
addTaskButton.addEventListener("click", function(event) {
event.preventDefault();
let newTaskText = document.querySelector(".Add-Task-Input");
if (newTaskText.value != "") {
let newTaskHTML = `<div class="Task"><p class="Task-Text">${newTaskText.value}</p><div class="Task-Operations"><img src="./Tick.png" alt="Done" class="Operation Tick"><img src="./Cross.png" alt="Delete" class="Operation Cross"></div></div>`;
pendingTasks.insertAdjacentHTML("beforeend", newTaskHTML);
newTaskText.value = "";
}
});
#import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
* {
margin: 0px;
padding: 0px;
box-sizing: border-box;
font-family: "Poppins";
}
body {
background: #EDF2F4;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
min-height: 100vh;
}
.Header {
background: #2B2D42;
color: #FFFFFF;
max-width: 500px;
width: 100%;
padding: 15px;
margin-bottom: 20px;
}
.Heading {
font-size: 25px;
text-align: center;
}
.Tasks-Space {
background: #8D99AE;
max-width: 500px;
width: 100%;
padding: 15px;
position: relative;
}
.Add-Task {
position: relative;
width: 100%;
}
.Add-Task-Input {
outline: none;
border: none;
padding: 5px 10px;
width: 100%;
font-size: 16px;
}
.Add-Task-Button {
outline: none;
border: none;
width: 100%;
padding: 5px 10px;
margin-top: 15px;
font-size: 16px;
cursor: pointer;
background: #D90429;
color: #FFFFFF;
}
.Add-Task-Button:hover {
background: #EF233C;
color: #FFFFFF;
}
.Tasks-Space-Heading {
margin-top: 15px;
padding: 5px 10px;
background: #2B2D42;
color: #FFFFFF;
}
.Pending-Tasks {
margin-top: 15px;
}
.Completed-Tasks {
margin-top: 15px;
}
.Task {
background: #FFFFFF;
padding: 5px 10px;
display: flex;
align-items: center;
justify-content: space-between;
}
.Task-Operations {
display: flex;
align-items: center;
}
.Operation {
height: 20px;
margin-left: 10px;
cursor: pointer;
}
.Done {
text-decoration: line-through;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>To-Do List</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="Header">
<p class="Heading">TO-DO LIST</p>
</div>
<div class="Tasks-Space">
<div class="Add-Task">
<input type="text" placeholder="Enter your task here" class="Add-Task-Input">
<button class="Add-Task-Button">Add Task</button>
</div>
<p class="Tasks-Space-Heading">Pending Tasks</p>
<div class="Pending-Tasks">
<div class="Task">
<p class="Task-Text">Make a pie</p>
<div class="Task-Operations">
<img src="./Tick.png" alt="Done" class="Operation Tick">
<img src="./Cross.png" alt="Delete" class="Operation Cross">
</div>
</div>
<div class="Task">
<p class="Task-Text">Play Minecraft</p>
<div class="Task-Operations">
<img src="./Tick.png" alt="Done" class="Operation Tick">
<img src="./Cross.png" alt="Delete" class="Operation Cross">
</div>
</div>
</div>
<p class="Tasks-Space-Heading">Completed Tasks</p>
<div class="Completed-Tasks"></div>
</div>
<script src="./main.js"></script>
</body>
</html>
Instead of pendingTasks.removeChild(pendingTasks.childNodes[index]); do task.remove(); since you already have the node that you want to remove. here is the reference to remove() function.
Also added document.querySelectorAll(".Tick").forEach(checkClickEvent); in add task click handler. This should add event listener to newly added tasks as well.
Edited
The solution did work but there was a slight problem. When we are adding event listeners to the new tasks using document.querySelectorAll(".Tick").forEach(checkClickEvent);, multiple event listeners are getting added to the existing tasks. To avoid this simply reinitialize the HTML inside the division. This can be done using pendingTasks.innerHTML = pendingTasks.innerHTML;. This code will remove all the existing event listeners on any element inside the division. Note that this code has to be used before adding event listeners again.
var completedTasks = document.querySelector(".Completed-Tasks");
var pendingTasks = document.querySelector(".Pending-Tasks");
var addTaskButton = document.querySelector(".Add-Task-Button");
var doneButtons = document.querySelectorAll(".Tick");
var deleteButtons = document.querySelectorAll(".Cross");
doneButtons.forEach(checkClickEvent);
console.log(doneButtons.length);
function checkClickEvent(button, index) {
button.addEventListener("click", function() {
let task = button.parentNode.parentNode;
task.remove();
let doneTaskHTML = `<div class="Task Done"><p class="Task-Text">${task.children[0].textContent}</p><div class="Task-Operations"><img src="./Cross.png" alt="Delete" class="Operation"></div></div>`;
completedTasks.insertAdjacentHTML("beforeend", doneTaskHTML);
});
}
addTaskButton.addEventListener("click", function(event) {
event.preventDefault();
let newTaskText = document.querySelector(".Add-Task-Input");
if (newTaskText.value != "") {
let newTaskHTML = `<div class="Task"><p class="Task-Text">${newTaskText.value}</p><div class="Task-Operations"><img src="./Tick.png" alt="Done" class="Operation Tick"><img src="./Cross.png" alt="Delete" class="Operation Cross"></div></div>`;
pendingTasks.insertAdjacentHTML("beforeend", newTaskHTML);
newTaskText.value = "";
pendingTasks.innerHTML = pendingTasks.innerHTML; // Re-initialize
document.querySelectorAll(".Tick").forEach(checkClickEvent); // Adding event listeners again
}
});
#import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
* {
margin: 0px;
padding: 0px;
box-sizing: border-box;
font-family: "Poppins";
}
body {
background: #EDF2F4;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
min-height: 100vh;
}
.Header {
background: #2B2D42;
color: #FFFFFF;
max-width: 500px;
width: 100%;
padding: 15px;
margin-bottom: 20px;
}
.Heading {
font-size: 25px;
text-align: center;
}
.Tasks-Space {
background: #8D99AE;
max-width: 500px;
width: 100%;
padding: 15px;
position: relative;
}
.Add-Task {
position: relative;
width: 100%;
}
.Add-Task-Input {
outline: none;
border: none;
padding: 5px 10px;
width: 100%;
font-size: 16px;
}
.Add-Task-Button {
outline: none;
border: none;
width: 100%;
padding: 5px 10px;
margin-top: 15px;
font-size: 16px;
cursor: pointer;
background: #D90429;
color: #FFFFFF;
}
.Add-Task-Button:hover {
background: #EF233C;
color: #FFFFFF;
}
.Tasks-Space-Heading {
margin-top: 15px;
padding: 5px 10px;
background: #2B2D42;
color: #FFFFFF;
}
.Pending-Tasks {
margin-top: 15px;
}
.Completed-Tasks {
margin-top: 15px;
}
.Task {
background: #FFFFFF;
padding: 5px 10px;
display: flex;
align-items: center;
justify-content: space-between;
}
.Task-Operations {
display: flex;
align-items: center;
}
.Operation {
height: 20px;
margin-left: 10px;
cursor: pointer;
}
.Done {
text-decoration: line-through;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>To-Do List</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="Header">
<p class="Heading">TO-DO LIST</p>
</div>
<div class="Tasks-Space">
<div class="Add-Task">
<input type="text" placeholder="Enter your task here" class="Add-Task-Input">
<button class="Add-Task-Button">Add Task</button>
</div>
<p class="Tasks-Space-Heading">Pending Tasks</p>
<div class="Pending-Tasks">
<div class="Task">
<p class="Task-Text">Make a pie</p>
<div class="Task-Operations">
<img src="./Tick.png" alt="Done" class="Operation Tick">
<img src="./Cross.png" alt="Delete" class="Operation Cross">
</div>
</div>
<div class="Task">
<p class="Task-Text">Play Minecraft</p>
<div class="Task-Operations">
<img src="./Tick.png" alt="Done" class="Operation Tick">
<img src="./Cross.png" alt="Delete" class="Operation Cross">
</div>
</div>
</div>
<p class="Tasks-Space-Heading">Completed Tasks</p>
<div class="Completed-Tasks"></div>
</div>
<script src="./main.js"></script>
</body>
</html>
Try using this code
// Create a "close" button and append it to each list item
var myNodelist = document.getElementsByTagName("LI");
var i;
for (i = 0; i < myNodelist.length; i++) {
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "close";
span.appendChild(txt);
myNodelist[i].appendChild(span);
}
// Click on a close button to hide the current list item
var close = document.getElementsByClassName("close");
var i;
for (i = 0; i < close.length; i++) {
close[i].onclick = function(data) {
var div = this.parentElement;
div.style.display = "none";
var data = div.innerText;
var list = document.getElementById("list");;
var firstname = div.value;
var entry = document.createElement('li');
entry.appendChild(document.createTextNode(data));
list.appendChild(entry);
}
}
// Add a "checked" symbol when clicking on a list item
var list = document.querySelector('ul');
list.addEventListener('click', function(ev) {
if (ev.target.tagName === 'LI') {
ev.target.classList.toggle('checked');
}
}, false);
// Create a new list item when clicking on the "Add" button
function newElement() {
var li = document.createElement("li");
var inputValue = document.getElementById("myInput").value;
var t = document.createTextNode(inputValue);
li.appendChild(t);
if (inputValue === '') {
alert("You must write something!");
} else {
document.getElementById("myUL").appendChild(li);
}
document.getElementById("myInput").value = "";
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "close";
span.appendChild(txt);
li.appendChild(span);
for (i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.style.display = "none";
}
}
}
body {
margin: 0;
min-width: 250px;
font-family: arial;
}
/* Include the padding and border in an element's total width and height */
* {
box-sizing: border-box;
}
/* Remove margins and padding from the list */
ul {
margin: 0;
padding: 0;
}
/* Style the list items */
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
list-style-type: none;
font-size: 18px;
transition: 0.2s;
border-bottom: 1px solid #eee;
margin-top: -1px;
/* make the list items unselectable */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* When clicked on, add a background color and strike out text */
ul li.checked {
background: #888;
color: #fff;
text-decoration: line-through;
}
/* Add a "checked" mark when clicked on */
ul li.checked::before {
content: '';
position: absolute;
border-color: #fff;
border-style: solid;
border-width: 0 2px 2px 0;
top: 10px;
left: 16px;
transform: rotate(45deg);
height: 15px;
width: 7px;
}
/* Style the close button */
.close {
position: absolute;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
}
.close:hover {
background-color: #f44336;
color: white;
}
/* Style the header */
.header {
background-color: #f44336;
padding: 30px 40px;
color: white;
text-align: center;
}
/* Clear floats after the header */
.header:after {
content: "";
display: table;
clear: both;
}
/* Style the input */
input {
margin: 0;
border: none;
border-radius: 0;
width: 75%;
padding: 10px;
float: left;
font-size: 16px;
}
input[type=text] {
outline: 0;
background: #eee;
border: 0;
transition: all .2s;
}
input[type=text]:focus {
background: #fff;
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);}
/* Style the "Add" button */
.addBtn {
padding: 10px;
width: 25%;
background: #d9d9d9;
color: #555;
float: left;
text-align: center;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
border-radius: 0;
}
.addBtn:hover {
background-color: #bbb;
}
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
<div id="myDIV" class="header">
<h2 style="margin:5px">My To Do List</h2>
<input type="text" id="myInput" placeholder="Title..." autocomplete="off">
<span onclick="newElement()" class="addBtn">Add</span>
</div>
<ul id="myUL">
<li>Hit the gym</li>
<li>Pay bills</li>
<li>Meet George</li>
<li>Buy eggs</li>
<li>Read a book</li>
<li>Organize office</li>
</ul>
<p style="padding-left: 30px;">Completed Tasks</p>
<ul id="list"></ul>
I'm very inexperienced at JavaScript so please forgive my terrible description! (Also it has to be JavaScript not jQuery as its going to work on a closed system (no Internet access).
So I want the last PLAY button clicked by the user to give an alert of what other buttons are currently active by reporting back their IDs.
All the buttons are the same class <btnpluse> but they all have unique ids. There are 4 groups of these buttons and only one button in a group can be active at one time. There are four functions that make this happen.
Once the user has selected one button from each group I would like the PLAY button to spit out an alert (and print it in the code) saying which on the active buttons have been selected so that eventually this can let me know what the user wants to 'play'
I've figured out how to get the id for a button when you press it, but can't seem to find code for a button that reports another buttons id if it has a function which has a CSS attached to it!
So here is my code so far.
function char(obj){
if (document.querySelector(".activeC"))
{
document.querySelector(".activeC").className = document.querySelector(".activeC").className.replace(" activeC","");
}
obj.className = obj.className + " activeC";
}
function rate(obj){
if (document.querySelector(".activeR"))
{
document.querySelector(".activeR").className = document.querySelector(".activeR").className.replace(" activeR","");
}
obj.className = obj.className + " activeR";
}
function sym(obj){
if (document.querySelector(".activeS"))
{
document.querySelector(".activeS").className = document.querySelector(".activeS").className.replace(" activeS","");
}
obj.className = obj.className + " activeS";
}
function rhy(obj){
if (document.querySelector(".activeRH"))
{
document.querySelector(".activeRH").className = document.querySelector(".activeRH").className.replace(" activeRH","");
}
obj.className = obj.className + " activeRH";
}
function playstop(obj, id) {
document.getElementById("playstop");
var btnOn = document.getElementById('playstop');
btnOn.classList.toggle('orange');
if (btnOn.innerHTML === "PLAY") {
btnOn.innerHTML = "STOP";
} else {
btnOn.innerHTML = "PLAY";
}
var showAlert = document.getElementById('playstop');
if(document.getElementsByClassName + "activeRH")
showAlert = (id);
alert(this.id);
}
h1 {
font-size: 18px;
color: #ec6607;
height: 5px;
}
h2 {
font-size: 18px;
color: #ec6607;
height: 5px;
}
h3{
font-size:14px;
color: #fca600;
padding-left: 10px;
height: 5px;
}
.Pulse-Char{
width: 100%;
height: 505px;
display: block;
background-color:#f2f2f2;
}
.Pulse-CharBG{
height: 26%;
width:96%;
display: inline-block;
background-color: #fcfcfc;
margin-top: 2%;
margin-right: 2%;
margin-left: 2%;
margin-bottom: 1%;
outline: auto;
outline-color: #c4c4c4;
box-shadow: 3px 3px 6px 1px #c4c4c4;
}
.Pulse-CharBG2{
height: 21.3%;
width:96%;
display: inline-block;
background-color: #fcfcfc;
margin-top: 1%;
margin-right: 2%;
margin-left: 2%;
margin-bottom: 1%;
outline: auto;
outline-color: #c4c4c4;
box-shadow: 3px 3px 6px 1px #c4c4c4;
}
.Pulse-CharBG3{
height: 20.3%;
width:96%;
display: inline-block;
background-color: #fcfcfc;
margin-top: 1%;
margin-right: 2%;
margin-left: 2%;
margin-bottom: 1%;
outline: auto;
outline-color: #c4c4c4;
box-shadow: 3px 3px 6px 1px #c4c4c4;
}
.Pulse-CharBG4{
height: 10.3%;
width: 96%;
display: block;
background-color: #fcfcfc;
margin-top: 1%;
margin-right: 2%;
margin-left: 2%;
margin-bottom: 1%;
outline: auto;
outline-color: #c4c4c4;
box-shadow: 3px 3px 6px 1px #c4c4c4;
}
.col{
width: 31.3%;
display: block;
float: left;
padding-left: 2%;
padding-top: 1%;
}
.colrate{
width: 22%;
display: inline-block;
float: left;
padding-left: 3%;
}
.pulse-panel .Pulse-Char .PlayBtnArea {
width: 100%;
float: left;
padding-left: 2%;
margin-top: 1%;
display: inline-block;
}
.colplay{
width: 96%;
display: inline-block;
float: left;
padding-left: 3%;
}
btnpulse{
width: 80%;
height: 20px;
font-size: 14px;
background-color: #F8F8F8;
color: #ec6607;
margin: 1%;
text-align: center;
padding: 3%;
float: left;
border-radius: 30px;
border: medium solid #EC6607;
cursor: pointer;
}
btnplaystop{
width: 80%;
height: 20px;
font-size: 14px;
background-color: #F8F8F8;
color: #ec6607;
margin: 1%;
text-align: center;
padding: 3%;
float: left;
border-radius: 30px;
border: medium solid #EC6607;
cursor: pointer;
}
btnpulse.activeC {
background-color: #ec6607 !important;
color:antiquewhite;
}
btnpulse.activeRH {
background-color: #ec6607 !important;
color:antiquewhite;
}
btnpulse.activeR {
background-color: #ec6607 !important;
color:antiquewhite;
}
btnpulse.activeS {
background-color: #ec6607 !important;
color:antiquewhite;
}
btnplaystop.orange{
background-color: #ec6607 !important;
color:antiquewhite;
}
<div class="Pulse-Char">
<div class="Pulse-CharBG">
<h3>Pulse Character</h3>
<div class="col"><btnpulse id = "btnPulNor" onClick = "char(this)">Normal</btnpulse></div>
<div class="col"><btnpulse id = "btnPulWeak" onClick = "char(this)">Weak</btnpulse></div>
<div class="col"><btnpulse id = "btnPulSlow" onClick = "char(this)">Slow rising</btnpulse></div>
<div class="col"><btnpulse id = "btnPulBou" onClick = "char(this)">Bounding</btnpulse></div>
<div class="col"><btnpulse id = "btnPulBis" onClick = "char(this)">Bisferens</btnpulse></div>
<div class="col"><btnpulse id = "btnPulAlt" onClick = "char(this)">Alternans</btnpulse></div>
</div>
<div class="Pulse-CharBG2">
<h3>Rhythm</h3>
<div class="col"><btnpulse id = "btnRhyReg" onClick = "rhy(this)">Regular</btnpulse></div>
<div class="col"><btnpulse id = "btnRhyRegIrr" onClick = "rhy(this)">Regularly Irregular</btnpulse></div>
<div class="col"><btnpulse id = "btnRhyIrrIrr" onClick = "rhy(this)">Irregularly Irregular</btnpulse></div>
</div>
<div class="Pulse-CharBG3">
<h3>Rate</h3>
<div class="colrate"><btnpulse id = "btnRate40" onClick = "rate(this)">40</btnpulse></div>
<div class="colrate"><btnpulse id = "btnRate70" onClick = "rate(this)">70</btnpulse></div>
<div class="colrate"><btnpulse id = "btnRate120" onClick = "rate(this)">120</btnpulse></div>
<div class="colrate"><btnpulse id = "btnRate180" onClick = "rate(this)">180</btnpulse></div>
</div>
<div class="Pulse-CharBG2">
<h3>Symmetry</h3>
<div class="col"><btnpulse id = "btnSymNor" onClick = "sym(this)">Normal</btnpulse></div>
<div class="col"><btnpulse id = "btnSymRR" onClick = "sym(this)">Radio-radial delay</btnpulse></div>
<div class="col"><btnpulse id = "btnSymRF" onClick = "sym(this)">Radio-femoral delay</btnpulse></div>
</div>
<div class="PlayBtnArea">
<div class="colplay"><btnplaystop id = "playstop" onClick = "playstop()">PLAY</btnplaystop></div>
</div>
</div>
You can just query for elements that have the activeXX class (differing XX), and get their id properties.
let ids = Array.from(document.querySelectorAll(".activeC, .activeRH, .activeR, .activeS"), btn => btn.id);
ids is an array. If you alert(ids), you will see a comma separated list of id property values.
Remark: the following lines in your code do not make sense to me:
if(document.getElementsByClassName + "activeRH")
showAlert = (id);
document.getElementsByClassName is a function, and then you perform a string addition to that function object, concatenating "activeRH". That gives a string value, which you then convert to a boolean since it is an if expression. That will always be true, since the string is not empty. I have no clue what you were trying to do there.
Also the parentheses around id are not doing anything. Moreover, obj and id are undefined, as playstop is called without arguments.