JavaScript Drag&Drop without childNodes of parent DIV? - javascript

TL;DR
I'd like to drag & drop spans from one container div to a target div and back but not being the draggable spans valid drop targets themselves.
Description
I'm bulding an interactive tournament bracket app and I do struggle with setting the elements valid drop targets. I have a bordered div with #team-container on the right where all the draggable team spans (.draggable-team) are placed (screen snippet).
I used this tutorial and managed to get it working perfectly but there is also an unintended behaviour.
The valid drop targets are indicated by .drop-target and I add event listeners to all of these via
const dropTargets = document.querySelectorAll(".drop-target");
dropTargets.forEach(dropTarget => {
// dropTarget.addEventListener(...);
});
Unfortunately, the items I like to place via drag & drop are also set as valid drop targets (see here). I think that is due to the querySelectorAll I use as the #team-container is a .drop-target too, so the teams can be returned there when necessary. The returned NodeList object does contain the #team-container with its childNodes containing the draggable teams which might be the cause of trouble.
Any idea how to prevent the childs (the teams I like to drag & drop) from being a valid drop target just by being placed into a div that also is a valid drop target? Every help much appreciated! Thanks!
Edit 1
Removed First version with the issue. Possible nesting of draggable team spans is undesirable.
I have kind of solved my problem with if clauses checking if the drop target also is a draggable-team or contains .name. Is there a more elegant way of preventing the draggable team spans from being selectable as drop target?
Edit 2
Added first version snippet here and removed links to jsfiddle.net
addDragListeners();
const dropTargets = document.querySelectorAll('.drop-target');
dropTargets.forEach((dropTarget) => {
dropTarget.addEventListener('dragenter', dragEnter);
dropTarget.addEventListener('dragover', dragOver);
dropTarget.addEventListener('dragleave', dragLeave);
dropTarget.addEventListener('drop', drop);
});
function addDragListeners() {
let items = document.querySelectorAll('.draggable-team');
items.forEach((item) => {
item.addEventListener('dragstart', dragStart);
});
}
function dragStart(e) {
e.dataTransfer.setData('text/plain', e.target.id);
setTimeout(() => {
e.target.classList.add('hide');
}, 0);
}
function dragEnter(e) {
e.preventDefault();
e.target.classList.add('drag-over');
}
function dragOver(e) {
e.preventDefault();
e.target.classList.add('drag-over');
}
function dragLeave(e) {
e.target.classList.remove('drag-over');
}
function drop(e) {
let dropTarget = e.target;
dropTarget.classList.remove('drag-over');
let id = e.dataTransfer.getData('text/plain');
let draggable = document.getElementById(id);
if (dropTarget.id === 'team-container') {
draggable.classList.remove('dragged-team');
} else {
draggable.classList.add('dragged-team');
}
dropTarget.appendChild(draggable);
draggable.classList.remove('hide');
}
/* ==================== GLOBAL ==================== */
:root {
--text-white: #aaaaaa;
--text-pale: #505050;
--team-background: #1e1e1e;
--background: #303030;
--blue: #003865;
--bracket-line: 4px solid #ffffff;
}
html {
font-size: 14px;
}
body {
margin: 0;
font-family: sans-serif;
background-color: var(--background);
}
* {
box-sizing: border-box;
}
/* ==================== BRACKET ==================== */
.bracket-container {
display: flex;
flex-direction: row;
column-gap: 0.5rem;
margin-left: 1rem;
}
/* ----- MATCHES ----- */
.match-column {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.match {
display: flex;
margin: 1rem 0;
}
.team-names {
display: flex;
flex-direction: column;
width: 100%;
}
.match-number {
height: 100%;
display: flex;
align-items: center;
margin-right: 0.5rem;
color: var(--text-white);
}
.top-number,
.top-name {
border-bottom: 1px solid var(--text-pale);
}
.number {
padding: 0.5rem 0.5rem;
min-width: 2.1rem;
color: var(--team-background);
background-color: var(--text-white);
justify-content: center;
display: flex;
}
.name {
padding: 0.5rem 1rem;
color: var(--text-white);
background-color: var(--team-background);
display: block;
height: 50%;
min-width: 10rem;
}
/* ==================== DRAG & DROP ==================== */
#team-container {
position: absolute;
display: flex;
flex-direction: column;
right: 10px;
top: 10px;
height: 50vh;
padding: 1rem 2rem;
border: 3px solid #000000;
}
#team-container>span {
margin: 1rem 0;
text-align: center;
max-height: 2.5rem;
}
.draggable-team {
cursor: move;
}
.dragged-team {
padding: 0;
height: 100%;
}
.hide {
display: none;
}
.drag-over {
background-color: var(--blue);
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<article class="bracket-container">
<div class="match-column">
<div class="match">
<span class="seed-number">
<span class="number top-number">1</span>
<span class="number bottom-number">4</span>
</span>
<span class="team-names">
<span class="drop-target name top-name"></span>
<span class="drop-target name bottom-name"></span>
</span>
</div>
<div class="match">
<span class="seed-number">
<span class="number top-number">2</span>
<span class="number bottom-number">3</span>
</span>
<span class="team-names">
<span class="drop-target name top-name"></span>
<span class="drop-target name bottom-name"></span>
</span>
</div>
</div>
<div class="match-column">
<div class="match">
<span class="seed-number">
<span class="number top-number">1</span>
<span class="number bottom-number">2</span>
</span>
<span class="team-names">
<span class="drop-target name top-name"></span>
<span class="drop-target name bottom-name"></span>
</span>
</div>
</div>
</article>
<div id="team-container" class="drop-target">
<span class="draggable-team name" id="team1" draggable="true">Team1</span>
<span class="draggable-team name" id="team2" draggable="true">Team2</span>
<span class="draggable-team name" id="team3" draggable="true">Team3</span>
<span class="draggable-team name" id="team4" draggable="true">Team4</span>
</div>
<script src="script.js"></script>
</body>
</html>
Added second snippet with improved version here
addDragListeners();
const dropTargets = document.querySelectorAll(".drop-target");
dropTargets.forEach(dropTarget => {
dropTarget.addEventListener("dragenter", dragEnter);
dropTarget.addEventListener("dragover", dragOver);
dropTarget.addEventListener("dragleave", dragLeave);
dropTarget.addEventListener("drop", drop);
});
function addDragListeners() {
let items = document.querySelectorAll(".draggable-team");
items.forEach(item => {
item.addEventListener("dragstart", dragStart);
});
}
function dragStart(e) {
e.dataTransfer.setData("text/plain", e.target.id);
setTimeout(() => {
e.target.classList.add("hide");
}, 0);
}
function dragEnter(e) {
e.preventDefault();
e.target.classList.add("drag-over");
}
function dragOver(e) {
e.preventDefault();
e.target.classList.add("drag-over");
}
function dragLeave(e) {
e.target.classList.remove("drag-over");
}
function drop(e) {
let dropTarget = e.target;
dropTarget.classList.remove("drag-over");
let id = e.dataTransfer.getData("text/plain");
let draggable = document.getElementById(id);
// Move draggable back to tem-container if dropTarget is not empty
if (dropTarget.textContent != "") {
resetToTeamContainer(e);
} else {
// Remove class dragged-team when put back to team-container
if (dropTarget.id === "team-container") {
draggable.classList.remove("dragged-team");
} else {
draggable.classList.add("dragged-team");
}
// If dropped in draggable team, place back in team-container and remove
// class dragged-team
if (dropTarget.classList.contains("draggable-team")) {
resetToTeamContainer(e);
} else {
dropTarget.appendChild(draggable);
}
}
// display the draggable element
draggable.classList.remove("hide");
}
function resetToTeamContainer(e) {
let id = e.dataTransfer.getData("text/plain");
let draggable = document.getElementById(id);
draggable.classList.remove("dragged-team");
document.querySelector("#team-container").appendChild(draggable);
}
/* ==================== GLOBAL ==================== */
:root {
--text-white: #aaaaaa;
--text-pale: #505050;
--team-background: #1e1e1e;
--background: #303030;
--blue: #003865;
--bracket-line: 4px solid #ffffff;
}
html {
font-size: 14px;
}
body {
margin: 0;
font-family: sans-serif;
background-color: var(--background);
}
* {
box-sizing: border-box;
}
/* ==================== BRACKET ==================== */
.bracket-container {
display: flex;
flex-direction: row;
column-gap: 0.5rem;
margin-left: 1rem;
}
/* ----- MATCHES ----- */
.match-column {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.match {
display: flex;
margin: 1rem 0;
}
.team-names {
display: flex;
flex-direction: column;
width: 100%;
}
.match-number {
height: 100%;
display: flex;
align-items: center;
margin-right: 0.5rem;
color: var(--text-white);
}
.top-number,
.top-name {
border-bottom: 1px solid var(--text-pale);
}
.number {
padding: 0.5rem 0.5rem;
min-width: 2.1rem;
color: var(--team-background);
background-color: var(--text-white);
justify-content: center;
display: flex;
}
.name {
padding: 0.5rem 1rem;
color: var(--text-white);
background-color: var(--team-background);
display: block;
height: 50%;
min-width: 10rem;
}
/* ==================== DRAG & DROP ==================== */
#team-container {
position: absolute;
display: flex;
flex-direction: column;
right: 10px;
top: 10px;
height: 50vh;
padding: 1rem 2rem;
border: 3px solid #000000;
}
#team-container>span {
margin: 1rem 0;
text-align: center;
max-height: 2.5rem;
}
.draggable-team {
cursor: move;
}
.dragged-team {
padding: 0;
height: 100%;
}
.hide {
display: none;
}
.drag-over {
background-color: var(--blue);
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<article class="bracket-container">
<div class="match-column">
<div class="match">
<span class="seed-number">
<span class="number top-number">1</span>
<span class="number bottom-number">4</span>
</span>
<span class="team-names">
<span class="drop-target name top-name"></span>
<span class="drop-target name bottom-name"></span>
</span>
</div>
<div class="match">
<span class="seed-number">
<span class="number top-number">2</span>
<span class="number bottom-number">3</span>
</span>
<span class="team-names">
<span class="drop-target name top-name"></span>
<span class="drop-target name bottom-name"></span>
</span>
</div>
</div>
<div class="match-column">
<div class="match">
<span class="seed-number">
<span class="number top-number">1</span>
<span class="number bottom-number">2</span>
</span>
<span class="team-names">
<span class="drop-target name top-name"></span>
<span class="drop-target name bottom-name"></span>
</span>
</div>
</div>
</article>
<div id="team-container" class="drop-target">
<span class="draggable-team name" id="team1" draggable="true">Team1</span>
<span class="draggable-team name" id="team2" draggable="true">Team2</span>
<span class="draggable-team name" id="team3" draggable="true">Team3</span>
<span class="draggable-team name" id="team4" draggable="true">Team4</span>
</div>
<script src="script.js"></script>
</body>
</html>
Remaining question:
Is there a way to prevent the draggable spans from being a valid drop target?

Related

on click div get big and back to normal

I created a mini Qcm, when I click on an answer the check moves on the div on which I clicked
const reps = document.getElementsByClassName('rep');
[].forEach.call(reps, function(rep) {
$(rep).click(function() {
if (!rep.querySelector('.check')) {
[].forEach.call(reps, function(repToDel) {
if (repToDel.querySelector('.check')) {
repToDel.querySelector('.check').remove()
}
})
$(rep).last().append('<div class="check"><object data="check.svg" width="20" > </object></div>')
}
})
})
.container {
padding: 5%;
display: grid;
gap: 20px;
grid-template-columns: 1fr;
}
.question_title {
font-size: 18px;
font-weight: 500;
text-align: center;
}
.container_reps {
display: grid;
justify-items: center;
gap: 10px;
}
.rep {
display: flex;
align-items: center;
border: 1px solid black;
padding: 0 10px;
max-width: 15%;
min-width: 170px;
border-radius: 10px;
cursor: pointer;
overflow: hidden;
}
.dot_rep {
background-color: black;
color: white;
margin-right: 7px;
padding: 5px 10px;
border-radius: 5px;
}
.text_rep {
font-weight: 700;
}
.check {
margin-left: 20%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div class="container">
<div class="question_title">
<p>Je suis une Question, quelle est votre reponse ?</p>
</div>
<div class="container_reps">
<div class="rep">
<span class="dot_rep">A</span>
<p class="text_rep">Reponse 1</p>
</div>
<div class="rep">
<span class="dot_rep">B</span>
<p class="text_rep">Reponse 2</p>
</div>
<div class="rep">
<span class="dot_rep">C</span>
<p class="text_rep">Reponse 3</p>
</div>
<div class="rep">
<span class="dot_rep">D</span>
<p class="text_rep">Reponse 4</p>
</div>
</div>
</div>
but when I click the div get big and back to normal at the same time,I tried the overflow:hidden, but it didn't work.
the change between the check must be done smoothly.
With this JQuery code and if the image is present it's works
$(function(){
const reps = document.getElementsByClassName('rep');
[].forEach.call(reps,function(rep){
$(rep).click(function(){
// Remove Check Div only for Old Check
[].forEach.call(reps,function(repToDel) { if(repToDel.querySelector('.check')) repToDel.querySelector('.check').remove(); });
// Put Check Div for New Check
if(! rep.querySelector('.check')) $(rep).last().append('<div class="check"><object data="check.png" width="20" > </object></div>');
});
});
});

How to .appendChild() to all elements with the same class

The blue F turns into an actual amount of weight when you enter a number into the input field above.
The Two Functions Kg() and Lbs() are changing the class .dynamic which is where Kg or Lbs is being appended to the 8 divs that have the .dynamic class. Now that's exactly what isn't happening. When I select the divs with the .dynamic class, It adds Kg or Lbs (whatever button I press) to the first element with the .dynamic class.
How do I make it so that it appends that to all of the elements with the .dynamic class?
//Variables
const mercury = document.getElementById("mercury");
const venus = document.getElementById("venus");
const earth = document.getElementById("earth");
const mars = document.getElementById("mars");
const jupiter = document.getElementById("jupiter");
const saturn = document.getElementById("saturn");
const uranus = document.getElementById("uranus");
const neptune = document.getElementById("neptune");
const weight = document.getElementById("weight");
weight.addEventListener("input", Calc);
function Calc() {
if (weight.value > 99999) {
alert("Max Amount Of Numbers is 99999");
weight.value = "";
} else {
var val = weight.value;
console.log(val);
var calculate = val * 0.38;
calculate = Math.round(calculate);
mercury.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.9;
calculate = Math.round(calculate);
venus.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 1;
calculate = Math.round(calculate);
earth.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.38;
calculate = Math.round(calculate);
mars.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 2.36;
calculate = Math.round(calculate);
jupiter.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.92;
calculate = Math.round(calculate);
saturn.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.89;
calculate = Math.round(calculate);
uranus.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 1.12;
calculate = Math.round(calculate);
neptune.innerHTML = calculate;
console.log(calculate);
}
}
function lbs() {
let unit = document.getElementById("unit");
if (unit == null) {
let newElement = document.createElement("h3");
newElement.setAttribute("class", "value");
newElement.setAttribute("id", "unit");
newElement.textContent = "Lbs";
document.querySelector(".dynamic").appendChild(newElement);
} else {
if (unit.innerHTML == "Kg") {
unit.innerHTML = "Lbs";
}
}
}
function kg() {
let unit = document.getElementById("unit");
if (unit == null) {
let newElement = document.createElement("h3");
newElement.setAttribute("class", "value");
newElement.setAttribute("id", "unit");
newElement.textContent = "Kg";
document.querySelector(".dynamic").appendChild(newElement);
} else {
if (unit.innerHTML == "Lbs") {
unit.innerHTML = "Kg";
}
}
}
#import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
#font-face {
font-family: SpaceQuest;
src: url(https://raw.githubusercontent.com/Lemirq/WODP/master/Fonts/SpaceQuest-yOY3.woff);
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: url(./dario-bronnimann-hNQwIirOseE-unsplash.jpg);
}
/* Handle */
::-webkit-scrollbar-thumb {
background: rgba(59, 59, 59, 0.741);
border-radius: 200px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: rgb(255, 255, 255);
}
* {
--c-light: #f4f4f4;
--c-dark: #141414;
--c-blue: rgb(10, 132, 255);
--f-body: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
--trans-ease-in-out: all 0.2s ease-in-out;
color: var(--c-light);
}
body {
background-image: url(https://raw.githubusercontent.com/Lemirq/WODP/master/images/dario-bronnimann-hNQwIirOseE-unsplash.jpg);
margin: 0;
inset: 50px;
font-family: var(--f-body);
}
a {
color: var(--c-light);
text-decoration: none;
}
/***** NAVBAR *****/
.nav-item {
transition: var(--trans-ease-in-out);
}
.ext-link {
cursor: alias;
}
.nav-item:hover {
color: var(--c-dark);
background-color: var(--c-light);
}
.nav-item:hover li {
color: var(--c-dark);
}
.nav-item.icon-link:hover i {
color: var(--c-dark);
}
.nav-item:not(:last-child) {
margin-right: 20px;
}
navbar {
display: flex;
flex-direction: row;
justify-content: end;
align-items: center;
padding: 20px;
}
.nav-item-container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
}
.nav-item {
display: inline-block;
list-style: none;
padding: 10px;
font-size: 20px;
border-radius: 10px;
}
.gh-icon {
font-size: 30px;
}
/***** End NAVBAR *****/
/***** Main *****/
#wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
font-family: SpaceQuest, sans-serif;
font-size: 3.5rem;
}
.input-group {
border: 2px var(--c-light) solid;
border-radius: 10px;
/* max-width: 400px; */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 50px;
}
[type="number"]:focus {
outline: none;
}
[type="number"] {
font-size: 20px;
padding: 5px;
background-color: rgba(217, 217, 217, 0.2);
border: none;
font-family: var(--f-body);
min-width: 280px;
}
.btn-form {
font-size: 20px;
padding: 5px;
background-color: rgba(217, 217, 217, 0.2);
border: none;
transition: var(--trans-ease-in-out);
cursor: pointer;
font-family: var(--f-body);
}
.btn-form:hover {
background-color: rgba(217, 217, 217, 0.4);
}
.btn-form:first {
border-right: var(--c-light) 1px solid;
}
.input-group-text {
background-color: rgba(217, 217, 217, 0.2);
font-size: 17px;
padding: 7px;
}
/***** End Main *****/
/***** CARDS *****/
.card-container {
margin: 50px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
align-items: center;
grid-gap: 10px;
width: calc(100vw - 200px);
}
.card {
background-color: #141414;
width: auto;
height: auto;
border-radius: 10px;
padding: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175);
}
.planet-info {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.planet {
font-size: 50px;
margin: 0;
text-transform: capitalize;
}
.planet-img {
width: 80px;
height: auto;
margin-right: 30px;
}
[src="./images/planets/Saturn.png"] {
height: 79.25px;
width: auto;
}
.weight {
margin-top: 10px;
text-transform: capitalize;
font-size: 20px;
}
.weight::after {
content: ":";
}
.divider {
height: 1px;
width: 100%;
margin: 20px 0;
background-color: var(--c-light);
}
.value {
font-size: 60px;
color: var(--c-blue);
}
.dynamic>.value:nth-child(2) {
margin-left: 10px;
}
.dynamic {
display: flex;
flex-direction: row;
justify-content: space-between;
}
/***** End CARDS *****/
.input-error {
outline: 1px solid red;
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.8.3/font/bootstrap-icons.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<navbar>
<ul class="nav-item-container">
<a target="_blank" class="nav-item ext-link" href="https://lemirq.github.io">
<li>Website</li>
</a>
<a target="_blank" class="nav-item icon-link ext-link" href="https://github.com/Lemirq">
<li><i class="bi bi-github gh-icon"></i></li>
</a>
</ul>
</navbar>
<div id="wrapper">
<h1 id="vs-h1">Your Weight On Different Planets</h1>
<div class="input-group">
<input id="weight" placeholder="Enter your Weight" type="number">
<button id="kg" onclick="kg()" class="btn-form" type="button">Kg</button>
<button id="lbs" onclick="lbs()" class="btn-form" type="button">Lbs</button>
</div>
<div class="card-container">
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Mercury.png" alt="EARTH">
<h3 class="planet">mercury</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="mercury" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Venus.png" alt="EARTH">
<h3 class="planet">venus</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="venus" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Earth.png" alt="EARTH">
<h3 class="planet">earth</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="earth" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Mars.png" alt="EARTH">
<h3 class="planet">mars</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="mars" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Jupiter.png" alt="EARTH">
<h3 class="planet">jupiter</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="jupiter" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Saturn.png" alt="EARTH">
<h3 class="planet">saturn</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="saturn" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Uranus.png" alt="EARTH">
<h3 class="planet">uranus</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="uranus" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Neptune.png" alt="EARTH">
<h3 class="planet">neptune</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="neptune" class="value">F</h3>
</div>
</div>
</div>
</div>
Select all elements with the .dynamic class by using querySelectorAll
Iterate through the NodeList and append desired child to each node
let dynamics = document.querySelectorAll(".dynamic")
dynamics.forEach((ele) => {
let newElement = document.createElement("h3");
ele.appendChild(newElement);
}
Minor Problems
There's no such HTML element <navbar>, there's <nav>.
There's a block code hard-coded 8 times with the only difference between them is a number. Whenever code needs to repeat itself, we use some sort of iteration such as a for loop or an array method and the numbers would be passed as a single variable passed with every iteration.
const gforce = [0.38, 0.9, 1, 0.38, 2.36, 0.92, 0.89, 1.12];
for (let i=0; i < 8; i++) {
var val = weight.value;
console.log(val);
var calculate = val * gforce[i];//<== that should be a variable
calculate = Math.round(calculate);
venus.innerHTML = calculate;
console.log(calculate);
}
Also, the <input> that's supposed to calculate lbs and kg doesn't appear to convert kg to lbs or vice-versa.
Details are commented in example
// An array of objects - each object represents a planet
const data = [{
planet: 'Mercury',
gforce: 0.38,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Mercury_in_true_color.jpg/440px-Mercury_in_true_color.jpg`
},
{
planet: 'Venus',
gforce: 0.9,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Venus_from_Mariner_10.jpg/440px-Venus_from_Mariner_10.jpg`
},
{
planet: 'Earth',
gforce: 1,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/c/cb/The_Blue_Marble_%28remastered%29.jpg/440px-The_Blue_Marble_%28remastered%29.jpg`
},
{
planet: 'Mars',
gforce: 0.38,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/OSIRIS_Mars_true_color.jpg/440px-OSIRIS_Mars_true_color.jpg`
},
{
planet: 'Jupiter',
gforce: 2.36,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Jupiter_and_its_shrunken_Great_Red_Spot.jpg/440px-Jupiter_and_its_shrunken_Great_Red_Spot.jpg`
},
{
planet: 'Saturn',
gforce: 0.92,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Saturn_during_Equinox.jpg/600px-Saturn_during_Equinox.jpg`
},
{
planet: 'Uranus',
gforce: 0.89,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Uranus_as_seen_by_NASA%27s_Voyager_2_%28remastered%29.png/440px-Uranus_as_seen_by_NASA%27s_Voyager_2_%28remastered%29.png`
},
{
planet: 'Neptune',
gforce: 1.12,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Neptune_-_Voyager_2_%2829347980845%29_flatten_crop.jpg/440px-Neptune_-_Voyager_2_%2829347980845%29_flatten_crop.jpg`
}
];
// Reference <form> and all form controls
const conv = document.forms.converter;
const IO = conv.elements;
/*
Collect all tags with [name='planet'] and convert it into an array...
...iterate through array and define a htmlString and interpolate planet
name and <img> url...
...render htmlString into current <output>
*/
[...IO.planet].forEach((output, index) => {
const html = `
<h3>${data[index].planet}</h3>
<img src='${data[index].img}'>
<h4></h4>`;
output.insertAdjacentHTML('beforeend', html)
});
// Bind the "input" event to <form>
conv.oninput = IWC;
/*
Event handler passes Event Object by default
Reference all form controls
Reference the tag the user interacted with
If the user typed into [name='weight']...
...if that tag was also #lbs...
...#kg value is the value of >origin< times 0.45359237...
...otherwise #lbs value is the value of >origin< times 2.20462...
*/
/*
Collect all [name='planet'] into an array and iterate with .forEach()...
...clear out the last tag of current <output> (<h4>)...
...display the calculated totals for lbs. and kg of each planet
*/
function IWC(e) {
const IO = this.elements;
const origin = e.target;
if (origin.name === 'weight') {
if (origin.id === 'lbs') {
IO.kg.value = +origin.value * 0.45359237;
} else {
IO.lbs.value = +origin.value * 2.20462;
}
[...IO.planet].forEach((output, index) => {
output.lastElementChild.innerHTML = '';
output.lastElementChild.insertAdjacentHTML('beforeend',
`lbs. ${Math.round(data[index].gforce * IO.lbs.value)}<br>
kg ${Math.round(data[index].gforce * IO.kg.value)}`);
});
}
}
#import url('https://fonts.googleapis.com/css2?family=Oswald:wght#300&family=Raleway:wght#300&display=swap');
html {
font: 300 2ch/1 Oswald;
}
body {
width: 100%;
overflow-x: hidden;
background: #aaa
}
h1 {
color: gold
}
h1,
h2,
h3,
h4 {
margin: 0;
font-family: Raleway;
}
h3,
h4 {
position: absolute;
z-index: 1;
}
h3 {
margin: 0 auto;
}
h4 {
bottom: 0
}
form {
margin: 0 0 0 -15px;
}
fieldset {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
max-width: 90%;
margin: 15px
}
fieldset+fieldset {
background: #222
}
fieldset+fieldset legend {
color: gold
}
output {
position: relative;
display: inline-flex;
width: 24%;
min-height: 8rem;
margin-top: 15px;
padding: 2px;
background: black;
color: cyan
}
input {
font: inherit;
width: 10rem;
margin: 10px;
text-align: center;
}
img {
width: 100%;
}
<form id='converter'>
<fieldset>
<legend>
<h1>Interplanetary<br>Weight Converter</h1>
</legend>
<input id="lbs" name='weight' placeholder=" Weight in lbs." type="number" min='0' max='99999'><label for='lbs'>lbs.</label>
<input id="kg" name='weight' placeholder="Weight in kg." type="number" min='0' max='99999'><label for='kg'>kg</label>
</fieldset>
<fieldset>
<legend>
<h2>The Solar System</h2>
</legend>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
</fieldset>
</form>
The script must look something like this:
<script>
let elements = document.querySelectorAll(".dynamic")
elements.forEach(el=>{
let child = document.createElement('div')
el.appendChild(child)
})
</script>
This can be simplified quite a bit, the buttons' innerHTML values match what you want in the new unit elements.
<button id="kg" class="btn-form" type="button">Kg</button>
<button id="lbs" class="btn-form" type="button">Lbs</button>
Use JavaScript to add an event listener to both unit buttons that call the same function which will update the innerHTML of the unit headings to match the clicked button. When initializing the unit heading elements you should not give them all the same id but rather a common class and you must also append a cloned copy of the element or it will just move the element form one spot to the next:
document.querySelectorAll('#kg, #lbs').forEach(ub => ub.addEventListener('click', units));
function units(e) {
let units = document.querySelectorAll(".value.unit");
if (units.length == 0) {
let newElement = document.createElement("h3");
newElement.setAttribute("class", "value unit");
document.querySelectorAll(".dynamic").forEach((dyn) => {
// append a cloned copy to each, not the same newElement
dyn.appendChild(newElement.cloneNode())
});
// re-run the query to find the newly added nodes
units = document.querySelectorAll(".value.unit");
}
// set the content
units.forEach(unit => unit.innerHTML = e.target.innerHTML);
}
//Variables
const mercury = document.getElementById("mercury");
const venus = document.getElementById("venus");
const earth = document.getElementById("earth");
const mars = document.getElementById("mars");
const jupiter = document.getElementById("jupiter");
const saturn = document.getElementById("saturn");
const uranus = document.getElementById("uranus");
const neptune = document.getElementById("neptune");
const weight = document.getElementById("weight");
weight.addEventListener("input", Calc);
function Calc() {
if (weight.value > 99999) {
alert("Max Amount Of Numbers is 99999");
weight.value = "";
} else {
var val = weight.value;
console.log(val);
var calculate = val * 0.38;
calculate = Math.round(calculate);
mercury.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.9;
calculate = Math.round(calculate);
venus.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 1;
calculate = Math.round(calculate);
earth.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.38;
calculate = Math.round(calculate);
mars.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 2.36;
calculate = Math.round(calculate);
jupiter.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.92;
calculate = Math.round(calculate);
saturn.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.89;
calculate = Math.round(calculate);
uranus.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 1.12;
calculate = Math.round(calculate);
neptune.innerHTML = calculate;
console.log(calculate);
}
}
document.querySelectorAll('#kg, #lbs').forEach(ub => ub.addEventListener('click', units));
function units(e) {
let units = document.querySelectorAll(".value.unit");
if (units.length == 0) {
let newElement = document.createElement("h3");
newElement.setAttribute("class", "value unit");
document.querySelectorAll(".dynamic").forEach((dyn) => {
// append a cloned copy to each, not the same newElement
dyn.appendChild(newElement.cloneNode())
});
// re-run the query to find the newly added nodes
units = document.querySelectorAll(".value.unit");
}
// set the content
units.forEach(unit => unit.innerHTML = e.target.innerHTML);
}
#import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
#font-face {
font-family: SpaceQuest;
src: url(https://raw.githubusercontent.com/Lemirq/WODP/master/Fonts/SpaceQuest-yOY3.woff);
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: url(./dario-bronnimann-hNQwIirOseE-unsplash.jpg);
}
/* Handle */
::-webkit-scrollbar-thumb {
background: rgba(59, 59, 59, 0.741);
border-radius: 200px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: rgb(255, 255, 255);
}
* {
--c-light: #f4f4f4;
--c-dark: #141414;
--c-blue: rgb(10, 132, 255);
--f-body: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
--trans-ease-in-out: all 0.2s ease-in-out;
color: var(--c-light);
}
body {
background-image: url(https://raw.githubusercontent.com/Lemirq/WODP/master/images/dario-bronnimann-hNQwIirOseE-unsplash.jpg);
margin: 0;
inset: 50px;
font-family: var(--f-body);
}
a {
color: var(--c-light);
text-decoration: none;
}
/***** NAVBAR *****/
.nav-item {
transition: var(--trans-ease-in-out);
}
.ext-link {
cursor: alias;
}
.nav-item:hover {
color: var(--c-dark);
background-color: var(--c-light);
}
.nav-item:hover li {
color: var(--c-dark);
}
.nav-item.icon-link:hover i {
color: var(--c-dark);
}
.nav-item:not(:last-child) {
margin-right: 20px;
}
navbar {
display: flex;
flex-direction: row;
justify-content: end;
align-items: center;
padding: 20px;
}
.nav-item-container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
}
.nav-item {
display: inline-block;
list-style: none;
padding: 10px;
font-size: 20px;
border-radius: 10px;
}
.gh-icon {
font-size: 30px;
}
/***** End NAVBAR *****/
/***** Main *****/
#wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
font-family: SpaceQuest, sans-serif;
font-size: 3.5rem;
}
.input-group {
border: 2px var(--c-light) solid;
border-radius: 10px;
/* max-width: 400px; */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 50px;
}
[type="number"]:focus {
outline: none;
}
[type="number"] {
font-size: 20px;
padding: 5px;
background-color: rgba(217, 217, 217, 0.2);
border: none;
font-family: var(--f-body);
min-width: 280px;
}
.btn-form {
font-size: 20px;
padding: 5px;
background-color: rgba(217, 217, 217, 0.2);
border: none;
transition: var(--trans-ease-in-out);
cursor: pointer;
font-family: var(--f-body);
}
.btn-form:hover {
background-color: rgba(217, 217, 217, 0.4);
}
.btn-form:first {
border-right: var(--c-light) 1px solid;
}
.input-group-text {
background-color: rgba(217, 217, 217, 0.2);
font-size: 17px;
padding: 7px;
}
/***** End Main *****/
/***** CARDS *****/
.card-container {
margin: 50px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
align-items: center;
grid-gap: 10px;
width: calc(100vw - 200px);
}
.card {
background-color: #141414;
width: auto;
height: auto;
border-radius: 10px;
padding: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175);
}
.planet-info {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.planet {
font-size: 50px;
margin: 0;
text-transform: capitalize;
}
.planet-img {
width: 80px;
height: auto;
margin-right: 30px;
}
[src="./images/planets/Saturn.png"] {
height: 79.25px;
width: auto;
}
.weight {
margin-top: 10px;
text-transform: capitalize;
font-size: 20px;
}
.weight::after {
content: ":";
}
.divider {
height: 1px;
width: 100%;
margin: 20px 0;
background-color: var(--c-light);
}
.value {
font-size: 60px;
color: var(--c-blue);
}
.dynamic>.value:nth-child(2) {
margin-left: 10px;
}
.dynamic {
display: flex;
flex-direction: row;
justify-content: space-between;
}
/***** End CARDS *****/
.input-error {
outline: 1px solid red;
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.8.3/font/bootstrap-icons.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<navbar>
<ul class="nav-item-container">
<a target="_blank" class="nav-item ext-link" href="https://lemirq.github.io">
<li>Website</li>
</a>
<a target="_blank" class="nav-item icon-link ext-link" href="https://github.com/Lemirq">
<li><i class="bi bi-github gh-icon"></i></li>
</a>
</ul>
</navbar>
<div id="wrapper">
<h1 id="vs-h1">Your Weight On Different Planets</h1>
<div class="input-group">
<input id="weight" placeholder="Enter your Weight" type="number">
<button id="kg" class="btn-form" type="button">Kg</button>
<button id="lbs" class="btn-form" type="button">Lbs</button>
</div>
<div class="card-container">
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Mercury.png" alt="EARTH">
<h3 class="planet">mercury</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="mercury" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Venus.png" alt="EARTH">
<h3 class="planet">venus</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="venus" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Earth.png" alt="EARTH">
<h3 class="planet">earth</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="earth" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Mars.png" alt="EARTH">
<h3 class="planet">mars</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="mars" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Jupiter.png" alt="EARTH">
<h3 class="planet">jupiter</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="jupiter" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Saturn.png" alt="EARTH">
<h3 class="planet">saturn</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="saturn" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Uranus.png" alt="EARTH">
<h3 class="planet">uranus</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="uranus" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Neptune.png" alt="EARTH">
<h3 class="planet">neptune</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="neptune" class="value">F</h3>
</div>
</div>
</div>
</div>

Problem with deleting object from array of objects

I have a issue where deleting dynamically created dom object from an array of objects. The problem is that when i delete some element from the array the rest of the elements after the spliced element also gets deleted.
To my knowledge this is happening due to the index of the next elements gets updated to the one before it and the function deletes the element having the same index over and over.
How can i fix this?(Code uploaded w HTML and CSS incl.)
Is this the recommended way to implement this function?
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
const cards = document.querySelectorAll('[data-index]');
cards.forEach(card => {
const removeButton = card.querySelector('.button');
removeButton.addEventListener('click', () => {
removeBook(book.id)
})
})
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
Full code
const form = document.getElementById('input-form');
const formButton = document.getElementById('add-form');
const formView = document.querySelector('.form-card')
const bookList = document.querySelector('.books-wrapper');
let myLibrary = [];
let newBook;
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
this.info = function() {
return `${this.title} is a book by ${this.author}, ${this.pages} pages, not read yet.`
};
};
Book.prototype.read = false;
function addToLibrary(e) {
{
e.preventDefault();
const title = (document.getElementById('title')).value;
const author = (document.getElementById('author')).value;
const pages = (document.getElementById('pages')).value;
newBook = new Book(title, author, pages);
} {
myLibrary.push(newBook);
populateBooks(myLibrary, bookList);
formDisplay();
form.reset();
console.table(myLibrary)
}
};
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
const cards = document.querySelectorAll('[data-index]');
cards.forEach(card => {
const removeButton = card.querySelector('.button');
removeButton.addEventListener('click', () => {
removeBook(book.id)
})
})
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
function formDisplay() {
form.reset();
formView.classList.toggle('toggle-on');
};
const theHobbit = new Book('The Hobbit', 'J.R.R. Tolkien', 295);
myLibrary.push(theHobbit)
const harryPotter = new Book('Harry Potter', 'J.K Rowling', 320);
myLibrary.push(harryPotter)
const sangaf = new Book('The Subtle Art of Not Giving a Fuck', 'Mark Manson', 300)
myLibrary.push(sangaf)
document.addEventListener("DOMContentLoaded", function() {
form.addEventListener("submit", function(e) {
addToLibrary(e)
});
});
formButton.addEventListener('click', formDisplay);
populateBooks(myLibrary, bookList);
#font-face {
font-family: "fanwood";
font-style: normal;
font-weight: normal;
src: url("fonts/Fanwood.otf");
font-display: swap;
}
:root {
--color-primary: #e9e2d7;
--color-primary-alt: #8e6549;
--color-secondary: #d42257;
--color-background: #d2fbf7;
--color-text: #412d86;
--color-light: #fff;
--color-anchor: #3a00ff;
--font-family: "fanwoood";
--font-weight-strong: 500;
--font-size-h1: 4rem;
--font-size-h2: 3rem;
--font-size-h3: 2rem;
--font-size-h4: 1.35rem;
--font-size-text: 1.15rem;
--border-radius: 8px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
html {
overflow-x: hidden;
}
/* Set core body defaults */
body {
font-family: 'fanwood';
min-height: 100vh;
font-size: 100%;
line-height: 1.5;
text-rendering: optimizeSpeed;
overflow-x: hidden;
}
/* Make images easier to work with */
img {
display: block;
max-width: 100%;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
body {
background-color: var(--color-primary);
}
button {
background-color: var(--color-primary);
border: none;
margin: 0;
}
input {
width: 100%;
margin-bottom: 10px 0;
}
.site-wrapper {
margin: 0 4%;
}
.card-info-wrapper {
margin: 4% 4%;
text-align: left;
}
.card-menu {
align-self: flex-end;
margin: 4% 4%;
}
.header {
color: var(--color-primary);
background-color: var(--color-primary-alt);
height: 84px;
display: flex;
align-items: center;
justify-content: center;
}
.tool-bar {
margin-top: 20px;
}
.tools {
display: flex;
}
.button {
cursor: pointer;
display: inline-flex;
padding: 2px 8px;
color: var(--color-primary-alt);
background-color: var(--color-primary);
}
.button.add {
display: inline-flex;
padding: 2px 8px;
background-color: var(--color-primary-alt);
color: var(--color-primary);
}
.books-wrapper {
margin-top: 20px;
/* border: 1px solid white; */
display: flex;
flex-wrap: wrap;
}
.book-card {
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.form-card {
display: none;
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
}
.toggle-on {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Book</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<div class="site-wrapper">
<div class="header-logo-container">
<h1>Library</h1>
</div>
</div>
</div>
<div class="tool-bar">
<div class="site-wrapper">
<div class="tools">
<div class="button add" id="add-form">
Add Book
</div>
</div>
</div>
</div>
<div class="books">
<div class="site-wrapper">
<div class="books-wrapper">
<!-- TEMPLATE FOR BOOK CARD -->
<!-- <div class="book-card">
<div class="card-info-wrapper">
<h2>Title</h2>
<h3>Author</h3>
<h4>Pages</h4>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ipsum fugit officiis animi soluta et, sit aliquid.</p>
</div>
<div class="card-menu">
<div class="button">
Remove
</div>
</div>
</div> -->
<div class="form-card">
<div class="card-info-wrapper">
<form id="input-form">
<label for="title"><h3>Title</h3></label>
<input type="text" id="title" name="title" placeholder="Name of the Book" required>
<label for="author"><h3>Author</h3></label>
<input type="text" id="author" name="author" placeholder="Name of the Author" required>
<label for="pages"><h3>Pages</h3></label>
<input type="number" id="pages" name="pages" placeholder="Number of Pages" required>
<button type="submit" class="button" id="addBook">Add Book</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
The problem with your code is that for every card aded in populateBook, you loop all the previous cards and add a click event listener, which means the second book gets 2 copies of this handler, the third 3 etc.
Instead of doing that, add a single event handler for clicking and handle appropriately:
document.querySelector(".books-wrapper").addEventListener("click", (e) => {
if(e.target.classList.contains("button")){
const index = e.target.parentElement.parentElement.dataset.index;
removeBook(index);
}
});
Live example:
const form = document.getElementById('input-form');
const formButton = document.getElementById('add-form');
const formView = document.querySelector('.form-card')
const bookList = document.querySelector('.books-wrapper');
let myLibrary = [];
let newBook;
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
this.info = function() {
return `${this.title} is a book by ${this.author}, ${this.pages} pages, not read yet.`
};
};
Book.prototype.read = false;
function addToLibrary(e) {
{
e.preventDefault();
const title = (document.getElementById('title')).value;
const author = (document.getElementById('author')).value;
const pages = (document.getElementById('pages')).value;
newBook = new Book(title, author, pages);
} {
myLibrary.push(newBook);
populateBooks(myLibrary, bookList);
formDisplay();
form.reset();
console.table(myLibrary)
}
};
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
function formDisplay() {
form.reset();
formView.classList.toggle('toggle-on');
};
const theHobbit = new Book('The Hobbit', 'J.R.R. Tolkien', 295);
myLibrary.push(theHobbit)
const harryPotter = new Book('Harry Potter', 'J.K Rowling', 320);
myLibrary.push(harryPotter)
const sangaf = new Book('The Subtle Art of Not Giving a Fuck', 'Mark Manson', 300)
myLibrary.push(sangaf)
document.addEventListener("DOMContentLoaded", function() {
form.addEventListener("submit", function(e) {
addToLibrary(e)
});
document.querySelector(".books-wrapper").addEventListener("click", (e) => {
if(e.target.classList.contains("button")){
const index = e.target.parentElement.parentElement.dataset.index;
removeBook(index);
}
})
});
formButton.addEventListener('click', formDisplay);
populateBooks(myLibrary, bookList);
#font-face {
font-family: "fanwood";
font-style: normal;
font-weight: normal;
src: url("fonts/Fanwood.otf");
font-display: swap;
}
:root {
--color-primary: #e9e2d7;
--color-primary-alt: #8e6549;
--color-secondary: #d42257;
--color-background: #d2fbf7;
--color-text: #412d86;
--color-light: #fff;
--color-anchor: #3a00ff;
--font-family: "fanwoood";
--font-weight-strong: 500;
--font-size-h1: 4rem;
--font-size-h2: 3rem;
--font-size-h3: 2rem;
--font-size-h4: 1.35rem;
--font-size-text: 1.15rem;
--border-radius: 8px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
html {
overflow-x: hidden;
}
/* Set core body defaults */
body {
font-family: 'fanwood';
min-height: 100vh;
font-size: 100%;
line-height: 1.5;
text-rendering: optimizeSpeed;
overflow-x: hidden;
}
/* Make images easier to work with */
img {
display: block;
max-width: 100%;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
body {
background-color: var(--color-primary);
}
button {
background-color: var(--color-primary);
border: none;
margin: 0;
}
input {
width: 100%;
margin-bottom: 10px 0;
}
.site-wrapper {
margin: 0 4%;
}
.card-info-wrapper {
margin: 4% 4%;
text-align: left;
}
.card-menu {
align-self: flex-end;
margin: 4% 4%;
}
.header {
color: var(--color-primary);
background-color: var(--color-primary-alt);
height: 84px;
display: flex;
align-items: center;
justify-content: center;
}
.tool-bar {
margin-top: 20px;
}
.tools {
display: flex;
}
.button {
cursor: pointer;
display: inline-flex;
padding: 2px 8px;
color: var(--color-primary-alt);
background-color: var(--color-primary);
}
.button.add {
display: inline-flex;
padding: 2px 8px;
background-color: var(--color-primary-alt);
color: var(--color-primary);
}
.books-wrapper {
margin-top: 20px;
/* border: 1px solid white; */
display: flex;
flex-wrap: wrap;
}
.book-card {
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.form-card {
display: none;
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
}
.toggle-on {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Book</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<div class="site-wrapper">
<div class="header-logo-container">
<h1>Library</h1>
</div>
</div>
</div>
<div class="tool-bar">
<div class="site-wrapper">
<div class="tools">
<div class="button add" id="add-form">
Add Book
</div>
</div>
</div>
</div>
<div class="books">
<div class="site-wrapper">
<div class="books-wrapper">
<!-- TEMPLATE FOR BOOK CARD -->
<!-- <div class="book-card">
<div class="card-info-wrapper">
<h2>Title</h2>
<h3>Author</h3>
<h4>Pages</h4>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ipsum fugit officiis animi soluta et, sit aliquid.</p>
</div>
<div class="card-menu">
<div class="button">
Remove
</div>
</div>
</div> -->
<div class="form-card">
<div class="card-info-wrapper">
<form id="input-form">
<label for="title"><h3>Title</h3></label>
<input type="text" id="title" name="title" placeholder="Name of the Book" required>
<label for="author"><h3>Author</h3></label>
<input type="text" id="author" name="author" placeholder="Name of the Author" required>
<label for="pages"><h3>Pages</h3></label>
<input type="number" id="pages" name="pages" placeholder="Number of Pages" required>
<button type="submit" class="button" id="addBook">Add Book</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>

How can I create a list where I can add and remove inputs via buttons?

I've an idea. For this I need a list like this draw:
So the main idea is to have a plus and a minus button. When a users presses the plus button, another input get's added. When pressing the minus button beside each input, the related input should be removed.
So I've startet with this here but I'm not very with it and the functionality is not given yet. How can I deal with this a smart way? Is there a problem with the id's? I mean I could copy a row or insert it (with JS) but how can I get the values later of all inputs in one map (with JS) for example? A lot of questions..
.out-task {
display: flex;
margin-bottom: 8px;
}
.delete-task-button {
margin-left: 6px;
background: red;
}
.button {
width: 30px;
height: 30px;
display: block;
border-radius: 50%;
color: white;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.add-task-button {
background: green;
}
<div class="wrapper">
<label>Tasks</label>
<div id="tasks-wrapper">
<div class="out-task">
<input type="text" id="task" name="task" value="">
<span class="delete-task-button button">-</span>
</div>
</div>
<span class="add-task-button button">+</span>
</div>
Thanks for helping me out!!!
As I have criticized everyone, I let you do the same on my code:
const ListTasks = document.querySelector('#wrapper > div')
, PosInsertTask = document.querySelector('#wrapper > div > span.add-task-button')
, taskWrapper = document.querySelector('#template > div')
;
ListTasks.onclick=e=>
{
if (e.target.classList.contains('add-task-button'))
{
let newTask = taskWrapper.cloneNode(true)
ListTasks.insertBefore(newTask, PosInsertTask)
}
else if (e.target.classList.contains('delete-task-button'))
{
ListTasks.removeChild(e.target.closest('.out-task'))
}
}
.out-task {
display : flex;
margin-bottom: 8px;
}
.delete-task-button {
margin-left: 6px;
background : red;
}
.button {
width : 30px;
height : 30px;
display : block;
border-radius : 50%;
color : white;
display : flex;
justify-content: center;
align-items : center;
cursor : pointer;
font-weight : bold;
}
#wrapper { display: inline-block; border: 1px solid grey; padding:.8em;}
#wrapper h4 { text-align: center; }
.add-task-button {
background: green;
margin: auto;
}
#template { display: none;}
<div id="wrapper">
<h4>Tasks</h4>
<div>
<div class="out-task">
<input type="text" value="">
<span class="delete-task-button button">-</span>
</div>
<span class="add-task-button button">+</span>
</div>
</div>
<div id="template">
<div class="out-task">
<input type="text" value="">
<span class="delete-task-button button">-</span>
</div>
</div>

Custom dropdown isn't working as expected

I wrote a custom drop down for a phone-gap project. It has 3 entries: Enterprices Routing & Switches, Junos Security and Service Provider Routing and Switching. Whenever I select any one of 3 entries, it gets selected successfully, however It shows selected entry twice. I want selected entry to be shown only once. My JavaScript code is:
$(".current_track").click(function() {
if ($('.track').is(':visible')) {
$(".track").hide();
$(".trackIcon").removeClass("trackOpenIcon").addClass("trackCloseIcon");
if ($('.trackName').text() != "Select Track") {
$(".category").show();
$('.lock_hide').show();
$('#TrackBuy').show();
}
} else {
$(".category").hide();
$('.lock_hide').show();
$('#TrackBuy').hide();
$(".track").show();
$('.lock_hide').show();
$(".trackIcon").removeClass("trackCloseIcon").addClass("trackOpenIcon");
}
});
$('.trackDiv').on("click", ".track", function() {
$('.trackName').text($(this).text());
$('.trackName').attr("id", $(this).attr("id"));
$(".track").hide();
$(".trackIcon").removeClass("trackCloseIcon").addClass("trackOpenIcon");
$('.lock_hide').show();
// saving user selected/clicked trackid and track name to local stroage
localStorage.setItem("mainTrackid", $(this).attr("id"));
localStorage.setItem("mainTrackname", $(this).text());
localStorage.setItem("selectedTrackPayStatus", $(this).attr("payStatus"));
localStorage.removeItem("selectedTrackPrice");
// check first paper exist or not if exist means load exams list
checkTrackpapersExist();
});
Entire code example.
Updated fiddle.
You could use an extra class selected to do the trick.
Remove selected class from all the track's and add it to the clicked one :
$('.trackDiv').on("click", ".track", function() {
$('.track').removeClass('selected');
$(this).addClass('selected');
$('.trackName').html($(this).html()); //Use '.html()' to show the icon
....
});
Hide the selected track from the dropdown :
$(".current_track").click(function() {
.....
$('.track.selected').hide();
});
Hope this helps.
$(".current_track").click(function() {
if ($('.track').is(':visible')) {
$(".track").hide();
$(".trackIcon").removeClass("trackOpenIcon").addClass("trackCloseIcon");
if ($('.trackName').text() != "Select Track") {
$(".category").show();
$('.lock_hide').show();
$('#TrackBuy').show();
}
} else {
$(".category").hide();
$('.lock_hide').show();
$('#TrackBuy').hide();
$(".track").show();
$('.lock_hide').show();
$(".trackIcon").removeClass("trackCloseIcon").addClass("trackOpenIcon");
}
$('.track.selected').hide();
});
$('.trackDiv').on("click", ".track", function() {
$('.track').removeClass('selected');
$(this).addClass('selected');
$('.trackName').html($(this).html());
$('.trackName').attr("id", $(this).attr("id"));
$(".track").hide();
$(".trackIcon").removeClass("trackCloseIcon").addClass("trackOpenIcon");
$('.lock_hide').show();
});
.trackDiv > .track > .fa-lock {
margin-top: 3px;
}
.fa-lock {
color: #fff;
font-size: 23px;
}
.lock_category {
padding-right: 0px;
padding-top: 3px;
}
.lock_hide {
display: none;
}
.fa-unlock {
color: #fff;
font-size: 17px;
}
.trackDiv {
background-color: #374550;
border-radius: 4px;
height: 40%;
margin: 2% 0 0;
padding: 3% 2.7% 2.9%;
width: 100%;
}
.trackName {
color: #fff;
font-family: "robotRegular";
font-size: 16px;
font-weight: 500;
}
.trackOpenIcon {
background-image: url("../JunosImages/mob/down-arrow_normal1.png");
background-image: url("../JunosImages/mob/front-arrow_normal_tab.png");
background-repeat: no-repeat;
background-size: 100% 100%;
display: block;
height: 11px;
margin-top: 1.5%;
width: 19px;
}
.trackCloseIcon {
background-image: url("../JunosImages/mob/front-arrow_normal_tab.png");
background-repeat: no-repeat;
background-size: 100% 100%;
display: block;
height: 18px;
margin-top: 0.7%;
width: 12px;
}
.track {
color: #fff;
display: block;
font-family: "robotRegular";
font-size: 16px;
font-weight: 500;
padding: 5% 0 0;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<div id="categorylist">
<div class="trackDiv">
<div class="current_track">
<span class="trackName">Select Track</span>
<span class="trackIcon trackOpenIcon pull-right"></span>
<div><span class="label label-primary Buy" id="TrackBuy" style="display:none">Buy $24</span></div>
</div>
<span id="t1" class="track">Enterprices Routing & Switches <span class="fa fa-lock pull-left"></span></span>
<span id="t2" class="track">Junos Security</span>
<!-- <span id="t3" class="track">Data Center Design</span> -->
<span id="t4" class="track"> Service Provider Routing and Switching</span>
</div>
<div class="category">
<span class="ctgryName">JNCIs - SEC</span>
<span class="ctgryIcon pull-right"></span>
</div>
<div class="category">
<span class="ctgryName">JNCIS - ENT</span>
<span class="ctgryIcon pull-right"></span>
</div>
<div class="category">
<span class="ctgryName">JNCIS - ENT</span>
<span class="ctgryIcon pull-right"></span>
</div>
<div class="category">
<span class="ctgryName" id="ctgryName">JNCIA - Junos</span>
<span class="ctgryIcon pull-right"></span>
</div>
</div>

Categories

Resources