Toggle classes with javascript - javascript

I have 3 buttons. They all toggle different content with css option display: none; display: flex;
With javascript i toggle between the class .show For each button i have a toggle script.
It detects if another button is allready opened but it does not work properly. If i click button 1 and then 2 it works. But if i hit number 3 after that, it removes everything instead of toggle the right class.
Also i find this to be a very long code for something that can most likely be done with less.
Im happy with less code but the point of the question is so that i understand what ive done wrong.
// Button 1
let b1 = document.querySelector(".button1");
let b1_toggle = document.querySelector(".toggle-button-1");
b1.addEventListener("click", function(){
let b2_toggle = document.querySelector(".toggle-button-2");
let b3_toggle = document.querySelector(".toggle-button-3");
let alreadyOpen = false;
if (b2_toggle.classList.contains("show"), b3_toggle.classList.contains("show")) alreadyOpen = true;
b2_toggle.classList.remove("show");
b3_toggle.classList.remove("show");
if (!alreadyOpen)b1_toggle.classList.toggle("show");
});
// Button 2
let b2 = document.querySelector(".button2");
let b2_toggle = document.querySelector(".toggle-button-2");
b2.addEventListener("click", function(){
let b1_toggle = document.querySelector(".toggle-button-1");
let b3_toggle = document.querySelector(".toggle-button-3");
let alreadyOpen = false;
if (b1_toggle.classList.contains("show"), b3_toggle.classList.contains("show")) alreadyOpen = true;
b1_toggle.classList.remove("show");
b3_toggle.classList.remove("show");
if (!alreadyOpen)b2_toggle.classList.toggle("show");
});
// Button 3
let b3 = document.querySelector(".button3");
let b3_toggle = document.querySelector(".toggle-button-3");
b3.addEventListener("click", function(){
let b1_toggle = document.querySelector(".toggle-button-1");
let b2_toggle = document.querySelector(".toggle-button-2");
let alreadyOpen = false;
if (b1_toggle.classList.contains("show"), b2_toggle.classList.contains("show")) alreadyOpen = true;
b1_toggle.classList.remove("show");
b2_toggle.classList.remove("show");
if (!alreadyOpen)b3_toggle.classList.toggle("show");
});
.flex {
display: flex;
}
.button {
background-color: red;
color: white;
padding: 10px 20px;
}
.toggle-button-1 { display: none;}
.toggle-button-1.show { display: flex;}
.toggle-button-2 { display: none;}
.toggle-button-2.show { display: flex;}
.toggle-button-3 { display: none;}
.toggle-button-3.show { display: flex;}
<div class="flex">
<div class="button button1">Button1</div>
<div class="button button2">Button2</div>
<div class="button button3">button3</div>
</div>
<div class="toggle-button-1">Button 1 toggled</div>
<div class="toggle-button-2">Button 2 toggled</div>
<div class="toggle-button-3">Button 3 toggled</div>

the "right" way for codding that:
const toggleButtons = document.querySelector('#toggle-buttons')
document.querySelector('#buttons').onclick = ({target}) =>
{
if (!target.matches('div[data-button]')) return
let actualButton = toggleButtons.className
, targetButton = target.dataset.button
;
toggleButtons.className = (actualButton===targetButton)
? ''
: targetButton
}
.flex {
display : flex;
}
#buttons > div[data-button] {
background-color : red;
color : white;
padding : 10px 20px;
}
#toggle-buttons > div {
display : none;
}
#toggle-buttons.b1 > .toggle-button-1,
#toggle-buttons.b2 > .toggle-button-2,
#toggle-buttons.b3 > .toggle-button-3 {
display : flex;
}
<div id="buttons" class="flex">
<div data-button="b1" > Button1 </div>
<div data-button="b2" > Button2 </div>
<div data-button="b3" > button3 </div>
</div>
<div id="toggle-buttons">
<div class="toggle-button-1">Button 1 toggled</div>
<div class="toggle-button-2">Button 2 toggled</div>
<div class="toggle-button-3">Button 3 toggled</div>
</div>

Related

Toggle show/hide functions between multiple divs

