classList.toggle() only works one time - javascript

I am trying to implement a different pop-up window when I click different divs. Here's an example:
<div class="column" onclick="togglePopup('popup', 2)">Residential cleaning</div> <!--Function used here. -->
<div id="overlay">
<div class="popup">
<div class="popup_content">
<div class="close_btn" onclick="closePopup('popup',2)">×</div>
<h1>Some heading</h1>
<p>SOME TEXT</p>
<img src="assets/some_image.jpg" alt="image" width="249">
</div>
</div> <!--END of the popup window -->
</div>
When I click on the div of class column, I should see the popup div, however it only works with the very first one. Here's the JavaScript:
function togglePopup(popup_class, div_num){
overlay.style.display="flex";
let all_divs = document.getElementsByClassName(popup_class);
let my_div = all_divs[div_num-1];
my_div.classList.toggle("active");
console.log(my_div)
}
function closePopup(popup_class, div_num){
overlay.style.display="none";
let all_divs = document.getElementsByClassName(popup_class);
let my_div = all_divs[div_num-1];
my_div.classList.toggle("active");
console.log(my_div)
}
The function saves all the divs of class popup in the let all_divs, then it uses the parameter div_num to select a specific div and finally toggle the class "active" (CSS will be below). But like I said it only works one time. By the way the id overlay is originally hidden.
CSS:
.popup.active .popup_content {
transition:all 300ms ease-in-out;
transform:translate(-50%,-50%) scale(1);
}
#overlay {
position:fixed;
top:0px;
left:0px;
width:100%;
height:100%;
background-color:rgba(0,0,0,0.7);
z-index:1;
display:none;
}
I really don't know why this happens, it works as intended only with the first element of the list "all_divs". Is there a way around this? I already tried many things and realized it only works on the first element of the list. I did not include the first div in the code, (the one that works) because it's the exact same thing, except instead of a 1 in onclick="togglePopup('popup', 1)" it has a 2.

You're attempting to access a HTMLCollection by an index that does not exist. e.g.
let all_divs = document.getElementsByClassName('popup');
let my_div = all_divs[2]; // undefined
Instead you should add data to the open button to explicitly describe which popup it should open by its unique id.
Also, you should:
use querySelectorAll to addEventListener to each of the relevant buttons
use html buttons instead of divs
const openBtns = document.querySelectorAll('.open_btn')
const closeBtns = document.querySelectorAll('.close_btn')
openBtns.forEach(openBtn => openBtn.addEventListener('click', (e) => {
const popupIdToOpen = e.currentTarget.dataset.popup;
const popupToOpen = document.querySelector(`#${popupIdToOpen}`)
if (popupToOpen) {
popupToOpen.classList.add('active')
}
}))
closeBtns.forEach(closeBtn => closeBtn.addEventListener('click', (e) => {
const popupToClose = e.currentTarget.closest(".overlay")
if (popupToClose) {
popupToClose.classList.remove('active')
}
}))
.overlay {
color: white;
position: fixed;
top: 0;
background-color: #000;
display: none;
}
.active {
display: block;
}
<button class="open_btn" data-popup="popup1">Popup 1</button>
<div class="overlay" id="popup1">
<button class="close_btn">×</button >
<h1>Popup 1 heading</h1>
<p>Popup 1 Content</p>
</div>
<button class="open_btn" data-popup="popup2">Popup 2</button>
<div class="overlay" id="popup2">
<button class="close_btn">×</button >
<h1>Popup 2 heading</h1>
<p>Popup 2 Content</p>
</div>
<button class="open_btn" data-popup="popup3">Popup 3</button>
<div class="overlay" id="popup3">
<button class="close_btn">×</button >
<h1>Popup 3 heading</h1>
<p>Popup 3 Content</p>
</div>

Related

jQuery multiple button show and hide elements when click

