Recently I came across a question, here it goes, I have two buttons BTN1, BTN2. On click of BTN1 it should disappear and two new BTN_A and BTN_B has to appear, on click or BTN_A, it should disappear & BTN1.A, BTN1.B should appear. How to achieve this if I still want to click on BTN's and make them disappear.
I thought we can make "display: none" but for how many, how to render a html dynamically in the best way?
There are 4 possibilities to hide a DOM element like a button.
The first three are css style changes:
opacity: 0;
visibility: hidden;
display: none;
removing the element from the DOM with removeChild()
The needed code is the following:
document.getElementById('myBtn').style.opacity = '0';
document.getElementById('myBtn').style.display = "none";
document.getElementById('myBtn').style.visibility = "hidden";
The code for adding and removing elements goes like this:
var div = document.getElementById('endlessBtnContainer');
div.innerHTML = div.innerHTML + '<button id="addBtn" onclick="addBtn()">addBtn</button>';
and removing
var div = document.getElementById('addBtn');
div.parentNode.removeChild(div);
opacity: 0; will simply make the element not visible, but it is still existing normally. So you can still click the button.
visibility: hidden; Will hide the element, but it will still take
up the same space it had when it was still visible. It won't be
clickable anymore.
display: none; Will hide the element and it won't take any space
anymore. Therefore following HTML elements will flow into the new
available space.
#btnA, #btnB, #btn1A, #btn1B{
opacity: 0;
}
<script>
function hideElem()
{
for (var i = 0; i < arguments.length; i++) {
//document.getElementById(''+arguments[i]).style.opacity = '0';//just invisible, still clickable
//document.getElementById(''+arguments[i]).style.display = "none";//no space taken
document.getElementById(''+arguments[i]).style.visibility = "hidden";//invisible, not clickable
}
}
function showElem(targetElem)
{
for (var i = 0; i < arguments.length; i++) {
document.getElementById(''+arguments[i]).style.opacity = '1';
}
}
function addBtn()
{
var div = document.getElementById('endlessBtnContainer');
div.innerHTML = div.innerHTML + '<button id="addBtn" onclick="addBtn()">addBtn</button>';
}
function removeBtn()
{
var div = document.getElementById('addBtn');
div.parentNode.removeChild(div);
}
</script>
<button id="btn1" onclick="hideElem('btn1', 'btn2'); showElem('btnA', 'btnB');">
btn1
</button>
<button id="btn2">
btn2
</button>
<button id="btnA" onclick="hideElem('btnA', 'btnB'); showElem('btn1A', 'btn1B');">
btnA
</button>
<button id="btnB">
btnB
</button>
<button id="btn1A">
btn1A
</button>
<button id="btn1B">
btn1B
</button>
<button id="addBtn" onclick="addBtn()">
addBtn
</button>
<button id="removeBtn" onclick="removeBtn()">
removeBtn
</button>
<div id="endlessBtnContainer">
</div>
Related
I have JavaScript to show/hide div on click. Inside that div are more buttons to show/hide PNGs.
I want the clicked button to have a bottom border line until another button in that div is clicked.
I have achieved this but each time I click on a button in the shown div the bottom border line stays on the button when I click the next button.
I've spent hours trying to fix this. please help
let wildCard = document.querySelectorAll(".element-select-container button");
for (let button of wildCard) {
button.addEventListener('click', (e) => {
const et = e.target;
const active = document.querySelector(".active");
let redline = (".redline");
if (active) {
active.classList.remove("redline");
active.classList.remove("active");
}
et.classList.add("active");
et.classList.add("redline");
let allContent = document.querySelectorAll('.button-wrapper');
for (let content of allContent) {
if(content.getAttribute('data-e') === button.getAttribute('data-e')) {
content.style.display = "block";
}
else {
content.style.display = "none";
}
}
});
}
HTML
<div class="element-select-container">
<button id="but81" class="but81 redline" data-e="81" type="button" name="">Doors</button>
<button id="but82" class="but82" data-e="82" type="button" name="">Windows</button>
<button id="but83" class="but83" data-e="83" type="button" name="">Facia</button>
<button id="but84" class="but84" data-e="84" type="button" name="">Guttering</button>
<button id="but85" class="but85" data-e="85" type="button" name="">Garage</button>
<button id="but86" class="but86" data-e="86" type="button" name="">Steps</button>
</div>
CSS
.redline {
border-bottom: 2px solid red;
}
The issue is, on first load, the first button is redline but not active - so, when you press a different button, the code to remove redline from active doesn't find active so redline isn't removed
simple fix
const active = document.querySelector(".active,.redline");
As follows
let wildCard = document.querySelectorAll(".element-select-container button");
for (let button of wildCard) {
button.addEventListener('click', (e) => {
const et = e.target;
const active = document.querySelector(".active,.redline");
if (active) {
active.classList.remove("redline");
active.classList.remove("active");
}
et.classList.add("active");
et.classList.add("redline");
let allContent = document.querySelectorAll('.button-wrapper');
for (let content of allContent) {
if(content.getAttribute('data-e') === button.getAttribute('data-e')) {
content.style.display = "block";
}
else {
content.style.display = "none";
}
}
});
}
.redline {
border-bottom: 2px solid red;
}
<div class="element-select-container">
<button id="but81" class="but81 redline" data-e="81" type="button" name="">Doors</button>
<button id="but82" class="but82" data-e="82" type="button" name="">Windows</button>
<button id="but83" class="but83" data-e="83" type="button" name="">Facia</button>
<button id="but84" class="but84" data-e="84" type="button" name="">Guttering</button>
<button id="but85" class="but85" data-e="85" type="button" name="">Garage</button>
<button id="but86" class="but86" data-e="86" type="button" name="">Steps</button>
</div>
The abbreviated JS file below provides the same functionality for 100 buttons.
All buttons are identified by ID names such as #btn1, #btn2 etc.
The buttons trigger the hide/show of content contained within div tags labelled within corresponding class names such as .btn1, .btn2, etc.
For example, selecting #btn1 is tied to the content within content content content .
The process is to select a button, then whichever button is selected, hide the content within all the 100 DIVs and then show the selected button’s associated content.
In writing the JS file I have written out the whole function 100 times - listing each one of 100 buttons to be selected, all 100 div areas to be hidden, and then the one div area to be shown.
How could this be simplified into a smarter and more concise function?
// JavaScript Document
$(document).ready(function() {
$('#btn0').click(function() {
location.reload();
});
});
$(document).ready(function() {
$('#btn1').click(function() {
$('.btn0').hide();
$('.btn1').hide();
$('.btn2').hide();
$('.btn3').hide();
$('.btn4').hide();
$('.btn5').hide();
$('.btn6').hide();
$('.btn7').hide();
$('.btn8').hide();
$('.btn9').hide();
$('.btn10').hide();
$('.btn11').hide();
$('.btn11').hide();
$('.btn12').hide();
$('.btn13').hide();
$('.btn14').hide();
$('.btn15').hide();
$('.btn16').hide();
$('.btn17').hide();
$('.btn18').hide();
$('.btn19').hide();
$('.btn20').hide();
$('.btn21').hide();
$('.btn22').hide();
$('.btn23').hide();
$('.btn24').hide();
$('.btn25').hide();
$('.btn26').hide();
$('.btn27').hide();
$('.btn28').hide();
$('.btn29').hide();
$('.btn30').hide();
$('.btn31').hide();
$('.btn32').hide();
$('.btn33').hide();
$('.btn34').hide();
$('.btn35').hide();
$('.btn36').hide();
$('.btn37').hide();
$('.btn38').hide();
$('.btn39').hide();
$('.btn40').hide();
$('.btn41').hide();
$('.btn42').hide();
$('.btn43').hide();
$('.btn44').hide();
$('.btn45').hide();
$('.btn46').hide();
$('.btn47').hide();
$('.btn48').hide();
$('.btn49').hide();
$('.btn50').hide();
$('.btn51').hide();
$('.btn52').hide();
$('.btn53').hide();
$('.btn54').hide();
$('.btn55').hide();
$('.btn51').hide();
$('.btn52').hide();
$('.btn53').hide();
$('.btn54').hide();
$('.btn55').hide();
$('.btn56').hide();
$('.btn57').hide();
$('.btn58').hide();
$('.btn59').hide();
$('.btn60').hide();
$('.btn61').hide();
$('.btn62').hide();
$('.btn63').hide();
$('.btn64').hide();
$('.btn65').hide();
$('.btn66').hide();
$('.btn67').hide();
$('.btn68').hide();
$('.btn69').hide();
$('.btn70').hide();
$('.btn71').hide();
$('.btn72').hide();
$('.btn73').hide();
$('.btn74').hide();
$('.btn75').hide();
$('.btn76').hide();
$('.btn77').hide();
$('.btn78').hide();
$('.btn79').hide();
$('.btn80').hide();
$('.btn81').hide();
$('.btn82').hide();
$('.btn83').hide();
$('.btn84').hide();
$('.btn85').hide();
$('.btn86').hide();
$('.btn87').hide();
$('.btn88').hide();
$('.btn89').hide();
$('.btn90').hide();
$('.btn91').hide();
$('.btn92').hide();
$('.btn93').hide();
$('.btn94').hide();
$('.btn95').hide();
$('.btn96').hide();
$('.btn97').hide();
$('.btn98').hide();
$('.btn99').hide();
$('.btn100').hide();
$('.btn98').hide();
$('.btn99').hide();
$('.btn100').hide();
$('.btn1').show();
});
});
$(document).ready(function() {
$('#btn2').click(function() {
$('.btn0').hide();
$('.btn1').hide();
$('.btn2').hide();
$('.btn3').hide();
$('.btn4').hide();
$('.btn5').hide();
$('.btn6').hide();
$('.btn7').hide();
$('.btn8').hide();
$('.btn9').hide();
$('.btn10').hide();
$('.btn11').hide();
…………………….. BTN12 to 97 ……………………..
$('.btn98').hide();
$('.btn99').hide();
$('.btn100').hide();
$('.btn1').show();
});
});
Etc., up to 100 buttons
// JavaScript Document
Assuming you can't change the html structure, I would probably do:
$('[id^="btn"]').on('click', function() {
const id = $(this).attr('id');
$('[class^="btn"]').hide();
$(`.${id}`).show();
});
Which will listen to the click event on any element where the id starts with btn, then hide all elements where the class starts with btn, then show the element with the same class as the id that was just clicked (e.g. #btn2 click will show .btn2)
something like this.
for(let i = 0;i<=99;i++){
let btnclass= ".btn" + i;
$(btnclass).hide()
}
You can use a for loop to iterate from 0 to 100:
$(document).ready(function() {
$("#btn1").click(function() {
for (let i = 0; i <= 100; i++) {
$(`.btn${i}`).hide();
}
});
})
Full version:
// JavaScript Document
$(document).ready(function() {
$("#btn0").click(function() {
location.reload();
});
});
$(document).ready(function() {
$("#btn1").click(function() {
for (let i = 0; i <= 100; i++) {
$(`.btn${i}`).hide();
}
});
})
$(document).ready(function() {
$("#btn2").click(function() {
for (let i = 0; i <= 100; i++) {
$(`.btn${i}`).hide();
}
});
});
Common class and data attributes along with event delegation makes the code easier to maintain.
document.querySelector("#wrapper").addEventListener("click", function (event) {
var toggles = event.target.dataset.toggles;
// Hide previous selected elements
var selectedElems = document.querySelectorAll(".out.selected");
if (selectedElems.length){
selectedElems.forEach(function (elem) {
elem.classList.remove("selected");
});
}
// show the new active elements
const activeElems = document.querySelectorAll(toggles);
if (activeElems.length){
activeElems.forEach(function (elem) {
elem.classList.add("selected");
});
}
});
.out {
display: none;
}
.out.selected {
display: block;
}
<div id="wrapper">
<button id="btn1" data-toggles=".out1">1</button>
<button id="btn2" data-toggles=".out2">2</button>
<button id="btn3" data-toggles=".out3">3</button>
<button id="btn4" data-toggles=".out4">4</button>
</div>
<div class="out out1">1</div>
<div class="out out2">2</div>
<div class="out out3">3</div>
<div class="out out4">4</div>
If you want to use jQuery
$("#wrapper").on("click", "[data-toggles]", function (event) {
var toggles = $(this).data('toggles');
$(".out.selected").removeClass("selected");
$(toggles).addClass("selected");
});
.out {
display: none;
}
.out.selected {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="wrapper">
<button id="btn1" data-toggles=".out1">1</button>
<button id="btn2" data-toggles=".out2">2</button>
<button id="btn3" data-toggles=".out3">3</button>
<button id="btn4" data-toggles=".out4">4</button>
</div>
<div class="out out1">1</div>
<div class="out out2">2</div>
<div class="out out3">3</div>
<div class="out out4">4</div>
I am trying to make it so that when I click on a category, the text, for that category appears. Then when a new category is clicked, the previous category text is removed, and the text for the new category takes its place. Right now, no matter what button I click, all items appear.
index.html
<div class='container'>
<button class="button">Fit guide</button>
<button class="button">Care</button>
<button class="button">Materials</button>
<div class="single-entry">
<p class="fit">lkasdnf;aksdjf;askdjflkjsdhflkajsdhflaksjdhfaksjdhflkasjdhfasjdbflaksjdhflkasdjhfkajsdbas</p>
</div>
<div class="single-entry">
<p>lkasdnf;aksdjf;askdjflkjsdhflkajsdhflaksjdhfaksjdhflkasjdhfasjdbflaksjdhflkasdjhfkajsdbas</p>
</div>
<div class="single-entry">
<p>lkasdnf;aksdjf;askdjflkjsdhflkajsdhflaksjdhfaksjdhflkasjdhfasjdbflaksjdhflkasdjhfkajsdbas</p>
</div>
</div>
index.js
const displayEntryButton = document.querySelectorAll('.button');
const test = document.querySelector('.fit');
for (var j = 0; j < displayEntryButton.length; j++) {
displayEntryButton[j].addEventListener('click', function () {
const allEntries = document.querySelectorAll('.single-entry');
for (let index = 0; index < allEntries.length; index++) {
if(allEntries[index].style.display === 'none') {
allEntries[index].style.display = 'block'
} else {
allEntries[index].style.display = 'none';
}
}
})
}
I've adjusted your code a bit to get things working.
My adjustment primarily adds an id to each entry div and a matching data-value to each button.
The id and data-value are then used for comparison when a button is clicked. If there's a match, the corresponding content is displayed.
Try the snippet below. - Comments are included within the code.
const displayButtons = document.querySelectorAll('.button');
const entryDivs = document.querySelectorAll('.single-entry');
//start for loop to iterate over each button
for (j = 0; j < displayButtons.length; j++) {
//btnValue is declared to capture the data-value attribute
const btnValue = displayButtons[j].getAttribute('data-value');
displayButtons[j].addEventListener('click', function() {
//nested loop to iterate over each entry div
for (i = 0; i < entryDivs.length; i++) {
//entryId is declared to capture the div id
const entryId = entryDivs[i].id;
//if btnValue matches entryId
if (btnValue === entryId) {
//show the corresponding content
entryDivs[i].style.display = "block"
} else {
//otherwise show nothing
entryDivs[i].style.display = "none"
}
}
})
}
/*set all entries to display none by default*/
.single-entry {
display: none;
}
<div class='container'>
<!-- Here we are setting a data-value on each button -->
<!-- The data-value must match the id for each corresponding div -->
<button class="button" data-value="fit">Fit guide</button>
<button class="button" data-value="care">Care</button>
<button class="button" data-value="materials">Materials</button>
<!-- Each div below has an id to match each buttons data-value -->
<div class="single-entry" id="fit">
<p>FIT ENTRY</p>
</div>
<div class="single-entry" id="care">
<p>CARE ENTRY</p>
</div>
<div class="single-entry" id="materials">
<p>MATERIALS ENTRY</p>
</div>
</div>
I am trying to add different classes to a div based on what is clicked, which I've managed to do, but need to remove the previously clicked/selected class and replace with the clicked one, Can't seem to get the remove part right. Most of the solutions I've come across are either toggles or adding and removing between two classes, but not 3 or more.
Thanks
This is what I have tried so far and the add part works as expected but when I click a different button it does not remove the previous clicked one
The HTML
<button id="btn-1" data-width="w-1/3">Mobile</button>
<button id="btn-2" data-width="w-2/3">Tablet</button>
<button id="btn-3" data-width="w-full">Desktop</button>
<div class="frame">
Some Content
</div>
The Javascript
let setMobile = document.querySelector('#btn-1');
let setTablet = document.querySelector('#btn-2');
let setDesktop = document.querySelector('#btn-3');
let btns = [setMobile, setTablet, setDesktop];
function getBtnId(btn) {
btn.addEventListener('click', function() {
let frame = document.querySelector('.frame')
frame.classList.add(this.dataset.width)
if(frame.classList.contains(btns)){
frame.classList.remove(this.dataset.width)
}
console.log(this.dataset.width);
});
}
btns.forEach(getBtnId);
Basically, what I am trying to do is a responsive frame which will adjust its width depending on what is clicked.
You can store the current class in a variable and use the remove() to remove the previous class on each click.
let setMobile = document.querySelector('#btn-1');
let setTablet = document.querySelector('#btn-2');
let setDesktop = document.querySelector('#btn-3');
let btns = [setMobile, setTablet, setDesktop];
var currentClass;
function getBtnId(btn) {
btn.addEventListener('click', function() {
let frame = document.querySelector('.frame')
if (currentClass) {
frame.classList.remove(currentClass);
}
currentClass = this.dataset.width;
frame.classList.add(currentClass);
console.log(this.dataset.width);
});
}
btns.forEach(getBtnId);
<button id="btn-1" data-width="w-1/3">Mobile</button>
<button id="btn-2" data-width="w-2/3">Tablet</button>
<button id="btn-3" data-width="w-full">Desktop</button>
<div class="frame">
Some Content
</div>
Here's a generalized version to work with multiple elements. I've wrapped each frame and buttons in a section element. Then I've bound the event listeners to the sections and used event bubbling / event delegation to perform the switch. I've also used a data attribute on the target frame to hold the current state.
function setWidthClass(event) {
var newWidth = event.target.dataset.width;
//This identifies a button click with our dataset
if (newWidth) {
//get the target div
var target = this.querySelector(".frame");
//if the target has a class set remove it
if (target.dataset.width) {
target.classList.remove(target.dataset.width);
}
//Add the new class
target.classList.add(newWidth);
//Update the data on the target element
target.dataset.width = newWidth;
}
}
//Add the event listener
var sections = document.querySelectorAll(".varyWidth");
for (var i = 0; i < sections.length; i++) {
sections[i].addEventListener("click", setWidthClass);
}
.w-third {
color: red;
}
.w-half {
color: blue;
}
.w-full {
color: green;
}
<section class="varyWidth">
<button data-width="w-third">Mobile</button>
<button data-width="w-half">Tablet</button>
<button data-width="w-full">Desktop</button>
<div class="frame">
Some Content
</div>
</section>
<section class="varyWidth">
<button data-width="w-third">Mobile</button>
<button data-width="w-half">Tablet</button>
<button data-width="w-full">Desktop</button>
<div class="frame">
Some Content
</div>
</section>
<section class="varyWidth">
<button data-width="w-third">Mobile</button>
<button data-width="w-half">Tablet</button>
<button data-width="w-full">Desktop</button>
<div class="frame">
Some Content
</div>
</section>
Rather than track the current class, you can also just reset it:
let setMobile = document.querySelector('#btn-1');
let setTablet = document.querySelector('#btn-2');
let setDesktop = document.querySelector('#btn-3');
let btns = [setMobile, setTablet, setDesktop];
function getBtnId(btn) {
btn.addEventListener('click', function() {
let frame = document.querySelector('.frame')
// reset the classList
frame.classList = ["frame"];
frame.classList.add(this.dataset.width)
console.log(this.dataset.width);
});
}
btns.forEach(getBtnId);
<button id="btn-1" data-width="w-1/3">Mobile</button>
<button id="btn-2" data-width="w-2/3">Tablet</button>
<button id="btn-3" data-width="w-full">Desktop</button>
<div class="frame">
Some Content
</div>
Okay so I'm making a dropdown for my social media website, and I wanted to add a slider button:
<div class = \"dropdown\">
<img id = \"navPFP\" style = \"margin-top: 2px;margin-left:20px; margin-right: 20px;\" class = \"pfp\" width = \"30\" height = \"30\" src=\"$pfpNAV\" alt=\"$userNAV's pfp\">
<div id = \"dropdown\" class = \"dropdown-content\">
<div id = \"names\" style = \"border-bottom: thin solid #BDBDBD;\">
<h2>$fnNAV $lnNAV</h2>
<p style = \"color:grey;margin-top:-40px;\">#$userNAV</p>
</div>
<div id = \"settings\" style = \"border-bottom: thin solid #BDBDBD;\">
Accout Settings
</div>
<label class = \"switch\">
<input type = \"checkbox\">
<span class = \"slider round\"></span>
</label>
Log out #$userNAV
Reset password #$userNAV
</div>
Problem is: unless it is just the #dropdown div, not any children, when you click it the dropdown closes
I have the following JS code to close the dropdown when anything other than the dropdown content is clicked:
window.onclick = function(event) {
if (!event.target.matches('#dropdown') && !event.target.matches('#navPFP')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
Even if I add a && !event.target.matches('.switch') (or any of the other div ids) to the if statement, the dropdown still closes when the slider is clicked. How can I fix this so that the dropdown stays open?
Instead of matches(), use closest():
if (!event.target.closest('#dropdown')) {
// target is neither #dropdown or one of its descendants; close the dropdown
}
element.closest('#dropdown') starts at element and walks upward through the DOM looking for #dropdown. If closest() finds #dropdown, it returns it, and element must be a child of #dropdown. If not, it returns null, and element must be outside #dropdown.
Recently, I have the same problem with event.target.matches().
Apart from using event.target.closest(), you can set the pointer-events CSS property to none.
document.querySelector("#container").addEventListener("click", (event) => {
if (event.target.matches(".click-here")) {
console.log("You clicked inside 'click-here' div.");
const result = document.querySelector("#result");
result.innerText = "You clicked inside 'click-here' div.";
}
})
.no-click {
pointer-events: none;
}
<div id="container">
<div class="click-here">
<div class="no-click">
<div>div1</div>
<div>
<div>nested</div>
</div>
<p>text</p>
</div>
</div>
</div>
<div id="result">
</div>