I have a page on my site which has 3 separate 'hidden' divs. Each with it's own 'show/hide' button.
Currently... each div and button set functions independently.
Therefore... if all divs are shown (open) at the same time, they stack according to their respective order.
Instead of that, I would rather restrict the function a bit, so that only div can be shown (open) at a time.
Example: If Div 1 is shown, and the user then clicks the Div 2 (or Dive 3) button, Div 1 (or which ever div is open at the time, will close.
I am not sure how to adjust my code to make that all work together. I have tried a few ideas, but they were all duds. So I posted a generic 'independent' version below.
function show_Div_1() {
var div1 = document.getElementById("Div_1");
if (div1.style.display === "none") {
div1.style.display = "block";
} else {
div1.style.display = "none";
}
}
function show_Div_2() {
var div2 = document.getElementById("Div_2");
if (div2.style.display === "none") {
div2.style.display = "block";
} else {
div2.style.display = "none";
}
}
function show_Div_3() {
var div3 = document.getElementById("Div_3");
if (div3.style.display === "none") {
div3.style.display = "block";
} else {
div3.style.display = "none";
}
}
.div {
width: 270px;
height: 30px;
padding-left: 10px;
}
<button type="button" onclick="show_Div_1()">Div 1 - Red</button>
<button type="button" onclick="show_Div_2()" style="margin-left: 4px">Div 2 - Blue</button>
<button type="button" onclick="show_Div_3()" style="margin-left: 4px">Div 3 - Green</button>
<div id="Div_1" class="div" style="background-color:red; display: none;"></div>
<div id="Div_2" class="div" style="background-color:blue; display: none;"></div>
<div id="Div_3" class="div" style="background-color:green; display: none;"></div>
I would suggest using data attributes for a toggle. Why? you can use CSS for them and you can use more than just a toggle - multiple "values".
Here in this example I do your "click" but also added a double click on the button for a third value. Try some clicks and double clicks!
A bit of overkill perhaps but more than just "toggle" for example you could use this to show "states" of things like a stoplight or any number of things.
Use the grid display and move them by just adding a data attribute value and double click it to get it to go (using css) to some grid-area:, things like that.
const hideValues = {
hide: "hidden",
show: "showme",
double: "dblclick"
};
function dblClickHander(event) {
const targetSelecor = event.target.dataset.target;
const target = document.querySelector(targetSelecor);
const action = target.dataset.hideme == hideValues.double ? hideValues.hide : hideValues.double;
const toggleTargets = document.querySelectorAll('.toggle-target');
toggleTargets.forEach(el => {
el.dataset.hideme = hideValues.hide;
});
target.dataset.hideme = action;
}
function toggleEventHandler(event) {
const targetSelecor = event.target.dataset.target;
const target = document.querySelector(targetSelecor);
const showHide = target.dataset.hideme == hideValues.hide ? hideValues.show : hideValues.hide;
const toggleTargets = document.querySelectorAll('.toggle-target');
toggleTargets.forEach(el => {
el.dataset.hideme = hideValues.hide;
});
target.dataset.hideme = showHide;
}
/* set up event handlers on the buttons */
const options = {
capture: true
};
/* we do this first to prevent the click from happening */
const toggleButtons = document.querySelectorAll('.toggle-button');
toggleButtons.forEach(el => {
el.addEventListener('dblclick', dblClickHander, options);
});
toggleButtons.forEach(el => {
el.addEventListener('click', toggleEventHandler, options)
});
.toggle-target {
width: 270px;
height: 30px;
padding-left: 10px;
}
.toggle-target[data-hideme="hidden"] {
display: none;
}
.toggle-target[data-hideme="showme"] {
display: block;
}
.toggle-target[data-hideme="dblclick"] {
display: block;
border: solid 2px green;
padding: 1rem;
opacity: 0.50;
}
.red-block {
background-color: red;
}
.blue-block {
background-color: blue;
}
.green-block {
background-color: green;
}
<button type="button" class="toggle-button" data-target=".red-block">Div 1 - Red</button>
<button type="button" class="toggle-button" data-target=".blue-block">Div 2 - Blue</button>
<button type="button" class="toggle-button" data-target=".green-block">Div 3 - Green</button>
<div class="toggle-target red-block" data-hideme="hidden">red</div>
<div class="toggle-target blue-block" data-hideme="hidden">blue</div>
<div class="toggle-target green-block" data-hideme="hidden">green</div>
This can be done in many ways. I think the best approach in your case could be
BUTTONS
<button type="button" onclick="show_div('Div_1')">Div 1 - Red</button>
<button type="button" onclick="show_div('Div_2')" style="margin-left: 4px">Div 2 - Blue</button>
<button type="button" onclick="show_div('Div_3')" style="margin-left: 4px">Div 3 - Green</button>
SCRIPT
function show_div(div_id) {
var thisDiv = document.querySelector('#'+div_id);
var thisState = thisDiv.style.display;
// close all in any cases
document.querySelectorAll('.div').forEach(function(el) {
el.style.display = "none";
});
// open this div only if it was closed
if (thisState == "none" ){
thisDiv.style.display = "block";
}
}

Issues with JavaScript list [duplicate]

This question already has answers here:
Clicking a button within a form causes page refresh
(11 answers)
Is there a way to add/remove several classes in one single instruction with classList?
(16 answers)
Closed 8 months ago.
I am assigned to create this to do list using eventlisteners and using JavaScript. My HTML and CSS are given to me however I believe I may be confusing my Id's with each other. The expectation is that a new item is added to the list, can be deleted from the list when clicked on the trashcan, and the input is cleared. Any advice on what I am missing would be helpful... I've been staring at this for 7hrs now.
const todoObjectList = [];
class toDo_Class {
constructor(item) {
this.ulElement = item;
}
add() {
const todoInput = document.querySelector("#todo-input").value;
if (todoInput == "") {
alert("Nothing was entered!");
} else {
const todoObject = {
id: todoObjectList.length,
todoText: todoInput,
isDone: false,
};
todoObjectList.unshift(todoObject);
this.display();
document.querySelector("#todo-input").value = '';
}
}
done_undone(x) {
const selectedTodoIndex = todoObjectList.findIndex((item) => item.id == x);
console.log(todoObjectList[selectedTodoIndex].isDone);
todoObjectList[selectedTodoIndex].isDone == false ? todoObjectList[selectedTodoIndex].isDone == true : todoObjectList[selectedTodoIndex].isDone = false;
this.display();
}
deleteElement(z) {
const selectedDelIndex = todoObjectList.findIndex((item) => item.id == z);
todoObjectList.splice(selectedDelIndex, 1);
this.display();
}
display() {
this.ulElement.innerHTML = "";
todoObjectList.forEach((object_item) => {
const liElement = document.createElement("li");
const delBtn = document.createElement("i");
liElement.innerText = object_item.todoText;
liElement.setAttribute("data-id", object_item.id);
delBtn.setAttribute("data-id", object_item.id);
delBtn.classList.add("fas fa-trash-alt");
liElement.appendChild(delBtn);
delBtn.addEventListener("click", function(e) {
const deleteId = e.target.getAttribute("data-id");
toDoList.deleteElement(deleteId);
});
liElement.addEventListener("click", function(e) {
const selectedId = e.target.getAttribute("data-id");
toDoList.done_undone(selectedId);
});
if (object_item.isDone) {
liElement.classList.add("checked");
}
this.ulElement.appendChild(liElement);
});
}
}
const listSection = document.querySelector("#todo-ul");
toDoList = new toDo_Class(listSection);
document.querySelector("#todo-btn").addEventListener("click", function() {
toDoList.add();
});
document.querySelector("#todo-input").addEventListener("keydown", function(e) {
if (e.keyCode == 13) {
toDoList.add();
}
});
body {
background-color: #34495e;
font-family: "Lato", sans-serif;
}
button {
margin: 0 auto;
float: right;
}
.centered {
margin: 0 auto;
width: 80%;
}
.card {
margin: 50px auto;
width: 18rem;
}
i {
float: right;
padding-top: 5px;
}
<html lang="en">
<body>
<div class="card">
<div class="card-body">
<h3 class="card-title">Today's To Do List</h3>
<form id="todo-form">
<div class="form-group">
<input type="text" class="form-control" id="todo-input" placeholder="What else do you need to do?">
</div>
<div class="form-group">
<input type="submit" id="todo-btn" class="btn btn-secondary btn-block" value="Add Item To List">
</div>
</form>
</div>
<ul class="list-group list-group-flush" id="todo-ul">
<li class="list-group-item">Pick up groceries <i class="fas fa-trash-alt"></i>
</li>
</ul>
</div>
</body>
</html>

Javascript shopping cart - need assistance with javascript portion, why isnt my code working?

I am making a javascript shopping cart. When you click on an item, the contents of that item are appended on the DOM via javascript. Having some difficulty with my code, specifically the sections where I bolded with '** **'
For my 'dropDown-amount' class, I have a number as the value but i cant seem to access that value later on in the code (see dropDown-price).
Also I have a For statement on my if/else statement that tries to loop through each index in 'items' array and to essentially count how many times one specific item is inside the list (so I can update quantity of x item on DOM) but having trouble getting everything to work. I know what I have to do to solve this problem and I know all 3 of these issues are linked together, just don't know what is exactly causing this to fail.
//dropdown menu hidden
const cartDropdown = document.querySelector('.cart-dropDown-items');
//every single + symbol
const addToCartButtons = document.querySelectorAll('.addToCart');
//price of item
const foodPrices = document.querySelectorAll('.selection-row-title');
//name of item
const foodNames = document.querySelectorAll('.selection-row-foodName');
//weight of item
const foodWeights = document.querySelectorAll('.selection-row-weight');
const items = [];
let total = 0;
for (let i = 0; i < addToCartButtons.length; i++) {
addToCartButtons[i].addEventListener('click', function() {
const newItem = document.createElement('div');
newItem.className = 'dropDown-item';
let amountItems = document.querySelector('.amount-items');
newItem.innerHTML =
`<div class='dropDown-title dropDown-info'>
${foodNames[i].innerHTML}
</div>
<div class='dropDown-amount dropDown-info'>
**<p class='amount-items'>${1}</p>**
</div>
<div class='dropDown-price dropDown-info'>
**${Number(foodPrices[i].innerHTML.substring(1)) * Number(amountItems.textContent)}**
</div>`;
console.log(newItem)
// if item currently exists in array, just update amount in checkout and increase count++
if (items.includes(addToCartButtons[i].value)) {
items.push(addToCartButtons[i].value); **
for (let i = 0; i < items.length; i++) {
if (items[i].includes(addToCartButtons[i].value)) {
Number(amountItems.innerHTML) + 1;
}
} **
}
// if items does not exist in array, update dom with new item UI and count = 1 by default
else {
items.push(addToCartButtons[i].value);
cartDropdown.appendChild(newItem);
}
console.log(items)
})
}
.cart-dropDown-items {}
.dropDown-title {}
.dropDown-item {
display: flex;
align-items: center;
text-align: center;
background-color: orange;
margin: 3px;
padding: 4px;
}
.dropDown-info {}
.dropDown-title {
width: 40%;
}
.dropDown-amount {
width: 30%;
text-align: center;
}
.dropDown-amount p {
border: 1px solid black;
width: 35%;
padding: 8px;
margin: 0 auto;
background-color: white;
}
.dropDown-price {
width: 30%;
}
<!--cart dropDown-->
<div class='cart-dropDown'>
<div class='cart-dropDown-header'>
<p>My Carts</p>
<p>Personal Cart</p>
<p class='cart-dropDown-close'>Close</p>
</div>
<div class='cart-dropDown-items'>
<!--
<div class='dropDown-item'>
<div class='dropDown-title dropDown-info'>Mixed bell pepper, 6 ct</div>
<div class='dropDown-amount dropDown-info'>1</div>
<div class='dropDown-price dropDown-info'>$9.84</div>
</div>
next unique item...
-->
</div>
<div class='cart-dropDown-checkout'>
<div class='cart-dropDown-checkout1'>
<p>Go to Checkout</p>
</div>
<div class='cart-dropDown-checkout2'>
<p>$0</p>
</div>
</div>
</div>
</div>

How to get JS tab selector to not scroll

I'm using this JS for tabs. However, it continually makes the selected tab box scroll to the top of the page when clicked.
I can't figure out what part of it is doing that and am trying to get rid of it. Essentially I just want it to function as a normal tab clicker without causing the entire page to scroll.
Any help?
I added a snippet with a large top margin so you can see what happens when you click the tab. I just want those boxes to change without the page physically scrolling to them on its own.
'use strict';
function Tabs() {
var bindAll = function() {
var menuElements = document.querySelectorAll('[data-tab]');
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].addEventListener('click', change, false);
}
}
var clear = function() {
var menuElements = document.querySelectorAll('[data-tab]');
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].classList.remove('active');
var id = menuElements[i].getAttribute('data-tab');
document.getElementById(id).classList.remove('active');
}
}
var change = function(e) {
clear();
e.target.classList.add('active');
var id = e.currentTarget.getAttribute('data-tab');
document.getElementById(id).classList.add('active');
}
bindAll();
}
var connectTabs = new Tabs();
.b-box {margin-top: 1500px;}
.b-tab {
padding: 20px;
border: 1px solid #000;
display: none
}
.b-tab.active {
display: block;
}
.b-nav-tab {
display: inline-block;
padding: 20px;
}
.b-nav-tab.active {
color: #ff4200;
}
<a href="#orange" data-tab="orange" class="b-nav-tab active">
Orange
</a>
<a href="#green" data-tab="green" class="b-nav-tab">
Green
</a>
<a href="#blue" data-tab="blue" class="b-nav-tab">
Blue
</a>
<div class="b-box">
<div id="orange" class="b-tab active">
Orange tab content
</div>
<div id="green" class="b-tab">
Green tab content
</div>
<div id="blue" class="b-tab">
Blue tab content
</div></div>
I updated your code.
Actually you was showing # sign in href so it redirect the position to that box. I removed it.
Good Luck.
'use strict';
function Tabs() {
var bindAll = function() {
var menuElements = document.querySelectorAll('[data-tab]');
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].addEventListener('click', change, false);
}
}
var clear = function() {
var menuElements = document.querySelectorAll('[data-tab]');
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].classList.remove('active');
var id = menuElements[i].getAttribute('data-tab');
document.getElementById(id).classList.remove('active');
}
}
var change = function(e) {
clear();
e.target.classList.add('active');
var id = e.currentTarget.getAttribute('data-tab');
document.getElementById(id).classList.add('active');
}
bindAll();
}
var connectTabs = new Tabs();
.b-tab {
padding: 20px;
border: 1px solid #000;
display: none;
}
.b-tab.active {
display: block;
}
.b-nav-tab {
display: inline-block;
padding: 20px;
}
.b-nav-tab.active {
color: #ff4200;
}
<a href="javascript:void(0)" data-tab="orange" class="b-nav-tab active">
Orange
</a>
<a href="javascript:void(0)" data-tab="green" class="b-nav-tab">
Green
</a>
<a href="javascript:void(0)" data-tab="blue" class="b-nav-tab">
Blue
</a>
<div id="orange" class="b-tab active">
Orange tab content
</div>
<div id="green" class="b-tab">
Green tab content
</div>
<div id="blue" class="b-tab">
Blue tab content
</div>
One way would be to add this e.preventDefault(); to your change function and the other way would be to replace href="#orange" with href="javascript:void(0)". All the same with other hrefs.

