can anyone help me how can I accomplish this, My goal is
if I click the buttons it will addClass on the designated content.
For example, if I clicked the 1st button it should add a class on the
1st content and if I clicked the 2nd button it will add a class on the
2nd content. Currently, it's only adding on the 1st child because I've set it to [0] on my JS. Thank you and looking forward to your help. Just learning JS
const btns = document.querySelectorAll(".btn")
const content = document.querySelectorAll(".content")
btns.forEach(btn => {
btn.addEventListener("click", (e)=> {
const container = e.currentTarget.parentElement.parentElement.children[1].children[0]
container.classList.toggle("addClass")
})
})
.main-container {
display: flex;
align-items: center;
gap: 10px;
}
.btn {
background: blue;
width: 100px;
margin-bottom: 10px;
text-align: center;
padding: 5px;
border-radius: 3px;
color: #ffffff;
letter-spacing:1px;
font-weight: 100;
}
.content {
background: green;
width: 300px;
padding: 5px;
border-radius: 3px;
color: #ffffff;
margin-bottom: 10px;
}
.content.addClass {
background: red;
}
<div class="main-container">
<div class="btn-container">
<div class="btn first">Button</div>
<div class="btn second">Button</div>
<div class="btn third">Button</div>
<div class="btn fourth">Button</div>
</div>
<div class="content-container">
<div class="content">Content</div>
<div class="content">Content</div>
<div class="content">Content</div>
<div class="content">Content</div>
</div>
</div>
I strongly suggest you delegate from the button container and use data attributes to find the content
I added a remove and toggle
const contents = document.querySelectorAll(".content")
document.querySelector(".btn-container").addEventListener("click", (e) => {
const tgt = e.target;
const chosen = document.getElementById(tgt.dataset.id);
contents.forEach(content => {
if (content !== chosen) content.classList.remove("addClass")
})
chosen.classList.toggle("addClass")
})
.main-container {
display: flex;
align-items: center;
gap: 10px;
}
.btn {
background: blue;
width: 100px;
margin-bottom: 10px;
text-align: center;
padding: 5px;
border-radius: 3px;
color: #ffffff;
letter-spacing: 1px;
font-weight: 100;
}
.content {
background: green;
width: 300px;
padding: 5px;
border-radius: 3px;
color: #ffffff;
margin-bottom: 10px;
}
.content.addClass {
background: red;
}
<div class="main-container">
<div class="btn-container">
<div class="btn" data-id="first">Button</div>
<div class="btn" data-id="second">Button</div>
<div class="btn" data-id="third">Button</div>
<div class="btn" data-id="fourth">Button</div>
</div>
<div class="content-container">
<div class="content" id="first">Content</div>
<div class="content" id="second">Content</div>
<div class="content" id="third">Content</div>
<div class="content" id="fourth">Content</div>
</div>
</div>
const btns = document.querySelectorAll(".btn")
const content = document.querySelectorAll(".content")
btns.forEach(btn => {
btn.addEventListener("click", (e) => {
let id = e.target.getAttribute('data-id');
let container = content[id];
container.classList.toggle("addClass")
})
})
.main-container {
display: flex;
align-items: center;
gap: 10px;
}
.btn {
background: blue;
width: 100px;
margin-bottom: 10px;
text-align: center;
padding: 5px;
border-radius: 3px;
color: #ffffff;
letter-spacing:1px;
font-weight: 100;
}
.content {
background: green;
width: 300px;
padding: 5px;
border-radius: 3px;
color: #ffffff;
margin-bottom: 10px;
}
.content.addClass {
background: red;
}
<div class="main-container">
<div class="btn-container">
<div class="btn" data-id="0">Button</div>
<div class="btn" data-id="1">Button</div>
<div class="btn" data-id="2">Button</div>
<div class="btn" data-id="3">Button</div>
</div>
<div class="content-container">
<div class="content" data-id="0">Content</div>
<div class="content" data-id="1">Content</div>
<div class="content" data-id="2">Content</div>
<div class="content" data-id="3">Content</div>
</div>
</div>
You can try this one also
Just get the index from forEach callback and get the element with the index in the .content-container element, and toggle the class
const btns = document.querySelectorAll(".btn")
const content = document.querySelectorAll(".content")
const contentContainer = document.querySelector('.content-container');
btns.forEach((btn, index) => {
btn.addEventListener("click", (e)=> {
const container = contentContainer.children[index]
container.classList.toggle("addClass")
})
})
.main-container {
display: flex;
align-items: center;
gap: 10px;
}
.btn {
background: blue;
width: 100px;
margin-bottom: 10px;
text-align: center;
padding: 5px;
border-radius: 3px;
color: #ffffff;
letter-spacing:1px;
font-weight: 100;
}
.content {
background: green;
width: 300px;
padding: 5px;
border-radius: 3px;
color: #ffffff;
margin-bottom: 10px;
}
.content.addClass {
background: red;
}
<div class="main-container">
<div class="btn-container">
<div class="btn first">Button</div>
<div class="btn second">Button</div>
<div class="btn third">Button</div>
<div class="btn fourth">Button</div>
</div>
<div class="content-container">
<div class="content">Content</div>
<div class="content">Content</div>
<div class="content">Content</div>
<div class="content">Content</div>
</div>
</div>
I also add a forEach for my content so when I tried click the other button it will remove the existing class that I added and add it on the btn that designated the other content
const btns = document.querySelectorAll(".btn")
const content = document.querySelectorAll(".content")
btns.forEach((btn, index) => {
btn.addEventListener("click", (e)=> {
const container = e.currentTarget.parentElement.parentElement.children[1].children[index]
//remove class
content.forEach((item, indexItem) => {
if(index !== indexItem) {
item.classList.remove("addClass")
}
})
container.classList.toggle("addClass")
})
})
.main-container {
display: flex;
align-items: center;
gap: 10px;
}
.btn {
background: blue;
width: 100px;
margin-bottom: 10px;
text-align: center;
padding: 5px;
border-radius: 3px;
color: #ffffff;
letter-spacing: 1px;
font-weight: 100;
}
.content {
background: green;
width: 300px;
padding: 5px;
border-radius: 3px;
color: #ffffff;
margin-bottom: 10px;
}
.content.addClass {
background: red;
}
<div class="main-container">
<div class="btn-container">
<div class="btn" data-id="first">Button</div>
<div class="btn" data-id="second">Button</div>
<div class="btn" data-id="third">Button</div>
<div class="btn" data-id="fourth">Button</div>
</div>
<div class="content-container">
<div class="content" id="first">Content</div>
<div class="content" id="second">Content</div>
<div class="content" id="third">Content</div>
<div class="content" id="fourth">Content</div>
</div>
</div>
Related
I created 2 Modals.The first modal, which is class = "modal" appears as it should when add button is clicked, but when I click the "+" sign on the form to display the next Modal which is the class = "category-modal that modal goes back to display none for some reason.
I made a runable code:
//get HTML elements username
let arrow = document.querySelector(".next");
let profSetting = document.querySelector(".prof-settings ul");
let asset = document.querySelector(".nav-side-asset");
//click event for username drop-down
arrow.addEventListener("click", () => {
arrow.classList.toggle('rotate-90');
profSetting.classList.toggle('transform');
asset.classList.toggle('position');
modal.classList.toggle('modal-position');
});
//get HTML elements assets
let arrowAsset = document.querySelector(".next-asset");
let assets = document.querySelector(".assets ul");
//click event for asset drop-down
arrowAsset.addEventListener("click", () => {
arrowAsset.classList.toggle('rotate-90');
assets.classList.toggle('transform');
})
//Generate tag ID
//get HTML elements modal
let modal = document.querySelector(".modal");
let addbtn = document.querySelector(".add-button");
let close = document.querySelector(".close");
let background = document.querySelector(".greyback")
//click event for modal pop up
addbtn.addEventListener("click", () => {
modal.style.display = "block";
genID()
background.style.display = "block";
})
//click event for modal remove itself
close.addEventListener("click", () => {
modal.style.display = "none";
})
function genID() {
let minNum = 0;
let maxNum = 1000;
let randomNum = Math.floor(Math.random() * (maxNum + 1) + minNum)
document.querySelector(".id").innerHTML = randomNum;
}
//get modal for category and department
let categoryModal = document.querySelector(".category-modal");
let categoryAdd = document.querySelector(".category-add");
let cancel = document.querySelector(".cancel");
categoryAdd.addEventListener("click", () => {
categoryModal.style.display = "block";
})
body {
margin: 0;
box-sizing: border-box;
font-family: sans-serif;
background-color: var(--light-purplr);
}
:root {
--dark-purple: #3B3F8C;
--light-purplr: #4449A6;
--light-green: #6BBF54;
--darkish-green: #6FA656;
--lighter-white: #F2F2F2;
--light-white: #e9e9e9;
}
.greyback {
background-color: rgb(128, 128, 128, 0.7);
width: 120em;
height: 60.55em;
position: absolute;
display: none;
z-index: 1;
}
.nav-top {
background-color: var(--lighter-white);
display: grid;
grid-template-columns: 15em 70em 0em;
margin-left: -1em;
}
.nav-top ul {
display: flex;
width: 20vw;
margin-top: 1.5em;
}
.nav-top li {
list-style-type: none;
margin-left: 1em;
display: flex;
cursor: pointer;
padding: 8px 8px 8px 8px;
}
.nav-top li:hover {
background-color: var(--light-white);
}
.nav-banner img {
margin-left: 2em;
margin-top: 1em;
width: 14em;
height: 3em;
}
.nav-top ul img {
width: 1em;
height: 1em;
}
.profile {
width: 2em !important;
height: 2em !important;
margin-top: -0.5em;
}
.nav-side,
.nav-side-asset {
background-color: var(--lighter-white);
width: 13em;
height: 2.5em;
}
.nav-side ul,
.nav-side-asset ul {
background-color: var(--lighter-white);
position: absolute;
height: 2.5em;
margin-top: 8px;
width: 10.5em;
}
.nav-side li,
.nav-side-asset li {
list-style-type: none;
margin-top: 10px;
margin-left: 5px;
}
.profile-side,
.asset-side {
display: flex;
}
.profile-side img,
.asset-side img {
width: 1.5em;
height: 1.5em;
margin-left: 1em;
margin-top: 0.5em;
}
.profile-side p,
.asset-side p {
margin-left: 3em;
margin-top: 0.8em;
position: absolute;
}
.next,
.next-asset {
margin: 1em 11em !important;
width: 10px !important;
height: 10px !important;
position: absolute;
cursor: pointer;
transition: .2s transform ease;
}
.prof-settings ul,
.assets ul {
background-color: var(--lighter-white);
padding-bottom: 3em;
cursor: pointer;
display: none;
}
.assets li {
display: flex;
}
.assets img {
padding-right: 1em;
}
.list {
width: 1em;
}
.add-asset {
width: 1em;
margin-left: -1px;
}
.in,
.out {
width: 1.3em;
margin-left: -4px;
}
.assets ul {
padding-bottom: 6em !important;
}
.prof-settings li:hover,
.assets li:hover {
color: var(--light-green);
}
.rotate-90 {
transform: rotate(90deg);
}
.transform {
display: block !important;
}
.position {
margin-top: 5em;
}
.modal {
position: absolute;
/*display: none;*/
z-index: 2
}
.asset-modal-box {
background-color: var(--light-white);
width: 40em;
height: 40em;
margin-left: 40em;
}
.modal-position {
margin-top: -5em;
}
.form {
padding: 1em;
}
.section-left {
position: absolute;
width: 7em;
}
.section-right {
position: relative;
margin-left: 20em;
margin-top: 2.1em;
}
.brand-container,
.model-container,
.serial-container {
margin-bottom: 1em;
}
.item-container,
.date-purchased-container,
.cost-container,
.tag-container,
.tag-container {
margin-bottom: 1em;
}
.description-container,
.model-container,
.serial-container,
.brand-container,
.item-container,
.date-purchased-container,
.tag-container,
.cost-container {
display: grid;
}
.description-container {
margin-top: 3em;
margin-bottom: 1em;
}
.tag-container {
display: flex;
}
.item {
width: 20em;
}
.l-purchased {
width: 8em;
}
.currency {
background-color: var(--darkish-green);
width: 2em;
}
.rand {
display: flex;
height: 2em;
padding-left: 1em;
}
.rand p {
margin-top: 7px;
margin-left: -6px;
}
.cost {
margin-left: 1em;
position: absolute;
}
.item,
.brand,
.model,
.serial,
.date,
.cost,
.description,
.purchased,
.tag {
height: 26px;
}
.buttons {
margin: 0em 1em;
padding-top: 12em;
}
.submit,
.close {
margin-left: 6em;
padding: 1em 2em;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
border: none;
background-color: var(--light-green);
}
.close:hover {
background-color: red;
}
.department {
margin-left: 4.3em;
}
select {
width: 10em;
}
.category-modal {
background-color: var(--light-green);
width: 37em;
padding: 7px;
position: absolute;
margin-top: -32em;
display: none;
}
.category-modal h3 {
color: var(--dark-purple);
}
.line,
.line-bottom {
border-top: 1px solid black;
}
.line-bottom {
margin-top: 1em;
}
.category-input {
padding: 5px;
margin-left: 28.8em;
margin-top: 1em;
}
.add,
.cancel {
width: 4em;
border: none;
background-color: var(--dark-purple);
border-radius: 3px;
cursor: pointer;
margin-top: 1em;
color: var(--light-white);
padding: 5px;
}
.add {
margin-left: 33em;
}
.cancel {
margin-left: 1em;
}
.close-category {
position: absolute;
margin-top: -2.5em;
margin-left: 35em;
cursor: pointer;
}
<div class="greyback"></div>
<header>
<nav class="nav-top">
<div class="nav-banner">
<img src="./references/images/CS247-Pastel-Logo (1).jpg" alt="CS247 Logo">
</div>
<ul class="left">
<li><img src="./references/images/List icon.png" alt="list icon">List of Assets</li>
<li class="add-button"><img src="./references/images/add icon.png" alt="Add button">Add an Asset</li>
<li><img src="./references/images/Search.png" alt="Search">Search</li>
</ul>
<ul class="right">
<li><img src="./references/images/clock.png" alt="clock">Changelog</li>
<li><img src="./references/images/tag.png" alt="clock">Buy Asset Tags</li>
<li><img class="profile" src="./references/images/profile pic.png" alt="profile pic"></li>
</ul>
</nav>
</header>
<main>
<aside>
<nav class="nav-side">
<div class="profile-side">
<img src="./references/images/profile pic.png" alt="profile pic">
<img class="next" src="./references/images/next.png" alt="next">
<p>Username</p>
</div>
<div class="prof-settings">
<ul>
<li>My Profile</li>
<li>Change Password</li>
<li>Log out</li>
</ul>
</div>
</nav>
<nav class="nav-side-asset">
<div class="asset-side">
<img src="./references/images/asset-management.png" alt="Asset icon">
<img class="next-asset" src="./references/images/next.png" alt="next">
<p>Assets</p>
</div>
<div class="assets">
<ul>
<li><img class="list" src="./references/images/List icon.png" alt="list icon">List of Assets</li>
<li><img class="add-asset" src="./references/images/add icon.png" alt="Add button">Add an Asset</li>
<li><img class="in" src="./references/images/checkbox-in.png" alt="in icon">Assets In</li>
<li><img class="out" src="./references/images/X-out.png" alt="out icon">Assets Out</li>
</ul>
</div>
</nav>
</aside>
<div class="modal">
<div class="asset-modal-box">
<form class="form">
<!--Left of form inputs and labels-->
<div class="section-left">
<div class="tag-container">
<label for="tag" class="l-tag">Tag ID:</label>
<label class="id"></label>
</div>
<div class="item-container">
<label for="item" class="l-item">Item</label>
<input type="text" name="item" class="item">
</div>
<div class="date-purchased-container">
<label for="item" class="l-purchased">Date of Purchase</label>
<input type="date" name="item" class="purchased">
</div>
<div class="cost-container">
<label for="item" class="l-cost">Cost</label>
<div class="currency">
<div class="rand">
<p>R</p>
<input type="text" name="item" class="cost">
</div>
</div>
</div>
</div>
<!--Right of form inputs and labels-->
<div class="section-right">
<div class="brand-container">
<label for="brand" class="l-brand">Brand</label>
<input type="text" name="brand" class="brand">
</div>
<div class="model-container">
<label for="model" class="l-model">Model</label>
<input type="text" name="Model" class="model">
</div>
<div class="serial-container">
<label for="serial" class="l-serial">Serial No</label>
<input type="text" name="serial" class="serial">
</div>
</div>
<div class="description-container">
<label for="description" class="l-description">Description</label>
<input type="text" name="serial" class="description">
</div>
<!--Selections-->
<div class="options">
<label class="category" for="category">Category
<select name="category" id="category" class="category">
</select>
<button class="category-add">+</button>
</label>
<label class="department" for="department">Department
<select name="department" id="department" class="department-select"></select>
<button class="department-add">+</button>
</label>
<!--Modal for select options input-->
<div class="category-modal">
<h3>Add Catagory</h3>
<div class="close-category">X</div>
<div class="line"></div>
<p>If you want to add a new category of assets, you're in the right spot. Add a category for computer equipment, wireless keyboards, or any assets you're working with.
</p>
<input type="text" class="category-input">
<div class="line-bottom"></div>
<button class="add">Add</button>
<button class="cancel">Cancel</button>
</div>
</div>
<!--Form Buttons-->
<div class="buttons">
<button class="submit">Add Item</button>
<button class="close">Close</button>
</div>
</form>
</div>
</div>
</main>
I have attached a screenshot on what buttons to press to get to the issue:
I installed the code you have given and played with it. I noticed that the issue you are facing is due to the page being refreshed, the normal behaviour when you click on a button inside a form, which you can prevent with e.preventDefault(). For categoryAdd as an example:
categoryAdd.addEventListener("click", (e) => {
e.preventDefault();
categoryModal.style.display = "block";
});
Add e.preventDefault() for each button that don't need to refresh the page, after passing the event e as parameter like I did above.
This question already has answers here:
Attach event to dynamic elements in javascript
(15 answers)
Closed 1 year ago.
I was creating a todo's list with JS. I have attached the code snippet for the same. I Have three hard coded todos in html and with each todo there are two buttons('x' to remove the todo 'y' to mark it as done) associated. Now for this hard coded todos everything is working fine. Now, to add a new todo I have this tag where todo text is entered and after clicking enter I am embedding them to html using innerHTML, while embedding the dynamically added todos are visible in DOM. But The problem is that the buttons('x' and 'y') associated with the todo is not working as expected. Further I tried to debug my problem and I found out that the buttons('x' and 'y') that are not getting selected. I could not understand why is this happening. Any help will be highly appreciated.
const todoInp = document.querySelector('.add input')
const todos = document.querySelector('.todos')
const remove = document.querySelectorAll('.remove')
const doneBtns = document.querySelectorAll('.check')
todoInp.addEventListener('keyup', (e) => {
if (e.key === 'Enter' && e.target.value !== '') {
const todoVal = e.target.value
const todo = document.createElement('div')
todo.classList.add('todo')
todo.innerHTML = `
<p>${todoVal}</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
`
todos.appendChild(todo)
todoInp.value = ''
}
})
doneBtns.forEach((btn) => {
btn.addEventListener('click', () => {
const mainPar = btn.parentElement.parentElement
mainPar.classList.toggle('done')
})
})
remove.forEach((btn) => {
btn.addEventListener('click', () => {
const parent = btn.parentElement
const mainPar = parent.parentElement
mainPar.remove()
})
})
* {
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 0;
padding: 0;
margin-top: 50px;
height: 100vh;
background-color: aliceblue;
}
.container{
width: 600px;
height: auto;
padding: 50px;
}
.add,
.todos{
display: flex;
flex-direction: column;
align-items: flex-start;
}
.add label{
font-family: 'Courier New', Courier, monospace;
font-weight: bold;
color: darkblue;
}
.add input{
background: white;
padding: 13px;
border: 0.5px solid #ccc;
width: 90%;
border-radius: 4px;
}
.add input:focus{
outline: none;
}
.add input::placeholder{
color:#ccc
}
.todo{
color: darkblue;
width: 90%;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
}
.todo:last-child{
border: 0
}
.todo p{
font-size: 24px;
font-weight: bold;
}
.buttons button{
background-color: transparent;
color: #aaa;
border: 0;
font-size: 24px;
cursor: pointer;
transition: all .5s ease-in-out;
}
.buttons button:hover{
color:black;
}
.todo.done p{
text-decoration: line-through;
text-decoration-thickness: 2px;
color: orangered;
text-decoration-style:double;
}
<div class="container">
<div class="add">
<label>ADD TODO</label>
<input type="text" placeholder="Write your To do">
</div>
<div class="todos">
<div class="todo">
<p>Todo 1</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
<div class="todo">
<p>Todo 2</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
<div class="todo">
<p>Todo 3</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
</div>
</div>
While delegation is a perfectly fine solution, I tend to take an (in my opionion) simpler approach, and simply bind the handlers to the new elements:
const todoInp = document.querySelector('.add input')
const todos = document.querySelector('.todos')
const remove = document.querySelectorAll('.remove')
const doneBtns = document.querySelectorAll('.check')
todoInp.addEventListener('keyup', (e) => {
if (e.key === 'Enter' && e.target.value !== '') {
const todoVal = e.target.value
const todo = document.createElement('div')
todo.classList.add('todo')
todo.innerHTML = `
<p>${todoVal}</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
`
todos.appendChild(todo)
bindRemove(todo.querySelector('.remove'));
bindDone(todo.querySelector('.check'));
todoInp.value = ''
}
})
const bindDone = (btn) => {
btn.addEventListener('click', () => {
const mainPar = btn.parentElement.parentElement
mainPar.classList.toggle('done')
})
}
const bindRemove = (btn) => {
btn.addEventListener('click', () => {
const parent = btn.parentElement
const mainPar = parent.parentElement
mainPar.remove()
})
}
doneBtns.forEach(bindDone)
remove.forEach(bindRemove)
* {
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 0;
padding: 0;
margin-top: 50px;
height: 100vh;
background-color: aliceblue;
}
.container{
width: 600px;
height: auto;
padding: 50px;
}
.add,
.todos{
display: flex;
flex-direction: column;
align-items: flex-start;
}
.add label{
font-family: 'Courier New', Courier, monospace;
font-weight: bold;
color: darkblue;
}
.add input{
background: white;
padding: 13px;
border: 0.5px solid #ccc;
width: 90%;
border-radius: 4px;
}
.add input:focus{
outline: none;
}
.add input::placeholder{
color:#ccc
}
.todo{
color: darkblue;
width: 90%;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
}
.todo:last-child{
border: 0
}
.todo p{
font-size: 24px;
font-weight: bold;
}
.buttons button{
background-color: transparent;
color: #aaa;
border: 0;
font-size: 24px;
cursor: pointer;
transition: all .5s ease-in-out;
}
.buttons button:hover{
color:black;
}
.todo.done p{
text-decoration: line-through;
text-decoration-thickness: 2px;
color: orangered;
text-decoration-style:double;
}
<div class="container">
<div class="add">
<label>ADD TODO</label>
<input type="text" placeholder="Write your To do">
</div>
<div class="todos">
<div class="todo">
<p>Todo 1</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
<div class="todo">
<p>Todo 2</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
<div class="todo">
<p>Todo 3</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
</div>
</div>
The reason your X and Y buttons are not working is because the querySelectorAll has already run before the new buttons exist, hence the addEventListener could not be added.
Using event delegation you only need a single click event on your parent element. Then inside the event handler you can check what element was pressed.
const todoInp = document.querySelector('.add input');
const todos = document.querySelector('.todos');
todoInp.addEventListener('keyup', (e) => {
if (e.key === 'Enter' && e.target.value !== '') {
const todoVal = e.target.value;
const todo = document.createElement('div');
todo.classList.add('todo');
todo.innerHTML = `
<p>${todoVal}</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
`;
todos.appendChild(todo);
todoInp.value = '';
}
})
// Add a single click event to the todos wrapper
todos.addEventListener('click', (e) => {
// exit if we don't click on a button
if(e.target.nodeName !== 'BUTTON') return;
// get `mainPar` element
const mainPar = e.target.parentElement.parentElement;
// remove todo item
if(e.target.classList.contains('remove')) {
return mainPar.remove();
}
// check todo item
if(e.target.classList.contains('check')) {
return mainPar.classList.toggle('done');
}
}, false);
* {
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 0;
padding: 0;
margin-top: 50px;
height: 100vh;
background-color: aliceblue;
}
.container{
width: 600px;
height: auto;
padding: 50px;
}
.add,
.todos{
display: flex;
flex-direction: column;
align-items: flex-start;
}
.add label{
font-family: 'Courier New', Courier, monospace;
font-weight: bold;
color: darkblue;
}
.add input{
background: white;
padding: 13px;
border: 0.5px solid #ccc;
width: 90%;
border-radius: 4px;
}
.add input:focus{
outline: none;
}
.add input::placeholder{
color:#ccc
}
.todo{
color: darkblue;
width: 90%;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
}
.todo:last-child{
border: 0
}
.todo p{
font-size: 24px;
font-weight: bold;
}
.buttons button{
background-color: transparent;
color: #aaa;
border: 0;
font-size: 24px;
cursor: pointer;
transition: all .5s ease-in-out;
}
.buttons button:hover{
color:black;
}
.todo.done p{
text-decoration: line-through;
text-decoration-thickness: 2px;
color: orangered;
text-decoration-style:double;
}
<div class="container">
<div class="add">
<label>ADD TODO</label>
<input type="text" placeholder="Write your To do">
</div>
<div class="todos">
<div class="todo">
<p>Todo 1</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
<div class="todo">
<p>Todo 2</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
<div class="todo">
<p>Todo 3</p>
<div class="buttons">
<button class="remove">x</button>
<button class="check">y</button>
</div>
</div>
</div>
</div>
You need to update the buttons list, after delete or add todo. This is hard to do and I suggest you use it this way:
You must edit HTML buttons to look like this:
<button class="remove" onclick="remove(this)">x</button>
<button class="check" onclick="done(this)">y</button>
After did that change your javascript file to:
const todoInp = document.querySelector('.add input')
const todos = document.querySelector('.todos')
todoInp.addEventListener('keyup', (e) => {
if (e.key === 'Enter' && e.target.value !== '') {
const todoVal = e.target.value
const todo = document.createElement('div')
todo.classList.add('todo')
todo.innerHTML = `
<p>${todoVal}</p>
<div class="buttons">
<button class="remove" onclick="remove(this)">x</button>
<button class="check" onclick="done(this)">y</button>
</div>
`
todos.appendChild(todo)
todoInp.value = ''
}
})
function done(btn){
const mainPar = btn.parentElement.parentElement
mainPar.classList.toggle('done')
}
function remove(btn){
const parent = btn.parentElement
const mainPar = parent.parentElement
mainPar.remove()
}
New to Javascript, I'm working on a quiz like solution that uses divs and buttons to cycle through the questions. I have written some code in Javascipt but for some reason It doesn't go through the last question where it should stop. Any help will be appreciated on why the next doesn't go to the last question.
var question = document.querySelectorAll('.question');
var next = document.getElementById("next");
next.addEventListener("click", function() {
var question = document.querySelectorAll(".question");
for (var i = 0; i < question.length; i++) {
if (question[i].style.display != "none") {
question[i].style.display = "none";
//resets to original questions
if (i == question.length - 1) {
question[0].style.display = "block";
} else {
question[i + 1].style.display = "block";
}
break;
}
}
});
.question {
margin: 50px;
width: 300px;
height: 300px;
border-radius: 10px;
padding: 50px;
text-align: center;
color: white;
background: #333;
position: relative;
display: none;
}
.visible {
display: block;
}
.q-input,
.move {
margin: 10px;
border: none;
padding: 10px;
}
.move {
display: flex;
justify-content: space-between;
}
.move button {
padding: 5px;
font-size: 16px;
width: 60px;
border-radius: 5px;
border: none;
cursor: pointer;
transition: 0.4s;
}
.move button:hover {
box-shadow: -2px -2px 20px #fff;
}
.move button:focus {
outline: none;
}
<div class="question visible">
<h1>Question <span class="one">1</span></h1>
<p>What is your Name</p>
<input type='text' class="q-input">
<div class="move">
<button id="prev">Prev</button>
<button id="next">Next</button>
</div>
</div>
<div class="question">
<h1>Question 2</h1>
<p>What is your Age</p>
<input type="text" class="q-input">
<div class="move">
<button id="prev">Prev</button>
<button id="next">Next</button>
</div>
</div>
<div class="question">
<h1>Question 3</h1>
<p>What is your Sex</p>
<input type="text" class="q-input">
<div class="move">
<button id="prev">Prev</button>
<button id="next">Next</button>
</div>
</div>
You are using document.getElementById("next") which only returns the first button with an ID of next, so only that button gets the click listener added to it.
IDs are meant to be just one unique element, so you should use class instead - change it to <button class="next">Next</button>
In your JS you should select all these next button elements, and use forEach to add a listener to all of them.
Then within that listener, you can get the connected question divs to show/hide by using parentElement and nextElementSibling, and check the next element is a question before changing the visibilities.
Also adding/removing the visible class is neater and easier to debug than trying to edit the style property manually.
All together that looks like:
var nextButtons = document.querySelectorAll('.next');
nextButtons.forEach(nextButton =>
nextButton.addEventListener("click", function() {
var currentQuestion = nextButton.parentElement.parentElement;
var nextElement = currentQuestion.nextElementSibling;
if (nextElement.classList.contains("question")) {
nextElement.classList.add("visible");
currentQuestion.classList.remove("visible");
}
}));
.question {
margin: 50px;
width: 300px;
height: 300px;
border-radius: 10px;
padding: 50px;
text-align: center;
color: white;
background: #333;
position: relative;
display: none;
}
.visible {
display: block;
}
.q-input,
.move {
margin: 10px;
border: none;
padding: 10px;
}
.move {
display: flex;
justify-content: space-between;
}
.move button {
padding: 5px;
font-size: 16px;
width: 60px;
border-radius: 5px;
border: none;
cursor: pointer;
transition: 0.4s;
}
.move button:hover {
box-shadow: -2px -2px 20px #fff;
}
.move button:focus {
outline: none;
}
<div class="question visible">
<h1>Question <span class="one">1</span></h1>
<p>What is your Name</p>
<input type='text' class="q-input">
<div class="move">
<button class="prev">Prev</button>
<button class="next">Next</button>
</div>
</div>
<div class="question">
<h1>Question 2</h1>
<p>What is your Age</p>
<input type="text" class="q-input">
<div class="move">
<button class="prev">Prev</button>
<button class="next">Next</button>
</div>
</div>
<div class="question">
<h1>Question 3</h1>
<p>What is your Sex</p>
<input type="text" class="q-input">
<div class="move">
<button class="prev">Prev</button>
<button class="next">Next</button>
</div>
</div>
It's not possible that more than 1 element has the same id (button next + prev), so changed to class='next' or use unique id's.
You need forEach of your button an eventListener. You can use forEach to iterate over it.
var question = document.querySelectorAll('.question');
var next = document.querySelectorAll(".next");
next.forEach(n => {n.addEventListener("click", function() {
var question = document.querySelectorAll(".question");
for (var i = 0; i < question.length; i++) {
if (question[i].style.display != "none") {
question[i].style.display = "none";
//resets to original questions
if (i == question.length - 1) {
question[0].style.display = "block";
} else {
question[i + 1].style.display = "block";
}
break;
}
}
})});
.question {
margin: 50px;
width: 300px;
height: 300px;
border-radius: 10px;
padding: 50px;
text-align: center;
color: white;
background: #333;
position: relative;
display: none;
}
.visible {
display: block;
}
.q-input,
.move {
margin: 10px;
border: none;
padding: 10px;
}
.move {
display: flex;
justify-content: space-between;
}
.move button {
padding: 5px;
font-size: 16px;
width: 60px;
border-radius: 5px;
border: none;
cursor: pointer;
transition: 0.4s;
}
.move button:hover {
box-shadow: -2px -2px 20px #fff;
}
.move button:focus {
outline: none;
}
<div class="question visible">
<h1>Question <span class="one">1</span></h1>
<p>What is your Name</p>
<input type='text' class="q-input">
<div class="move">
<button id="prev1">Prev</button>
<button class="next" id="next1">Next</button>
</div>
</div>
<div class="question">
<h1>Question 2</h1>
<p>What is your Age</p>
<input type="text" class="q-input">
<div class="move">
<button id="prev2">Prev</button>
<button class="next" id="next2">Next</button>
</div>
</div>
<div class="question">
<h1>Question 3</h1>
<p>What is your Sex</p>
<input type="text" class="q-input">
<div class="move">
<button id="prev3">Prev</button>
<button class="next" id="next3">Next</button>
</div>
</div>
I'm attempting to create a function that sorts elements based on their numerical separation from the data-attribute value of a designated "leader."
Here's what I'm trying to achieve:
If the participant data-title value is less than or equal to 4 from the data-title of the "leader" then they are appended to playoffs
If the data-title value is greater than 4 from the data-title of the "leader" than they are appended to out.
I'm hoping to keep the leader in place and only sort/append the elements that are equal to or larger than the leader data-title into their respective categories. How can I adjust the code below to facilitate something like this?
var participant = $('.participant')
var leader = $('#leader')
$(participant).sort(function (a, b) {
var contentA = parseInt( $('.participant').data('title'));
var contentB = parseInt( $(leader).data('title'));
if (contentA - contentB <= 4) {
$(participant).appendTo('#in-race');
}
else {
$(participant).appendTo('#out');
}
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
.section-hold {
margin: 2em 0;
min-height: 40px;
padding: 15px;
display: block;
}
.participant{
background-color: #EEE;
padding: 20px;
width: 70vw;
margin: 10px auto;
display: inline-block;
}
.name{
float: left;
}
.score{
float: right;
}
.section-title{
margin: 10px 0;
color: #FFF;
}
#participant-hold{
outline: solid 1px grey;
}
#in-race{
outline: solid 1px yellow;
}
#out{
outline: solid 1px red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="section-hold" id="participant-hold">
<h3 class="section-title">PARTICIPANTS</h3>
<div class="participant">
<div class="name">LEADER</div>
<div class="score" id="leader" data-title="18"><em>18</em></div>
</div>
<div class="participant">
<div class="name">TEST 1</div>
<div class="score" data-title="21.5"><em>21.5</em></div>
</div>
<div class="participant">
<div class="name">TEST 2</div>
<div class="score" data-title="28"><em>28</em></div>
</div>
</div>
<div class="section-hold" id="in-race">
<h3 class="section-title">PLAYOFFS</h3>
</div>
<div class="section-hold" id="out">
<h3 class="section-title">OUT</h3>
</div>
To keep the leader in place and only sort the other participants you can do this:
var participant = $('.participant:not(:has(#leader))');
var leader = $('#leader')
participant.each(function() {
var contentA = parseInt($(this).find(".score").data("title"));
var contentB = parseInt(leader.data("title"));
if (contentA - contentB <= 4) {
$(this).appendTo('#in-race');
} else {
$(this).appendTo('#out');
}
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
.section-hold {
margin: 2em 0;
min-height: 40px;
padding: 15px;
display: block;
}
.participant{
background-color: #EEE;
padding: 20px;
width: 70vw;
margin: 10px auto;
display: inline-block;
}
.name{
float: left;
}
.score{
float: right;
}
.section-title{
margin: 10px 0;
color: #FFF;
}
#participant-hold{
outline: solid 1px grey;
}
#in-race{
outline: solid 1px yellow;
}
#out{
outline: solid 1px red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="section-hold" id="participant-hold">
<h3 class="section-title">PARTICIPANTS</h3>
<div class="participant">
<div class="name">LEADER</div>
<div class="score" id="leader" data-title="18"><em>18</em></div>
</div>
<div class="participant">
<div class="name">TEST 1</div>
<div class="score" data-title="21.5"><em>21.5</em></div>
</div>
<div class="participant">
<div class="name">TEST 2</div>
<div class="score" data-title="28"><em>28</em></div>
</div>
</div>
<div class="section-hold" id="in-race">
<h3 class="section-title">PLAYOFFS</h3>
</div>
<div class="section-hold" id="out">
<h3 class="section-title">OUT</h3>
</div>
I'd like to know how to close multiple popup boxes by clicking close buttons with Pure Javascript.
I tried the code below, but it didn't work.
JavaScript
const buttons = document.querySelectorAll('.button');
buttons.forEach((button) => {
button.addEventListener('click', () => {
this.parentElement.querySelector('.popup').style.display = 'none';
});
});
HTML
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.1
</div>
</div>
</div>
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.2
</div>
</div>
</div>
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.3
</div>
</div>
</div>
CSS
.popup {
border: 3px solid gray;
}
.button {
position: absolute;
left: -15px;
top: -15px;
display: block;
border: 1px solid #000;
width: 30px;
height: 30px;
background-color: #fff;
border-radius: 50%;
text-align: center;
line-height: 30px;
}
.content-wrapper {
max-height: 50vh;
overflow-y: auto;
padding: 20px;
}
.content {
overflow: hidden;
}
.popup {
width: 300px;
position: relative;
position: fixed;
right:30px;
}
.popup:nth-child(1) {
bottom:30px;
}
.popup:nth-child(2) {
bottom:130px;
}
.popup:nth-child(3) {
bottom:230px;
}
const buttons = Array.prototype.slice.call(document.querySelectorAll('.button'));
buttons.forEach((button) => {
button.addEventListener('click', () => {
button.parentElement.style.display ='none';
});
});
.popup {
border: 3px solid gray;
}
.button {
position: absolute;
left: -15px;
top: -15px;
display: block;
border: 1px solid #000;
width: 30px;
height: 30px;
background-color: #fff;
border-radius: 50%;
text-align: center;
line-height: 30px;
}
.content-wrapper {
max-height: 50vh;
overflow-y: auto;
padding: 20px;
}
.content {
overflow: hidden;
}
.popup {
width: 300px;
position: relative;
position: fixed;
right:30px;
}
.popup:nth-child(1) {
bottom:30px;
}
.popup:nth-child(2) {
bottom:130px;
}
.popup:nth-child(3) {
bottom:230px;
}
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.1
</div>
</div>
</div>
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.2
</div>
</div>
</div>
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.3
</div>
</div>
</div>
Try this
<!DOCTYPE html>
<html>
<head>
<style>
.popup {
border: 3px solid gray;
}
.closePopup{
display:none;
}
.button {
position: absolute;
left: -15px;
top: -15px;
display: block;
border: 1px solid #000;
width: 30px;
height: 30px;
background-color: #fff;
border-radius: 50%;
text-align: center;
line-height: 30px;
}
.content-wrapper {
max-height: 50vh;
overflow-y: auto;
padding: 20px;
}
.content {
overflow: hidden;
}
.popup {
width: 300px;
position: relative;
position: fixed;
right:30px;
}
.popup:nth-child(1) {
bottom:30px;
}
.popup:nth-child(2) {
bottom:130px;
}
.popup:nth-child(3) {
bottom:230px;
}
</style>
</head>
<body>
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.1
</div>
</div>
</div>
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.2
</div>
</div>
</div>
<div class="popup">
<span class="button">×</span>
<div class="content-wrapper">
<div class="content">
No.3
</div>
</div>
</div>
<script>
const buttons = document.querySelectorAll('.button');
const popups = document.querySelectorAll('.popup');
buttons.forEach(function(button,index){console.log('index:',index);
let newIndex =index; button.addEventListener('click', () => {
console.log('newIndex: ',popups[newIndex]);
popups[newIndex].classList.add("closePopup");
});
});
</script>
</body>
</html>