Dropdown toggle only picking first value - javascript

I have made a dropdown which toggles successfully, but it only seems to pick the first class and when I click the second dropdown it toggles the contents of the first. Is there something I am missing here? Here is my code:
const menuListDropdown = document.querySelectorAll('.menu-block-dropdown');
const menuBlock = document.querySelector('.menu-block');
menuListDropdown.forEach((menuBlockList) => {
menuBlockList.addEventListener('click', function() {
menuBlock.classList.toggle('menu-block-active');
})
})
.menu-block {
background: #fff;
box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 5px 0px, rgba(0, 0, 0, 0.1) 0px 0px 1px 0px;
padding: 15px;
border-radius: 8px;
position: absolute;
top: 35px;
opacity: 0;
transition: 150ms ease;
}
.menu-block-active {
transition: 150ms all;
opacity: 1;
}
.menu-block-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.menu-block-list a {
color: #444444;
margin: 0 0 0.25 0;
padding: 0;
font-weight: 500;
}
<li class="menu-block-dropdown">
Resources
<div class="menu-block">
<div class="menu-block-list">
Dropdown 1
Dropdown 2
</div>
</div>
</li>
<li class="menu-block-dropdown">
Blogs
<div class="menu-block">
<div class="menu-block-list">
Dropdown 3
Dropdown 4
</div>
</div>
</li>

The problem is, you are selecting just one dropdown. So what need to be done is select the dropdown in respect to the Menu Link you clicked.
See the below I've made changes in JS
const menuListDropdown = document.querySelectorAll('.menu-block-dropdown');
// Not needed
// const menuBlock = document.querySelector('.menu-block');
menuListDropdown.forEach((menuBlockList) => {
menuBlockList.addEventListener('click', function() {
// Select the Block within the Target List
const menuBlock = menuBlockList.querySelector(".menu-block");
menuBlock.classList.toggle('menu-block-active');
})
})
.menu-block {
background: #fff;
box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 5px 0px, rgba(0, 0, 0, 0.1) 0px 0px 1px 0px;
padding: 15px;
border-radius: 8px;
position: absolute;
top: 35px;
opacity: 0;
transition: 150ms ease;
}
.menu-block-active {
transition: 150ms all;
opacity: 1;
}
.menu-block-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.menu-block-list a {
color: #444444;
margin: 0 0 0.25 0;
padding: 0;
font-weight: 500;
}
li {
display: inline-block;
}
<li class="menu-block-dropdown">
Resources
<div class="menu-block">
<div class="menu-block-list">
Dropdown 1
Dropdown 2
</div>
</div>
</li>
<li class="menu-block-dropdown">
Blogs
<div class="menu-block">
<div class="menu-block-list">
Dropdown 3
Dropdown 4
</div>
</div>
</li>
Edit: Activate just one Dropdown at a time.
In this case, we need access to all the dropdowns Since, we need to update all their active state. So that only the intended Dropdown is Active at once.
const menuListDropdown = document.querySelectorAll('.menu-block-dropdown');
const menuBlocks = document.querySelectorAll('.menu-block');
menuListDropdown.forEach((menuBlockList) => {
menuBlockList.addEventListener('click', function() {
menuBlocks.forEach((menuBlock) => {
// Active the Dropdown if it's the child of the BlockList Element
if (menuBlockList.contains(menuBlock)) {
menuBlock.classList.toggle('menu-block-active');
} else {
menuBlock.classList.remove('menu-block-active');
}
});
});
});
.menu-block {
background: #fff;
box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 5px 0px, rgba(0, 0, 0, 0.1) 0px 0px 1px 0px;
padding: 15px;
border-radius: 8px;
position: absolute;
top: 35px;
opacity: 0;
transition: 150ms ease;
}
.menu-block-active {
transition: 150ms all;
opacity: 1;
}
.menu-block-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.menu-block-list a {
color: #444444;
margin: 0 0 0.25 0;
padding: 0;
font-weight: 500;
}
li {
display: inline-block;
}
<li class="menu-block-dropdown">
Resources
<div class="menu-block">
<div class="menu-block-list">
Dropdown 1
Dropdown 2
</div>
</div>
</li>
<li class="menu-block-dropdown">
Blogs
<div class="menu-block">
<div class="menu-block-list">
Dropdown 3
Dropdown 4
</div>
</div>
</li>

Related

I don't understand why my pop-up isn't working