In JavaScript DOM is it possible to clone an entire button functionality to another?

I mean is it possible to connect an existing button with a newly created button that will work as the existing button?
Reference site: https://www.ninetypercent.com/products/sleeveless-gather-maxi-dress-c5?color=red
Please paste the below code to the browser console after opening the above link:
let title = document.querySelector(".product-details__name").textContent;
let price;
let oldPrice = document.querySelector(".product-details__old-price");
if (oldPrice) {
price = oldPrice.nextSibling.textContent.trim();
} else {
price = document.querySelector(".js-product-price").textContent;
}
let div = document.createElement("div");
let path = document.querySelector("header");
div.innerHTML = `
<div
id="sticky-nav"
style="position: fixed;
top:110px;
display: none;
padding: 0 10px;
justify-content: space-between;
align-items: center;
background-color:#e4e4e4;
width: 100vw;"
class="sticky-wrapper"
>
<div
style="width:80%; display: flex; justify-content: space-between; align-items:center"
class="sticky-text-wrapper"
>
<p style="margin: 0;" class="sticky-text">${title}</p>
<p style="margin: 0;" class="sticky-price">${price}</p>
</div>
<span style="width: 18%; display: flex; justify-content: space-around;" class="button"></span>
</div>`;
path.appendChild(div);
const sourceElement = document.querySelector(".js-product-details-submit-wrapper button");
const destination = document.querySelector(".button");
const copy = sourceElement.cloneNode(true);
destination.appendChild(copy);
window.addEventListener("scroll", () => {
const scrollPos = window.scrollY;
if (scrollPos > 1500) {
document.getElementById("sticky-nav").style.display = "flex";
} else {
document.getElementById("sticky-nav").style.display = "none";
}
});
I want to connect the sticky-nav button to the existing "Add to Bag" button and the functionality of both button will be the same. The same messaging as in control will appear on the PDP(Product details page) and basket icon quantity will increase. How can I do that?
Within the click-handler for the one button you could trigger the other, like so (names are made up of course):
stickyNavBtn.onclick = () => addToBagBtn.click();
You can create a function (in JS) and call it from both the original button (addToBagBtn in your case) and newly created button (stickyNavBtn in your case).
Like:-
/*JavaScript*/
function doSomething() {
alert("Replace with whatever task you like!");
}
var button = document.createElement("button");
button.innerText = "Newly Created Button";
button.onclick = function() {
doSomething();
}
document.getElementById("container").appendChild(button);
<!--HTML-->
<button id="button1" onclick="doSomething()">Original Button</button>
<br><br>
<div id="container">
<!--New button will be created here from JS-->
</div>

Categories

Resources