I have a multiple buttons has show and hide class. Which is also activate the elements every toggle click. I want to make it a shorter code and make it globally. Please help me how to do it. All I want is to achieve a lesser code and same with the result.. Thank you.
$('.show').on('click', function () {
$(this).addClass('inactive');
$('.hide').removeClass('inactive');
$('.helloworld').removeClass('inactive')
})
$('.hide').on('click', function () {
$(this).addClass('inactive');
$('.show').removeClass('inactive');
$('.helloworld').addClass('inactive')
})
$('.ok').on('click', function () {
$(this).addClass('inactive');
$('.cancel').removeClass('inactive');
$('.thanks').removeClass('inactive')
})
$('.cancel').on('click', function () {
$(this).addClass('inactive');
$('.ok').removeClass('inactive');
$('.thanks').addClass('inactive')
})
<style>
.inactive{
display:none;
}
button{
padding:5px 25px;
color: #fff;
background-color:#1d9bf0;
margin-top: 10px;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="show"> + Show </button>
<button class="hide inactive"> - Hide </button>
<p class="helloworld inactive">Hello WOrld</p>
<br>
<button class="ok"> + Ok </button>
<button class="cancel inactive"> - Cancel </button>
<p class="thanks inactive">Thank you</p>
The technique you're looking for here is DRY, or Don't Repeat Yourself. To do this, look for the common patterns in the logic you have.
In this case each button has its text updated, and it changes the state of it's following sibling. Therefore you can place common class attributes on the elements so that the same JS logic can be applied to them all. From there you can use jQuery's DOM traversal methods to relate the elements to each other, and also data attributes to store custom metadata about the elements which can be used when the click event occurs.
Finally you can use toggleClass() to add/remove the classes to display/hide the elements as necessary.
Here's a working example:
$('.toggle').on('click', e => {
let $btn = $(e.target);
$btn
.text(() => $btn.data($btn.hasClass('show') ? 'hide-text' : 'show-text')).toggleClass('show') // update text
.next().toggleClass('inactive'); // toggle related content
})
<style>
.inactive {
display: none;
}
button {
padding: 5px 25px;
color: #fff;
background-color: #1d9bf0;
margin-top: 10px;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="toggle-container">
<button class="toggle show" data-show-text="+ Show" data-hide-text="- Hide">+ Show</button>
<p class="content inactive">Hello WOrld</p>
</div>
<div class="toggle-container">
<button class="toggle show" data-show-text="+ Ok" data-hide-text="- Cancel">+ Ok</button>
<p class="content inactive">Thank you</p>
</div>

Use onclick in button to add a border to another div

Basically explained : click button A to add a border to card A, click button B to add a border to card B and remove border from card A etc. (these "cards" are also set up to be side scrolling so having the "highlighted card" scroll into view if it is out of view, so if you have any advice on how to do that too, I'm all ears)
This is what I have so far, but I keep getting an error that says the highlight function is not defined. I have it called on the button as onclick="highlight()"
function highlight() {
var item = document.getElementById('item-id')
var unhighlight = document.getElementsByClassName('item-class')
unhighlight.removeClass('border');
item.addClass('border');
}
Here's a simplified version that uses data attributes on the buttons and the divs to update the DOM.
Move your inline code to its own script.
Cache your elements.
Add an event listener to each button
Inside your highlight function: get the id from the button's data attribute, remove the borders from all the other divs, use the id to identify which div should now be highlighted, and then scroll it into view.
const buttons = document.querySelectorAll('button');
const items = document.querySelectorAll('div');
buttons.forEach(button => button.addEventListener('click', highlight, false));
function highlight(e) {
const button = e.target;
const id = button.dataset.id;
items.forEach(item => item.classList.remove('border'));
const div = document.querySelector(`div[data-id="${id}"]`);
div.classList.add('border');
div.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
}
#container { display: flex; flex-direction: row; }
div { padding: 1em; margin: 0.5em; width: 1500px; }
.border { border: 2px solid black; }
<button data-id="1">Click 1</button>
<button data-id="2">Click 2</button>
<button data-id="3">Click 3</button>
<div id="container">
<div data-id="1">Hallo 1</div>
<div data-id="2">Hallo 2</div>
<div data-id="3">Hallo 3</div>
</div>
Consider the following example.
$(function() {
$("button").click(function() {
var target = $(this).data("rel");
$(target).toggleClass("highlight");
});
});
.highlight {
border: 1px solid yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="button-a" data-rel="#card-a">A</button>
<button id="button-b" data-rel="#card-b">B</button>
<button id="button-c" data-rel="#card-c">C</button>
<div id="card-a" class="card">
<div class="card-title">Card A</div>
</div>
<div id="card-b" class="card">
<div class="card-title">Card B</div>
</div>
<div id="card-c" class="card">
<div class="card-title">Card C</div>
</div>

How can I usa this JavaScript code with more than 2 sections?

I'm new in javascript and Html and I created two scrolling button to go from section1 to section2.
What can I do if I need to have more than two sections?
function f1() {
var elmnt1 = document.getElementById("sez1");
elmnt1.scrollIntoView();
}
function f2() {
var elmnt2 = document.getElementById("sez2");
elmnt2.scrollIntoView();
}
The best way to do this is by creating a pattern to any solution to make it a generic solution.
Mark your sections with an attribute of your liking, e.g.: scroll-to-id="1" and your sections can now look like <div scroll-to-id="1"></div><div scroll-to-id="2"></div><div scroll-to-id="3"></div><div scroll-to-id="4"></div>
Now make a function which will take the number as an input and scroll to the desired section. To get all targeted sections we can call the function document.querySelectorAll('[scroll-to-id]') to get all the elements with the attribute scroll-to-id.
Then find the element with the desired scroll-to-id to go to and scrollTo that element.
Full solution below:
function scrollToSection(sectionId) {
const sections = document.querySelectorAll('[scroll-to-id]');
for(let section of sections) {
if(section.getAttribute('scroll-to-id') == sectionId) {
section.scrollIntoView();
// scrollIntoView doesnot have the best browser supports. It is better to calculate the position of the section and do a scrollTo() or scrollBy()
}
}
}
button {
margin-bottom: 20px;
}
div {
padding-top: 80px;
padding-bottom: 80px;
border: 1px solid #cecece;
}
<button onclick="scrollToSection(1)">Scroll To 1</button>
<button onclick="scrollToSection(2)">Scroll To 2</button>
<button onclick="scrollToSection(3)">Scroll To 3</button>
<button onclick="scrollToSection(4)">Scroll To 4</button>
<div scroll-to-id="1">Hello 1</div>
<div scroll-to-id="2">Hello 2</div>
<div scroll-to-id="3">Hello 3</div>
<div scroll-to-id="4">Hello 4</div>
pass element as an argument to your function
function scroll(el){
el.scrollIntoView()
}
Welcome to Stackoverflow #Francy3k!
Just update your existing function to accept the ID of the section you'd like to scroll to:
.buttons { position:fixed; top:0; left:0; width:100%; background:white; padding:10px 0; text-align:center }
#sez1 { height:300px; background:green; vertical-align:middle }
#sez2 { height:300px; background:red; vertical-align:middle }
#sez3 { height:300px; background:blue; vertical-align:middle }
<div class="buttons">
<button onclick="scrollToSection(1)">Go to SEZ1</button>
<button onclick="scrollToSection(2)">Go to SEZ2</button>
<button onclick="scrollToSection(3)">Go to SEZ3</button>
</div>
<div id="sez1">
</div>
<div id="sez2">
</div>
<div id="sez3">
</div>
<script>
function scrollToSection(index) {
const section = document.getElementById("sez" + index);
section.scrollIntoView();
}
</script>
Provided the section IDs follow the same convention, you should be able to use this pattern.

Switch DIV content using only Javascript

Why is it not working after I added this code below? As you can see on snippet. Container #1 is not working when click. I've added title and content on it but it is not showing.
Is there anything that I can use instead of getElementsByTagName?
function showDiv(idInfo) {
var sel = document.getElementById('divLinks').getElementsByTagName('div');
for (var i = 0; i < sel.length; i++) {
sel[i].style.display = 'none';
}
document.getElementById('container' + idInfo).style.display = 'block';
}
#container1,
#container2,
#container3,
#container4 {
display: none;
border: 3px solid blue;
height: 200px;
overflow: hidden;
}
<div id="linkDiv">
Home
link 1
link 2
link 3
link 4
</div>
<div id="container">
The container I want all content divs to load into... and by default, to show the first container content
</div>
<div id="divLinks">
<div id="container1">Container #1
<div>Title</div>
<div>Content:</div>
<p>Whole bunch of text 1</p>
</div>
<div id="container2">
Container #2
<p>Whole bunch of text 2</p>
</div>
<div id="container3">
Container #3
<p>Whole bunch of text 3</p>
</div>
<div id="container4">
Container #4
<p>Whole bunch of text 4</p>
</div>
</div>
You can use querySelectorAll to target the direct children of divLinks only.
function showDiv(idInfo) {
const sel = document.querySelectorAll('#divLinks > div');
sel.forEach(item => {
item.style.display = item.id === `container${idInfo}` ? 'block' : 'none';
});
}
#container1,
#container2,
#container3,
#container4 {
display: none;
border: 3px solid blue;
height: 200px;
overflow: hidden;
}
<div id="linkDiv">
Home
link 1
link 2
link 3
link 4
</div>
<div id="container">
The container I want all content divs to load into... and by default, to show the first container content
</div>
<div id="divLinks">
<div id="container1">Container #1
<div>Title</div>
<div>Content:</div>
<p>Whole bunch of text 1</p>
</div>
<div id="container2">
Container #2
<p>Whole bunch of text 2</p>
</div>
<div id="container3">
Container #3
<p>Whole bunch of text 3</p>
</div>
<div id="container4">
Container #4
<p>Whole bunch of text 4</p>
</div>
</div>
You just only need to get Parent children not nested child.
Just modify
var sel = document.getElementById('divLinks').getElementsByTagName('div');
with
sel = document.querySelector('#divLinks').children;

Changing Parent Class With Child's Button Using Javascript

What i'm basically asking is if i could do this?
function Close(){
// what the furry mermaids should i put in here!?
}
.vissible {
display: block;
}
.hidden {
display: none;
}
#parentDiv1{
background-color: red;
}
#parentDiv2{
background-color:blue;
}
<div id="parentDiv1" class="visible">
<button id="closebtn" onclick="Close()">close</button>
<p> This is div 1 </p>
</div>
<div id="parentDiv2" class="visible">
<button id="closebtn" onclick="Close()">close</button>
<p> This is div 2 </p>
</div>
There are two divs that contain the same button but each button changes their parent div's class to hidden. Their parent's div only.
This is because i want to make a lot of pages but they close one by one with the same code and the same button. I'm wanting a minimalist solution here.
Oh and please don't be vague with your answers. If you are going to present it please explain how it works and how to apply it. A working code example is desired.
Note: Only one function may be used and is used by two identical buttons that are separated by two Divs.
Please and Thank You! :D
You can pass the current element context this to method. Then parent div can be accessed using parentNode property. To manipulate element's class use Element.classList property.
function Close(elem) {
elem.parentNode.classList.add('hidden')
elem.parentNode.classList.remove('visible')
}
function Close(elem) {
elem.parentNode.classList.add('hidden')
elem.parentNode.classList.remove('visible')
}
.vissible {
display: block;
}
.hidden {
display: none;
}
#parentDiv1 {
background-color: red;
}
#parentDiv2 {
background-color: blue;
}
<div id="parentDiv1" class="visible">
<button id="closebtn" onclick="Close(this)">close</button>
<p>This is div 1</p>
</div>
<div id="parentDiv2" class="visible">
<button id="closebtn" onclick="Close(this)">close</button>
<p>This is div 2</p>
</div>
I would recommend you to use unobtrusive event handler. Instead of using ugly inline click handler.
document.addEventListener("DOMContentLoaded", function(event) {
var elements = document.querySelectorAll('.closebtn');
elements.forEach(function(element) {
element.addEventListener('click', function() {
this.parentNode.classList.add('hidden');
this.parentNode.classList.remove('visible');
})
});
});
.vissible {
display: block;
}
.hidden {
display: none;
}
#parentDiv1 {
background-color: red;
}
#parentDiv2 {
background-color: blue;
}
<div id="parentDiv1" class="visible">
<button type="button" class="closebtn">close</button>
<p>This is div 1</p>
</div>
<div id="parentDiv2" class="visible">
<button type="button" class="closebtn">close</button>
<p>This is div 2</p>
</div>
Pass event in close function and access to parent with event.target.parentNode;
function Close(event){
const parent= event.target.parentNode
parent.classList.remove('vissible');
parent.classList.add('hidden');
// what the furry mermaids should i put in here!?
}
.vissible {
display: block;
}
.hidden {
display: none;
}
#parentDiv1{
background-color: red;
}
#parentDiv2{
background-color:blue;
}
<div id="parentDiv1" class="visible">
<button id="closebtn" onclick="Close(event)">close</button>
<p> This is div 1 </p>
</div>
<div id="parentDiv2" class="visible">
<button id="closebtn" onclick="Close(event)">close</button>
<p> This is div 2 </p>
</div>
Here you go :)
https://jsfiddle.net/
$('#parentDiv1 #closebtn').on('click', function(){
$(this).parent().addClass('hidden')
})
$('#parentDiv2 #closebtn').on('click', function(){
$(this).parent().addClass('hidden')
})
This is how i would do it. Use something like this.
$(".closebtn").click(function(){
$(this).parent().removeClass("visible");
$(this).parent().addClass("hidden");
});
Also in your css your class is spelled vissible and in your html the class it spelled visible

Categories

Resources