So I am currently working on a personal project but cannot get my modal to appear in the browser. I've tried researching it but I'm getting the same answers back that I am currently applying.
const open1 = document.getElementById('open1');
const modal_container = document.getElementById('modal_container');
const close1 = document.getElementById('close1');
open1.addEventListener('click', () => {
modal_container.classList.add('show');
});
close1.addEventListener('click', () => {
modal_container.classList.remove('show');
});
div.modal-container {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background-color: rgba(0,0,0,0.3);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
}
.real45 {
background-color: #BC6C25;
border: 0;
border-radius: 25px;
color: #fff;
padding: 10px 25px;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.real45:hover {
color: black;
}
.modal {
background-color: #fff;
width: 600px;
max-width: 100%;
padding: 30px;
border-style: solid;
border-radius: 25px;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
text-align: center;
}
.modal h1 {
margin: 0;
}
.modal p {
opacity: 0.7;
}
.modal-container.show {
opacity: 1;
pointer-events: 1;
}
<button class="real45" id="open1"> Open Me </button>
<div class="modal-container" id="modal_container">
<div class="modal">
<h1> Modals are cool </h1>
<p> practice text </p>
<button class="real45" id="close1"> Close Me </button>
</div>
</div>
Please any and all suggestions are welcome I greatly appreciate it.
When you put pointer-events: none; on .modal-container, all child nodes will also inherit that property, which disables all pointer events (clicking, dragging, hovering, etc.).
So when you attempt to close the modal by clicking the button, nothing will happen.
I've added pointer-events: auto; to the .modal so that these events work for child nodes and you are able to close the modal.
const open1 = document.getElementById('open1');
const modal_container = document.getElementById('modal_container');
const close1 = document.getElementById('close1');
open1.addEventListener('click', () => {
modal_container.classList.add('show');
});
close1.addEventListener('click', () => {
modal_container.classList.remove('show');
});
div.modal-container {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
}
.real45 {
background-color: #BC6C25;
border: 0;
border-radius: 25px;
color: #fff;
padding: 10px 25px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.real45:hover {
color: black;
}
.modal {
background-color: #fff;
width: 600px;
max-width: 100%;
padding: 30px;
border-style: solid;
border-radius: 25px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
text-align: center;
position: relative;
pointer-events: auto;
}
.modal h1 {
margin: 0;
}
.modal p {
opacity: 0.7;
}
.modal-container.show {
opacity: 1;
pointer-events: 1;
}
<button id="open1">open
</button>
<div class="modal-container" id="modal_container">
<div class="modal">
<h1> Modals are cool </h1>
<p> practice text </p>
<button class="real45" id="close1"> Close Me </button>
</div>
</div>
I needed to add my <script> below my <button> and <div> tags. This is the correct way to make it work in browser.
<button class="real45" id="open1"> Open Me </button>
<div class="modal-container" id="modal_container">
<div class="modal">
<h1> Modals are cool </h1>
<p> practice text </p>
<button class="real45" id="close1"> Exit </button>
</div>
</div>
<script>
const open1 = document.getElementById("open1");
const modal_container = document.getElementById("modal_container");
const close1 = document.getElementById("close1");
open1.addEventListener("click", () => {
modal_container.classList.add("show");
});
close1.addEventListener("click", () => {
modal_container.classList.remove("show");
});
</script>

targeting click of a specific button type with jquery

I have a simple jquery block where I'm navigating through text slides with buttons. This is working and I don't want to change how it's operating but I would like to make it automatic as well. In other words, if nobody clicks the buttons the process would automatically proceed to the next every 4 or 5 seconds.
One thought I had was to simulate a next button click with jquery, but I'm confused on how to actually target the next button due to the way I've declared my buttons. I have a setInterval for 4 seconds that triggers the data-carousel-button but how do I actually target the 'next' version of that button?
const buttons = document.querySelectorAll("[data-carousel-button]")
buttons.forEach(button => {
button.addEventListener("click", () => {
const offset = button.dataset.carouselButton === "next" ? 1 : -1
const slides = button
.closest("[data-carousel]")
.querySelector("[data-slides]")
const activeSlide = slides.querySelector("[data-active]")
let newIndex = [...slides.children].indexOf(activeSlide) + offset
if (newIndex < 0) newIndex = slides.children.length - 1
if (newIndex >= slides.children.length) newIndex = 0
slides.children[newIndex].dataset.active = true
delete activeSlide.dataset.active
})
})
setInterval(function() {
$("data-carousel-button").trigger("click");
}, 4000);
.slideshow_overlay {
padding: 30px;
position: absolute;
display: flex;
justify-content: space-between;
align-items: center;
padding: 30px;
bottom: 5;
bottom: 0;
height: 15vh;
background: rgba(0, 0, 0, 0.3);
width: 100vw;
margin-left: 0px;
}
.slideshow_overlay-btnGroup {
display: flex;
}
.hero_slideshow {
width: 100vw;
height: calc(100vh - 105px);
min-height: 400px !important;
margin-top: 105px;
position: relative;
}
.hero_slideshow ul {
margin: 0;
padding: 0;
list-style: none;
}
.hero_carousel-button {
backgorund: none;
border: none;
z-index: 2;
font-size: 4rem;
top: 50%;
transform: translateY(-50%);
color: rgba(255, 255, 255, .5);
cursor: pointer;
border-radius: .25rem;
padding: 0 .5rem;
background-color: rgba(0, 0, 0, .1);
}
.hero_carousel-button:hover,
.hero_carousel-button:focus {
color: white;
background-color: rgba(0, 0, 0, .2);
}
.slide_hero {
position: absolute;
inset: 0;
opacity: 0;
transition: 200ms opacity ease-in-out;
transition-delay: 200ms;
}
.slide_hero>.slide_hero__img {
display: block;
width: 100%;
height: calc(100vh - 105px);
min-height: 400px !important;
object-fit: cover;
object-position: center;
}
.slide_hero[data-active] {
opacity: 1;
z-index: 1;
transition-delay: 0ms;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section aria-label="Hero Slideshow">
<div class="hero_slideshow" data-carousel>
<button class="hero_carousel-button prev" data-carousel-button="prev">Prev</button>
<button class="hero_carousel-button next" data-carousel-button="next">next</button>
<ul data-slides>
<li class="slide_hero" data-active>
Test 1
</li>
<li class="slide_hero">
Test 2
</li>
</ul>
</div>
</section>
You are not supplying the correct selector in your setInterval callback so you should change this
setInterval(function() {
$("data-carousel-button").trigger("click");
}, 4000);
into this
setInterval(function() {
// this selector, which is called attribute selector, will target the buttons having the "data-carousel-button" set to "next"
$("[data-carousel-button=next]").trigger("click");
}, 4000);
And here's a live demo of your code's corrected version:
const buttons = document.querySelectorAll("[data-carousel-button]")
buttons.forEach(button => {
button.addEventListener("click", () => {
const offset = button.dataset.carouselButton === "next" ? 1 : -1
const slides = button
.closest("[data-carousel]")
.querySelector("[data-slides]")
const activeSlide = slides.querySelector("[data-active]")
let newIndex = [...slides.children].indexOf(activeSlide) + offset
if (newIndex < 0) newIndex = slides.children.length - 1
if (newIndex >= slides.children.length) newIndex = 0
slides.children[newIndex].dataset.active = true
delete activeSlide.dataset.active
})
})
setInterval(function() {
$("[data-carousel-button=next]").trigger("click");
}, 4000);
.slideshow_overlay {
padding: 30px;
position: absolute;
display: flex;
justify-content: space-between;
align-items: center;
padding: 30px;
bottom: 5;
bottom: 0;
height: 15vh;
background: rgba(0, 0, 0, 0.3);
width: 100vw;
margin-left: 0px;
}
.slideshow_overlay-btnGroup {
display: flex;
}
.hero_slideshow {
width: 100vw;
height: calc(100vh - 105px);
min-height: 400px !important;
margin-top: 105px;
position: relative;
}
.hero_slideshow ul {
margin: 0;
padding: 0;
list-style: none;
}
.hero_carousel-button {
backgorund: none;
border: none;
z-index: 2;
font-size: 4rem;
top: 50%;
transform: translateY(-50%);
color: rgba(255, 255, 255, .5);
cursor: pointer;
border-radius: .25rem;
padding: 0 .5rem;
background-color: rgba(0, 0, 0, .1);
}
.hero_carousel-button:hover,
.hero_carousel-button:focus {
color: white;
background-color: rgba(0, 0, 0, .2);
}
.slide_hero {
position: absolute;
inset: 0;
opacity: 0;
transition: 200ms opacity ease-in-out;
transition-delay: 200ms;
}
.slide_hero>.slide_hero__img {
display: block;
width: 100%;
height: calc(100vh - 105px);
min-height: 400px !important;
object-fit: cover;
object-position: center;
}
.slide_hero[data-active] {
opacity: 1;
z-index: 1;
transition-delay: 0ms;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section aria-label="Hero Slideshow">
<div class="hero_slideshow" data-carousel>
<button class="hero_carousel-button prev" data-carousel-button="prev">Prev</button>
<button class="hero_carousel-button next" data-carousel-button="next">next</button>
<ul data-slides>
<li class="slide_hero" data-active>
Test 1
</li>
<li class="slide_hero">
Test 2
</li>
</ul>
</div>
</section>
Learn more about Attribute Selectors on MDN.
You are REALLY overthinking this. The data attributes are not necessary here
I had to fix the CSS to just use .active
const $slides = $(".slides li");
$(".hero_carousel-button").on("click", function() {
const next = $(this).is(".next");
const idx = $slides.filter(".active").index()
let $activeSlide = $slides.eq(idx + (next ? 1 : -1))
if ($activeSlide.length === 0) {
$activeSlide = $slides[next ? "first" : "last"]() // first or last
}
$slides.removeClass("active");
$activeSlide.addClass("active");
})
setInterval(function() {
$(".next").trigger("click");
}, 4000);
.slideshow_overlay {
padding: 30px;
position: absolute;
display: flex;
justify-content: space-between;
align-items: center;
padding: 30px;
bottom: 5;
bottom: 0;
height: 15vh;
background: rgba(0, 0, 0, 0.3);
width: 100vw;
margin-left: 0px;
}
.slideshow_overlay-btnGroup {
display: flex;
}
.hero_slideshow {
width: 100vw;
height: calc(100vh - 105px);
min-height: 400px !important;
margin-top: 105px;
position: relative;
}
.hero_slideshow ul {
margin: 0;
padding: 0;
list-style: none;
}
.hero_carousel-button {
background: none;
border: none;
z-index: 2;
font-size: 4rem;
top: 50%;
transform: translateY(-50%);
color: rgba(255, 255, 255, .5);
cursor: pointer;
border-radius: .25rem;
padding: 0 .5rem;
background-color: rgba(0, 0, 0, .1);
}
.hero_carousel-button:hover,
.hero_carousel-button:focus {
color: white;
background-color: rgba(0, 0, 0, .2);
}
.slide_hero {
position: absolute;
inset: 0;
opacity: 0;
transition: 200ms opacity ease-in-out;
transition-delay: 200ms;
}
.slide_hero>.slide_hero__img {
display: block;
width: 100%;
height: calc(100vh - 105px);
min-height: 400px !important;
object-fit: cover;
object-position: center;
}
.slide_hero.active {
opacity: 1;
z-index: 1;
transition-delay: 0ms;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<section aria-label="Hero Slideshow">
<div class="hero_slideshow">
<button class="hero_carousel-button prev">Prev</button>
<button class="hero_carousel-button next">next</button>
<ul class="slides">
<li class="slide_hero active">
Test 1
</li>
<li class="slide_hero">
Test 2
</li>
<li class="slide_hero">
Test 3
</li>
<li class="slide_hero">
Test 4
</li>
</ul>
</div>
</section>
Since your carousal is in loop (like if it a last slider then it go back to first) you can target the next button alone.
And also you need to use "clearInterval" function along with this In-order to avoid the slider override behaviour (if you are trying to interfere when the auto carousel is also trying to run the next button) that you can prevent by clearing the interval on every button click made by you.
This code made your carousel flexible.
See working example here: https://jsbin.com/diwomakime/edit?html,css,js,output
const buttons = document.querySelectorAll("[data-carousel-button]");
/*** new changes ***/
var myCarouselInterval = setInterval(nextSlide, 4000);
buttons.forEach(button => {
button.addEventListener("click", () => {
/*** new changes ***/
clearSlideInterval();
const offset = button.dataset.carouselButton === "next" ? 1 : -1
const slides = button
.closest("[data-carousel]")
.querySelector("[data-slides]")
const activeSlide = slides.querySelector("[data-active]")
let newIndex = [...slides.children].indexOf(activeSlide) + offset
if (newIndex < 0) newIndex = slides.children.length - 1
if (newIndex >= slides.children.length) newIndex = 0
slides.children[newIndex].dataset.active = true
delete activeSlide.dataset.active
})
})
function nextSlide() {
/*** new changes ***/
$(".next[data-carousel-button]").trigger("click");
}
/*** new changes ***/
function clearSlideInterval() {
clearInterval(myCarouselInterval);
myCarouselInterval = setInterval(nextSlide, 4000);
}

Unable to add transition when width of navbar is changed with Javascript

I have a navbar in with I am changing the width when the checkbox is clicked. When the checkbox is clicked, I want the navbar to smoothly transit to the larger width. It seems to be working only the first time on the snippet.
Here is the code I have:
function change() {
var nav = document.getElementById("navbar");
var checkBox = document.getElementById("checkbox");
if (checkBox.checked == true) {
nav.style.width = "300px";
} else {
nav.style.width = "fit-content";
}
}
nav {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 100px;
height: 100%;
transition: 0.3s;
box-shadow: rgba(136, 165, 191, 0.26) 4px 2px 10px 0px, rgba(255, 255, 255, 0.8) -4px -2px 16px 0px;
}
nav input {
margin: 10px;
position: absolute;
width: 50px;
height: 50px;
opacity: 0;
cursor: pointer;
}
<nav id="navbar">
<input onclick="change()" type="checkbox" name="" id="checkbox">
<p>Home</p>
</nav>
Instead of using checkbox with JavaScript you can use onClick event listener on navbar. And then create a class for example .expanded and toggle class accordingly.
const navbar = document.querySelector('.navbar');
navbar.addEventListener('click', (e) => {
navbar.classList.toggle('expanded');
});
* {
margin: 0;
padding: 0;
outline: 0;
box-sizing: border-box;
}
.navbar {
width: 100px;
padding: 1rem;
background-color: white;
transition: width 250ms ease;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25);
}
.navbar.expanded {
width: 300px;
}
<nav class="navbar">
<p>Navbar</p>
</nav>
Hope it will work 🚀
A transition only works on numeric values, so you need to change the width in the else-case accordingly, for example to 50px.
function change(){
var nav = document.getElementById("navbar");
var checkBox = document.getElementById("checkbox");
if(checkBox.checked == true){
nav.style.width = "300px";
} else{
nav.style.width = "50px";
}
}
nav{
display: flex;
flex-direction: column;
justify-content: space-between;
width:100px;
height: 100%;
transition: 0.3s;
box-shadow: rgba(136, 165, 191, 0.26) 4px 2px 10px 0px, rgba(255, 255, 255, 0.8) -4px -2px 16px 0px;
}
nav input{
margin: 10px;
position: absolute;
width: 50px;
height: 50px;
opacity: 0;
cursor: pointer;
}
<nav id="navbar">
<input onclick="change()" type="checkbox" name="" id="checkbox">
<p>Home</p>
</nav>

CSS clip path issue with background

I'm currently implementing a switch. The problem is that the background which should be hidden by the switch shows one thin line at the left end. I've absolutely no idea why. The strange thing is that here on SO everything looks really good. The switch is located in a scrollable main wrapper with all the other content. I thought this could be the problem but after removing the scrolling, the error was still there:
When I run the inspector and hover the element, the background seems to go out:
This is my code. I've tried a low but can't find the problem:
let toggle = document.getElementById('container');
let toggleContainer = jQuery('#toggle-container');
let toggleNumber;
jQuery('#container').click( function() {
toggleNumber = !toggleNumber;
if (toggleNumber) {
toggleContainer.css( "clip-path", "inset(0 0 0 50%)" );
} else {
toggleContainer.css( "clip-path", "inset(0 50% 0 0)" );
}
});
#container {
width: 100%;
height: 56px;
margin-bottom: 20px;
position: relative;
overflow: hidden;
user-select: none;
cursor: pointer;
border-radius: 3px;
-webkit-box-shadow: 0 0.2rem 0.4rem 0 rgba(0, 0, 0, .2);
box-shadow: 0 0.12rem 0.4rem 0 rgba(0, 0, 0, .2);
}
.inner-container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
text-transform: uppercase;
}
.inner-container:first-child {
background: gray;
color: #ffffff;
}
.inner-container:nth-child(2) {
background: chartreuse;
color: #ffffff;
clip-path: inset(0 50% 0 0);
-webkit-transition: 0.2s;
-o-transition: 0.2s;
transition: 0.2s;
}
.toggle {
width: 50%;
position: absolute;
height: inherit;
display: flex;
font-weight: 600;
}
.toggle p {
margin: auto;
}
.toggle:nth-child(1) {
right: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div class="inner-container">
<div class="toggle">
<p>Private</p>
</div>
<div class="toggle">
<p>Public</p>
</div>
</div>
<div class="inner-container" id='toggle-container'>
<div class="toggle">
<p>Private</p>
</div>
<div class="toggle">
<p>Public</p>
</div>
</div>
</div>
I would suggest an optimized version with less of code and without the use of clip-path:
let toggle = document.getElementById('container');
let toggleContainer = jQuery('.inner-container');
jQuery('#container').click(function() {
toggleContainer.toggleClass('second');
});
#container {
margin-bottom: 20px;
user-select: none;
cursor: pointer;
border-radius: 3px;
box-shadow: 0 0.12rem 0.4rem 0 rgba(0, 0, 0, .2);
}
.inner-container {
height: 56px;
text-transform: uppercase;
display: flex;
background:
linear-gradient(chartreuse,chartreuse) left/50% 100% no-repeat,
gray;
color: #ffffff;
transition: 0.2s;
}
.inner-container.second {
background-position:right;
}
.toggle {
width: 50%;
display: flex;
font-weight: 600;
}
.toggle p {
margin: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div class="inner-container">
<div class="toggle">
<p>Private</p>
</div>
<div class="toggle">
<p>Public</p>
</div>
</div>
</div>
This seems to be what you are seeing:
Which was done by making the CSS: clip-path: inset(0 50% 0 1px);
Maybe just try adding some negative space to the left side:
toggleContainer.css( "clip-path", "inset(0 50% 0 -10px)" );

Javascript Stop Propagation

I have been trying to figure out how to get an index of a dynamically created element so that I can write a function to delete it (splice).
I have figured out how to get the index really manually, but the problem is that I am getting Propagation from my event delegation and I am not sure how to stop it.
I have tried putting stopPropagation(), preventDefault(), stopImmediatePropagation() at various points within the function and have spent the last hour reading around online trying to figure out how to stop it. I even tried setting the e.bubble to false with no avail.
Could someone point me in the right direction here? Im sure its my inexperience but I am just out of ideas as of now.
// Title of Question Set
const title = document.querySelector(".input_title-input");
// Array of Questions
const questions = [];
let chosen = [];
// Selected Question
let qChosen = [];
// Toggles if a new question is selected
let toggle = 0;
// Selecting Inputs and Button
let question = document.querySelector(".input_question-input");
let answer = document.querySelector(".input_answer-input");
let submit = document.querySelector(".input_submit-button");
let display = document.querySelector(".input_display");
let card = document.querySelector(".card_container");
let start = document.querySelector(".input_start-btn");
let guessInput = document.querySelector(".guess_input");
let submitGuess = document.querySelector(".submitGuess");
let nextQuestion = document.querySelector(".nextQuestion");
// Select all display items
let displayItems = document.getElementsByClassName("input_display-item");
// Select P quiz card values
let cardQuestion = document.querySelector(".quiz_question");
let cardAnswer = document.querySelector(".quiz_answer");
// Event Listener on Submit Button for Display Items Idividually
submit.addEventListener("click", function() {
if (question.value === "") {
question.classList.toggle("error");
answer.classList.toggle("error");
} else {
createObj();
let trashCan = createDisplayItem();
trashCan.addEventListener("click", function(e) {
console.log(this.parentNode);
console.log(questions);
console.log(e);
this.parentNode.remove();
});
inputReset();
toggle = questions.length;
start.removeAttribute("disabled");
}
});
start.addEventListener("click", function() {
console.log("clicked");
generateCard();
});
// Event Listener to test if guess is correct
submitGuess.addEventListener("click", function() {
if (guessInput.value.toLowerCase() === qChosen.answer.toLowerCase()) {
card.classList.toggle("flip");
submitGuess.disabled = true;
} else {
console.log("wrong or not working");
}
});
nextQuestion.addEventListener("click", function() {
card.classList.toggle("flip");
submitGuess.disabled = false;
setTimeout(generateCard, 1000);
});
// Create The object for inclusion to array
function createObj() {
let obj = {};
obj.question = question.value;
obj.answer = answer.value;
questions.push(obj);
}
// Resets inputs to blank after submit
function inputReset() {
question.value = "";
answer.value = "";
if (question.classList.contains("error")) {
question.classList.toggle("error");
answer.classList.toggle("error");
}
}
// Creates Each Display Item
function createDisplayItem() {
// Create new Div
let newUl = document.createElement("ul");
// Create Li and Image Elements
let liQuestion = document.createElement("li");
let liAnswer = document.createElement("li");
let trashCan = document.createElement("img");
// Set img src
trashCan.src = "../assets/trash.svg";
// Set classes
newUl.className = "input_display-item";
liQuestion.className = "input_display-question";
liAnswer.className = "input_display-answer";
trashCan.className = "input_display-delete";
// Set LI textContent
liQuestion.textContent = question.value;
liAnswer.textContent = answer.value;
// Append Children
display.appendChild(newUl);
newUl.appendChild(liQuestion);
newUl.appendChild(liAnswer);
return newUl.appendChild(trashCan);
}
//Generating Card Information per question
function generateCard() {
random();
if (toggle < 0) {
cardQuestion.textContent = "There are no more questions left";
cardAnswer.textContent = "There are no more questions left";
} else {
cardQuestion.textContent = qChosen.question;
cardAnswer.textContent = qChosen.answer;
}
}
// Choses a random value for the selection set
function random() {
if (questions.length === 0) {
toggle = -1;
} else {
let num = Math.floor(Math.random() * questions.length);
chosen = questions.splice(num, 1).concat(chosen);
qChosen = chosen[0];
}
}
// Notes
// I need to create a function that upon submit of a guess, checks its value against the answer textContent.
// I will likely need to make the text lowercase for the check to just make sure that they match exactly and that a capital letter wont create a false when its true.
/** Variables
---------------------------------------------------------*/
/** Reset
---------------------------------------------------------*/
* {
margin: 0;
padding: 0; }
*,
*::before,
*::after {
box-sizing: inherit; }
html {
box-sizing: border-box;
font-size: 62.5%; }
body {
font-weight: 400;
line-height: 1.5;
font-size: 2rem;
background-color: #bdbdc7; }
/** Primary Container
---------------------------------------------------------*/
.container {
max-width: 180rem;
display: flex; }
.flex {
display: flex;
justify-content: center;
align-items: center; }
.visible {
visibility: hidden; }
/** Input Section
---------------------------------------------------------*/
input[type="text"] {
padding: 0.5rem;
width: auto;
min-width: 100%;
line-height: 2rem; }
.input {
width: 40rem;
height: 100%;
padding: 1rem;
background-color: #ccc;
display: flex;
align-items: flex-start;
flex-direction: column; }
.input_title {
width: 100%;
display: flex;
flex-direction: column; }
.input_title-label {
display: flex;
justify-content: center; }
.input_title-input {
padding: 0.5rem; }
.input_question {
width: 100%;
display: flex;
flex-direction: column; }
.input_question-label {
display: flex;
justify-content: center; }
.input_question-input {
padding: 0.5rem; }
.input_answer {
width: 100%;
display: flex;
flex-direction: column; }
.input_answer-label {
display: flex;
justify-content: center; }
.input_answer-input {
padding: 0.5rem; }
.input_question-input.error, .input_answer-input.error {
border: 2px red solid; }
.input_submit {
width: 100%; }
.input_submit-button {
margin-top: 1rem;
padding: 0 1.5rem; }
.input_start {
width: 100%; }
.input_display {
width: 100%;
font-size: 1.5rem;
padding: 2rem 0 1rem 0; }
.input_display-item {
margin-bottom: 1rem;
padding: .2rem 2rem;
text-transform: capitalize;
background-color: #fff;
border-radius: 1rem;
list-style: none;
display: flex;
justify-content: space-between;
align-items: center; }
.input_display-item:nth-child(odd) {
background-color: #aaa;
border-radius: 1rem; }
.input_display-delete {
height: 1.8rem;
width: 1.8rem; }
.input :not(.input_display) div {
padding-bottom: 2rem; }
/** Quiz Card
---------------------------------------------------------*/
.card {
display: flex;
justify-content: center;
align-items: center;
width: 100%; }
.card_container {
transform-style: preserve-3d;
perspective: 1000px;
width: 60rem;
margin: 1rem;
cursor: pointer; }
.card_container .front {
transform: rotateY(0deg);
transform-style: preserve-3d; }
.card_container .front:after {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
content: "";
display: block;
opacity: 0.6;
background-color: #000;
backface-visibility: hidden;
border-radius: 10px; }
.card_container .back {
position: absolute;
top: 0;
left: 0;
width: 100%;
background-color: #cedce7;
background: linear-gradient(45deg, #dedce7 0%, #596a72 100%);
transform: rotateY(180deg);
transform-style: preserve-3d; }
.card_container .front,
.card_container .back {
background-color: red;
background-size: cover;
background-position: center;
transition: transform 0.7s cubic-bezier(0.4, 0.2, 0.2, 1);
transition: transform 0.7s cubic-bezier(0.4, 0.2, 0.2, 1);
backface-visibility: hidden;
text-align: center;
min-height: 500px;
height: auto;
border-radius: 10px;
color: #fff;
font-size: 1.5rem; }
.flip {
transition: transform 0.7s cubic-bezier(0.4, 0.2, 0.2, 1);
transition: transform 0.7s cubic-bezier(0.4, 0.2, 0.2, 1); }
.flip .back {
transform: rotateY(0deg);
transform-style: preserve-3d; }
.flip .front {
transform: rotateY(-180deg);
transform-style: preserve-3d; }
.inner {
transform: translateY(-50%) translateZ(60px) scale(0.94);
top: 50%;
position: absolute;
left: 0;
width: 100%;
padding: 2rem;
box-sizing: border-box;
outline: 1px solid transparent;
perspective: inherit;
z-index: 2; }
.front .inner p {
font-size: 2rem;
margin-bottom: 2rem;
position: relative; }
.card_container-guess {
padding-top: 2rem; }
.card_container-guess .guess_input {
width: 2rem;
margin: 1rem auto;
padding: 1rem;
border-radius: 1rem;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.55), 0px 1px 1px rgba(255, 255, 255, 0.5);
border: 1px solid #666;
opacity: 0.6; }
.card_container-guess .guess_input:hover, .card_container-guess .guess_input:focus {
opacity: .8;
color: #08c;
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25), inset 0px 3px 6px rgba(0, 0, 0, 0.25); }
.btnNew {
height: 5rem;
width: 12rem;
margin: 1.5rem 3rem 1rem 0;
font-weight: 700;
color: #333;
background-image: linear-gradient(top, #f4f1ee, #fff);
box-shadow: 0px 8px 30px 1px rgba(0, 0, 0, 0.3), inset 0px 4px 1px 1px white, inset 0px -3px 1px 1px rgba(204, 198, 197, 0.5);
border-radius: 5%;
position: relative;
transition: all .1s linear;
outline: none; }
.btnNew:after {
color: #e9e6e4;
content: "";
display: block;
font-size: 30px;
height: 3rem;
text-decoration: none;
text-shadow: 0px -1px 1px #bdb5b4, 1px 1px 1px white;
position: absolute;
width: 3rem; }
.btnNew:hover {
background-image: linear-gradient(top, #fff, #f4f1ee);
color: #0088cc; }
.btnNew:active {
background-image: linear-gradient(top, #efedec, #f7f4f4);
box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.4), inset opx -3px 1px 1px rgba(204, 198, 197, 0.5);
outline: none; }
.btnNew:active:after {
color: #dbd2d2;
text-shadow: 0px -1px 1px #bdb5b4, 0px 1px 1px white;
outline: none; }
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Flash</title>
<!-- Custom CSS -->
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="container">
<section class="input">
<div class="input_title">
<label class="input_title-label" for="title">Enter a Title</label>
<input class="input_title-input" id="title" type="text" placeholder="List of Towels">
</div>
<div class="input_question">
<label class="input_question-label" for="question">Enter a Question</label>
<input class="input_question-input" id="question" type="text" placeholder="What is 42?">
</div>
<div class="input_answer">
<label class="input_answer-label" for="answer">Enter an Answer</label>
<input class="input_answer-input" id="answer" type="text" placeholder="The Meaning Life, Universe, and Everything">
</div>
<div class="input_submit flex">
<button class="input_submit-button btnNew">Submit</button>
</div>
<div class="input_display"></div>
<div class="input_start flex">
<button type="button" class="input_start-btn btnNew" disabled>Start Quiz</button>
</div>
</section>
<section class="card">
<div class="card_container">
<div class="front">
<div class="inner">
<p class="quiz_question">Question</p>
</div>
</div>
<div class="back">
<div class="inner">
<p class="quiz_answer">Answer</p>
</div>
</div>
<div class="card_container-guess">
<input type="text" class="guess_input">
<button class="submitGuess btnNew">Submit Guess</button>
<button class="nextQuestion btnNew">Next Question</button>
</div>
</div>
</section>
</div>
<!-- Custom JS -->
<script src="js/scripts.js"></script>
</body>
</html>
I am wanting to figure out how to delete an item from the list
How about we change the last line of the function createDisplayItem to ..
function createDisplayItem(){
...
return newUl.appendChild(trashCan) // Added return
}
Now, you have an instance of the newly created trash can element being returned to the calling code, so now all we have to do is add a click event to this specific trash can element and let it delete its parent ul ..
submit.addEventListener("click", function() {
...
let trashCan = createDisplayItem();
trashCan.addEventListener('click', function(){
confirm('Are you sure you want to delete this?')
&& this.parentNode.remove()
})
...
});
So, since now in this code, each trash can takes care of its own parent element, you do not have to worry about finding the index from the parent display element anymore.

Categories