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>
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.
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>
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()
}
I am doing a school project of designing an ecommerce webpage. I am new to this and is facing some problem at the shopping cart check out portion. Would really appreciate any help from you guys ! :)
There's already an item (let's call it item A) in the cart every time the webpage reload. I am able to get a correct total price computation for every increasing quantity of item A in the cart. However, when I add another item into the cart and increase the quantity of the newly added item , the total price won't update. I also noticed that when I increase the quantity of item A when there is other items in the cart. The total price = (quantity * total amount) in the cart instead of computing just the price of item.
Here's the js, css and html code:
if (document.readyState == 'loading') {
document.addEventListener('DOMContentLoaded', ready)
} else {
ready()
}
function ready() {
var removeCartItemButtons = document.getElementsByClassName('btn-1');
console.log(removeCartItemButtons)
for (var i = 0; i < removeCartItemButtons.length; i++) {
var button = removeCartItemButtons[i]
button.addEventListener('click', removeCartItem)
}
var quantityInputs = document.getElementsByClassName('cart-quantity-input')
for (var i = 0; i < quantityInputs.length; i++) {
var input = quantityInputs[i]
input.addEventListener('change', quantityChanged)
}
var addToCartButtons = document.getElementsByClassName('btn-2')
for (var i = 0; i < addToCartButtons.length; i++) {
var button = addToCartButtons[i]
button.addEventListener('click', addToCartClicked)
}
document.getElementsByClassName
}
function removeCartItem(event) {
var buttonClicked = event.target
buttonClicked.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.remove()
updateCartTotal()
}
function quantityChanged(event) {
var input = event.target
if (isNaN(input.value) || input.value <= 0) {
input.value = 1
}
updateCartTotal()
}
function addToCartClicked(event) {
var button = event.target
var shopItem = button.parentElement
var title = shopItem.getElementsByClassName('shop-item-title')[0].innerText
var price = shopItem.getElementsByClassName('shop-item-price')[0].innerText
var imageSrc = shopItem.getElementsByClassName('shop-item-image')[0].src
console.log(title, price, imageSrc)
addItemToCart(title, price, imageSrc)
updateCartTotal()
}
function addItemToCart(title, price, imageSrc) {
var cartRow = document.createElement('div')
cartRow.classList.add()
var cartItems = document.getElementsByClassName('cart-items')[0]
var cartItemNames = cartItems.getElementsByClassName('cart-item-title')
for (var i = 0; i < cartItemNames.length; i++) {
if (cartItemNames[i].innerText == title) {
alert('This item is already been added to the cart')
return
}
}
var cartRowContents = `
<div class="cart-items">
<table class="cart-row">
<tr>
<td>
<div class="cart-info">
<img class="cart-item-image"src="${imageSrc}" >
<div>
<p class="cart-item-title">${title}</p>
<small>Price: ${price}</small>
<br>
<button class="btn-1" type="button">Remove</button>
</div>
</div>
</td>
<td><input class="cart-quantity-input" type="number" min="1" value="1"></td>
<td
class="cart-price">${price}
</td >
</tr>
</table>
</div>`
cartRow.innerHTML = cartRowContents
cartItems.append(cartRow)
cartRow.getElementsByClassName('btn-1')[0].addEventListener('click', removeCartItem)
cartRow.getElementsByClassName('cart-quantity-input')[0].addEventListener('change', quantityChanged)
}
function updateCartTotal() {
var cartItemContainer = document.getElementsByClassName('cart-items')[0]
var cartRows = cartItemContainer.getElementsByClassName('cart-row')
var total = 0
for (var i = 0; i < cartRows.length; i++) {
var cartRow = cartRows[i]
var quantityElement = cartItemContainer.getElementsByClassName('cart-quantity-input')[0]
var priceElement = cartRow.getElementsByClassName('cart-price')[0]
var price = parseFloat(priceElement.innerHTML.replace('$', ''))
var quantity = quantityElement.value
total = total + (price * quantity)
}
total = (Math.round(total * 100) / 100).toFixed(2)
document.getElementsByClassName('cart-total-price')[0].innerHTML = '$' + total
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.navbar {
display: flex;
align-items: center;
padding: 20px;
}
nav {
flex: 1;
text-align: right;
}
nav ul {
display: inline-block;
list-style-type: none;
}
nav ul li {
display: inline-block;
margin-right: 10px;
}
a {
text-decoration: none;
color: black;
}
p {
color: black;
}
.container {
max-width: 1300px;
margin: auto;
padding-left: 50px;
padding-right: 50px;
}
.row {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-around;
}
.col-2 {
flex-basis: 50%;
min-width: 400px;
}
.col-2 img {
max-width: 100%;
padding: 50px 0;
}
.col-2 h1 {
font-size: 50px;
line-height: 60px;
margin: 25px 0;
}
.btn {
display: inline-block;
background: #ff523b;
color: #fff;
padding: 8px 18px;
margin: 30px 0;
border-radius: 30px;
cursor: pointer;
outline: none;
}
.col-4 {
flex-basis: 25%;
padding: 10px;
min-width: 200px;
margin-bottom: 50px;
transition: transform 0.5s;
}
.col-4 img {
width: 100%;
}
.small-container {
max-width: 1080px;
margin: auto;
padding-left: 50px;
padding-right: 50px;
}
.title {
text-align: center;
margin: 0 auto 30px;
position: relative;
line-height: 50px;
color: black
}
.title::after {
content: '';
background: black;
width: 80px;
height: 5px;
border-radius: 5px;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
.col-4:hover {
transform: translateY(-5px);
}
.btn-2 {
display: inline-block;
background: #ff523b;
color: #fff;
padding: 8px 18px;
margin: 30px 0;
border-radius: 30px;
cursor: pointer;
border: none;
outline: none;
}
/*------footer-----*/
.footer {
background: #000;
color: #8a8a8a;
font-size: 14px;
padding: 40px 0 20px;
}
.footer-col-1 {
min-width: 250px;
margin-bottom: 10px;
flex-basis: 30%;
flex: 1;
text-align: center;
}
.footer-col-1 img {
width: 200px;
margin-bottom: 20px;
}
.footer p {
color: #8a8a8a;
}
/*------cart-----*/
.cart-page {
margin: 80px auto;
}
table {
width: 100%;
border-collapse: collapse;
}
.cart-info {
display: flex;
flex-wrap: nowrap;
}
th {
text-align: justify;
padding: 5px;
color: #fff;
background: #ff523b;
font-weight: normal;
}
td {
padding: 10px 5px;
width: 1px;
}
td input {
width: 45px;
height: 30px;
padding: 5px;
border-radius: 30px;
text-align: right;
}
td a {
color: #ff523b;
font-size: 10px;
}
td img {
width: 80px;
height: 80px;
margin-right: 10px;
}
.btn-1 {
display: inline-block;
background: #ff523b;
color: #fff;
padding: 8px 10px;
margin: 3px 0;
border-radius: 30px;
font-size: 10px;
cursor: pointer;
border: none;
outline: none;
}
.total-price {
display: flex;
justify-content: flex-end;
}
.total-price table {
border-top: 3px solid #ff523b;
width: 100%;
max-width: 350px;
margin: 30px 0;
}
td:last-child {
text-align: right;
}
th:last-child {
text-align: right;
}
.btn-purchase {
display: inline-block;
background: #ff523b;
color: #fff;
padding: 8px 40px;
margin: 30px 0;
border-radius: 30px;
outline: none;
border: none;
cursor: pointer;
}
.btn-purchase:hover {
transform: translateY(-3px);
}
.btn-1:hover {
transform: translateY(-1px);
}
.cart-item {
width: 60%;
}
/*------------ account page --------------*/
.account-page {
padding: 50px 0;
}
.form-container {
background: #fff;
width: 300px;
height: 300px;
position: relative;
text-align: center;
padding: 20px 0;
margin: auto;
box-shadow: 0 0 20px 0px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.form-container span {
font-weight: bold;
padding: 0 10px;
color: #555;
cursor: pointer;
width: 100px;
display: inline-block;
}
.form-btn {
display: inline-block;
}
.form-container form {
max-width: 300px;
padding: 0 20px;
position: absolute;
top: 130;
transition: transform 1s;
}
form input {
width: 100%;
height: 30px;
margin: 10px 0;
padding: 0 10px;
border: 1px solid #ccc;
}
form .btn {
width: 100%;
border: none;
cursor: pointer;
margin: 10px 0;
}
form .btn:focus {
outline: none;
}
#LoginForm {
left: -300px;
}
#RegForm {
left: 0;
}
form a {
font-size: 12px;
}
#Indicator {
width: 100px;
border: none;
background: #ff523b;
margin-top: 8px;
height: 3px;
transform: translateX(100px);
transition: transform 1s;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Internet Security PBIL</title>
<link rel="stylesheet" href="style.css">
<script src="store.js" async></script>
</head>
<body>
<div class="container">
<div class="navbar">
<div class="logo">
<img src="yeet/yeet.jpg" width="80px">
</div>
<nav>
<ul>
<li>Home</li>
<li>Account</li>
</ul>
</nav>
</div>
<div class="row">
<div class="col-2">
<h1>Good Sport <br>Good game!</h1>
<p>Come buy our good items now!</p>
Explore Now
</div>
<div class="col-2">
<img src="yeet/image1.png">
</div>
</div>
</div>
<!-------- products -------->
<div class="small-container">
<h2 class="title">Shirt</h2>
<div class="row">
<div class="col-4">
<img class="shop-item-image" src="yeet/product-1.jpg">
<h4 class="shop-item-title">Red</h4>
<p class="shop-item-price">$10.00</p>
<button class="btn-2">Add to cart</button>
</div>
<div class="col-4">
<img class="shop-item-image" src="yeet/product-2.jpg">
<h4 class="shop-item-title">Blue</h4>
<p class="shop-item-price">$20.00</p>
<button class="btn-2">Add to cart</button>
</div>
<div class="col-4">
<img class="shop-item-image" src="yeet/product-3.jpg">
<h4 class="shop-item-title">Green</h4>
<p class="shop-item-price">$30.00</p>
<button class="btn-2">Add to cart</button>
</div>
<div class="col-4">
<img class="shop-item-image" src="yeet/product-4.jpg">
<h4 class="shop-item-title">Yellow</h4>
<p class="shop-item-price">$40.00</p>
<button class="btn-2">Add to cart</button>
</div>
</div>
<h2 class="title">Shorts</h2>
<div class="row">
<div class="col-4">
<img class="shop-item-image" src="yeet/product-5.jpg">
<h4 class="shop-item-title">1 printed t-shirt</h4>
<p class="shop-item-price">$50.00</p>
<button class="btn-2">Add to cart</button>
</div>
<div class="col-4">
<img class="shop-item-image" src="yeet/product-6.jpg">
<h4 class="shop-item-title">2 printed t-shirt</h4>
<p class="shop-item-price">$60.00</p>
<button class="btn-2">Add to cart</button>
</div>
<div class="col-4">
<img class="shop-item-image" src="yeet/product-7.jpg">
<h4 class="shop-item-title">3 printed t-shirt</h4>
<p class="shop-item-price">$70.00</p>
<button class="btn-2">Add to cart</button>
</div>
<div class="col-4">
<img class="shop-item-image" src="yeet/product-8.jpg">
<h4 class="shop-item-title">4 printed t-shirt</h4>
<p class="shop-item-price">$80.00</p>
<button class="btn-2">Add to cart</button>
</div>
</div>
</div>
<!----- cart items details ----->
<section class="small-container cart-page">
<h2 class="title">Cart</h2>
<table>
<tr>
<th class="cart-item">Product</th>
<th class="cart-qunatity">Quantity</th>
<th class="cart-price">Subtotal</th>
</tr>
</table>
<div class="cart-items">
<table class="cart-row">
<tr>
<td>
<div class="cart-info">
<img class="cart-item-image" src="yeet/buy-1.jpg">
<div>
<p class="cart-item-title">Red Printed Tshirt</p>
<small>Price: $50.00</small>
<br>
<button class="btn-1" type="button">Remove</button>
</div>
</div>
</td>
<td><input class="cart-quantity-input" type="number" min="1" value="1"></td>
<td><span class="cart-price">$50.10</span>
</td>
</tr>
</table>
</div>
<!--- <table>
<tr class="cart-row">
<td>
<div class="cart-info">
<img class="cart-item-image" src="yeet/buy-2.jpg" >
<div>
<p class="cart-item-title">Red Printed Tshirt</p>
<small>Price: $80.0</small>
<br>
<button class="btn-1" type="button">Remove</button>
</div>
</div>
</td>
<td><input class="cart-quantity-input" type="number" min="1" value="1"></td>
<td class="cart-price">$80.50</td>
</tr>
</table>
</div> --->
<div class="total-price">
<table>
<tr>
<td class="cart-total-title">Total</td>
<td class="cart-total-price">$50.50</td>
</tr>
<tr>
<td>
<button class="btn-purchase" type="button">Purchase</button>
</td>
</tr>
</table>
</div>
</section>
<!-----footer----->
<div class="footer">
<div class="container">
<div class="footer-col-1">
<p>Products brought to you by Yeet!</p>
<img src="yeet/yeet.jpg">
</div>
</div>
</div>
</body>
</html>
Thank you guys for your help!
(ps: please ignore the naming of my items as is not finalize yet haha,cheers!)
Ah, it was hard to see, so I made an answer of it:
you used once cartItemContainer instead of cartRow, so you couldn't understand my comment... :-)
var quantityElement = cartItemContainer.getElementsByClassName('cart-quantity-input') [0] <- should be cartRow
var priceElement = cartRow.getElementsByClassName('cart-price')[0]
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>