Can anyone advice me why the allTabs forEach wont work when I am trying to add the .none class
?
When clicking second item the other class should disappear but it doesn't,
let allTabs = document.querySelectorAll('.none');
function setActiveTab(tab) {
allTabs.forEach((tabs) => {
tabs.classList.add("none")
})
let singleTab = document.querySelector(`.${tab}`)
singleTab.classList.toggle("block")
}
.none {
display: none;
}
.block {
display: block;
}
<div>
<button onclick="setActiveTab('first')">
Test1
</button>
<button onclick="setActiveTab('second')">
Test2
</button>
</div>
<div class="none first">
first tab
</div>
<div class="none second">
second tab
</div>
A better approach is just to remove the none class on the current tab. You won't need the block class.
let allTabs = document.querySelectorAll('.none');
function setActiveTab(tab) {
allTabs.forEach((tabs) => {
tabs.classList.add("none")
})
let singleTab = document.querySelector(`.${tab}`)
singleTab.classList.remove("none")
}
.none {
display: none;
}
<div>
<button onclick="setActiveTab('first')">
Test1
</button>
<button onclick="setActiveTab('second')">
Test2
</button>
</div>
<div class="none first">
first tab
</div>
<div class="none second">
second tab
</div>
if you look at the inspector, the class none is always present.
Related
Using this example:
$('[data-switch]').on('click', function(e)
{
var
$page = $('#page-2')
, blockToShow = e.currentTarget.getAttribute('data-switch')
;
// Hide all children.
$page.children().hide();
// And show the requested component.
$page.children(blockToShow).show();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button data-switch="#about_me">Click to read about me</button>
<button data-switch="#education">Click to show my education</button>
<button data-switch="#about_name_bravitus">Click to read about the name Bravitus</button>
<div id="page-2">
<div id="about_me" class="container">
<h1>This is about me section</h1>
<div>about me about me about me</div>
</div>
<!-- Hidden blocks that you show when requested. -->
<div id="education" class="container" style="display: none;">
<h1>This is about my education</h1>
<div>education education education</div>
</div>
<div id="about_name_bravitus" class="container" style="display: none;">
<h1>This is about the name bravitus</h1>
<div>bravitus bravitus bravitus</div>
</div>
</div>
How would one go about changing the font color and weight of the button text when the content is being displayed (not based on when the button is clicked)?
I have tried to find posts with a similar request, but I am greatly struggling. Any direction would be appreciated.
There is many ways to do that, like this one :
const
btns_Switch = document.querySelectorAll('[data-switch]')
, page2_parent = document.querySelector('#page-2')
;
btns_Switch.forEach( btn =>
{
btn.onclick = e =>
{
page2_parent.dataset.switch = btn.dataset.switch;
setButtonActive();
}
})
setButtonActive(); // init on page load...
function setButtonActive()
{
let activBtn = page2_parent.dataset.switch;
btns_Switch.forEach( btn =>
btn.classList.toggle('active', activBtn === btn.dataset.switch ))
}
#page-2[data-switch="about_me"] > div:not(#about_me) ,
#page-2[data-switch="education"] > div:not(#education) ,
#page-2[data-switch="about_name_bravitus"] > div:not(#about_name_bravitus)
{
display : none;
}
button.active
{
background : yellow;
font-weight : bold;
}
<button data-switch="about_me">Click to read about me</button>
<button data-switch="education">Click to show my education</button>
<button data-switch="about_name_bravitus">Click to read about the name Bravitus</button>
<div id="page-2" data-switch="education" > <!-- set initial ID value -->
<div id="about_me" class="container">
<h1>This is about me section</h1>
<div>about me about me about me</div>
</div>
<div id="education" class="container" >
<h1>This is about my education</h1>
<div>education education education</div>
</div>
<div id="about_name_bravitus" class="container" >
<h1>This is about the name bravitus</h1>
<div>bravitus bravitus bravitus</div>
</div>
</div>
Sorry I don't code much and have adapted this code, so help would be greatly appreciated.
I'm trying to emulate a shopping page where you can 'like' a product and shows number of 'likes' for each product.
What is happening:
When I click on different instances of the 'like' button they get saved as one instance on firebase and all the 'like' counters show the same number of 'likes'
What I want:
Every time I click a different instance of the 'like' button I want it saved as a different instance on firebase so the counts are different for each 'like' button.
var dCounters = document.querySelectorAll('.CountLike');
[].forEach.call(dCounters, function(dCounter) {
var el = dCounter.querySelector('button');
var cId = dCounter.id;
var dDatabase = firebase.database().ref('Like Number Counter').child(cId);
// get firebase data
dDatabase.on('value', function(snap) {
var data = snap.val() || 0;
dCounter.querySelector('span').innerHTML = data;
});
// set firebase data
el.addEventListener('click', function() {
dDatabase.transaction(function(dCount) {
return (dCount || 0) + 1;
});
});
});
.CountLike div {
display: inline-flex;
}
.item-like {
font-size: 18px;
display: inline-block;
margin-top: 10px;
}
.counterStat {
margin-right: 15px;
margin-top: 5px;
}
.heart {
width: 32px;
height: 32px;
}
.btn {
background: none;
border: none;
cursor: pointer;
}
<div>
<div class="store-action">
<div class="CountLike" id="Like Count">
<div class="likes">
<span class="counterStat">0</span>
<button class="btn"><img src="https://www.svgrepo.com/show/164008/heart.svg" class="heart" alt="the heart-like button"></button>
</div>
</div>
</div>
</div>
<div>
<div class="store-action">
<div class="CountLike" id="Like Count">
<div class="likes">
<span class="counterStat">0</span>
<button class="btn"><img src="https://www.svgrepo.com/show/164008/heart.svg" class="heart" alt="the heart-like button"></button>
</div>
</div>
</div>
</div>
Below snippet should do it for now. Both of your elements have the same id value set which is set as id="Like Count"
So right now you just end up writing and reading from the same field for every cell you have.
As it is also stated on this link you should always make sure the id values you assign are unique.
<div>
<div class="store-action">
<div class="CountLike" id="xyz">
<div class="likes">
<span class="counterStat">0</span>
<button class="btn"><img src="https://www.svgrepo.com/show/164008/heart.svg" class="heart" alt="the heart-like button"></button>
</div>
</div>
</div>
</div>
<div>
<div class="store-action">
<div class="CountLike" id="xyzt">
<div class="likes">
<span class="counterStat">0</span>
<button class="btn"><img src="https://www.svgrepo.com/show/164008/heart.svg" class="heart" alt="the heart-like button"></button>
</div>
</div>
</div>
</div>
Whenever I click on the first button, I want to hide rest of elements and display the title with description for first item, I want to do this for the rest items as well. Whenever I am trying to do this, I am not able to target all elements in my function. Can someone assist? Also should I add to the buttons something like data-* and based on that display/show the div?
class MyClass {
constructor() {
this.items = document.querySelectorAll('.test');
this.btn = document.querySelector('.click');
console.log(this.items); //logs elements
this.btn.addEventListener("click", this.testFunc);
}
testFunc() {
console.log(this.items); //undefined ?
}
}
new MyClass();
.test {
display: none;
}
<div class="item first">
<h3>Item-1</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<div class="item second">
<h3>Item-1</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<div class="item third">
<h3>Item-1</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<button class="click">
Test
</button>
<button class="click">
Test
</button>
<button class="click">
Test
</button>
The following update to your MyClass should do the trick.
The main change is that you attach an event listenter to all buttons (changed querySelector('.click') to querySelectorAll('.click'). And the helper function showItem takes an index which is which item to show (and hide all the rest).
I'm using the hidden attribute to show/hide elements. You could instead add or remove a class from the item that has the display: none.
class MyClass {
constructor() {
this.items = document.querySelectorAll(".test");
this.btns = document.querySelectorAll(".click");
this.items.forEach((item, index) => {
item.setAttribute("hidden", true);
this.btns
.item(index)
.addEventListener("click", (ev) => this.showItem(index));
});
}
showItem(idx) {
this.items.forEach((item, index) => {
if (index === idx) {
item.removeAttribute("hidden");
} else {
item.setAttribute("hidden", true);
}
});
}
}
new MyClass();
<div class="item first">
<h3>Item-1</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<div class="item second">
<h3>Item-2</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<div class="item third">
<h3>Item-3</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<button class="click">
Test 1
</button>
<button class="click">
Test 2
</button>
<button class="click">
Test 3
</button>
I'm trying to create a function that reveals additional content by clicking a button.
I've got a script that displays the content by default - How do I switch it so the content is hidden by default and only displays when the button is clicked?
function revealContent() {
var x = document.getElementById("additionalContent");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
<div class="col-10 text-center">
<button class="btn btn-sm btn-primary" onclick="revealContent()">Show More Options...</button>
</div>
<div class="col-10" id="additionalContent">
<p>Content!</p>
</div>
You just have to have the style display: none applied by default and then toggle it. classList.toggle is a good use for this. You can do something like:
.hidden {
display: none;
}
<div class="col-10 text-center">
<button class="btn btn-sm btn-primary" onclick="revealContent()">Show More Options...</button>
</div>
<div class="col-10 hidden" id="additionalContent">
<p>Content!</p>
</div>
<script>
function revealContent() {
var x = document.getElementById("additionalContent");
x.classList.toggle('hidden');
}
</script>
<div class="col-10" id="additionalContent" style="display:none">
<p>Content!</p>
</div>
EDIT: You've to add the style="display:none" to make this as default when the page is loaded.
I'm trying to create a chain of buttons:
First options;
- Button 1
- Button 2
IF chosen Button 1:
- Button 1a
- Button 1b
IF chosen Button 1a:
- Button 1aa
- Button 1ab
IF chosen Button 1b:
- Button 1ba
- Button 1bb
And so on.. same goes for Button 2.
Thus far I got this but my .js is not working out for me.
I tried it in two ways.
WAY 1:
HTML (onclick="nextPush" is going to change in way 2)
<div class="buttons1-2">
<button id="btn1" class="btn btn1" onclick="buttonPushed(this)">Button 1</button>
<button id="btn2" class="btn btn2" onclick="buttonPushed(this)">Button 2</button>
</div>
<div class="buttons1a-b">
<button id="btn1a" class="btn btn1a" onclick="nextPush(this)">Button 1a</button>
<button id="btn1b" class="btn btn1b" onclick="nextPush(this)">Button 1b</button>
</div>
<div class="buttons2a-b">
<button id="btn2a" class="btn btn2a">Button 2a</button>
<button id="btn2b" class="btn btn2b">Button 2b</button>
</div>
<div class="buttons1aa-ab">
<button id="btn1aa" class="btn btn1a">Button 1aa</button>
<button id="btn1ab" class="btn btn1b">Button 1ab</button>
</div>
<div class="buttons1ba-bb">
<button id="btn1ba" class="btn btn2a">Button 1ba</button>
<button id="btn1bb" class="btn btn2b">Button 1bb</button>
</div>
WAY 1: .JS
function buttonPushed(btn) {
var replacewith = "buttons1a-b";
if (btn.id == "btn2") {
replacewith = "buttons2a-b";
}
function nextPush(btn) {
var replacewith = "buttons1aa-ab";
if (btn.id == "btn1b") {
replacewith = "buttons1ba-bb";
}
var allChildren = document.getElementsByClassName('buttons')[0].children;
for (var i = 0; i < allChildren.length; i++) {
var child = allChildren[i];
if (child.className != replacewith) {
child.style.display = "none";
} else {
child.style.display = "inline";
}
}
}
WAY 2: HTML (notice the onclick="nextPush" is gone)
<div class="buttons1-2">
<button id="btn1" class="btn btn1" onclick="buttonPushed(this)">Button 1</button>
<button id="btn2" class="btn btn2" onclick="buttonPushed(this)">Button 2</button>
</div>
<div class="buttons1a-b">
<button id="btn1a" class="btn btn1a" onclick="buttonPushed(this)">Button 1a</button>
<button id="btn1b" class="btn btn1b" onclick="buttonPushed(this)">Button 1b</button>
</div>
<div class="buttons2a-b">
<button id="btn2a" class="btn btn2a">Button 2a</button>
<button id="btn2b" class="btn btn2b">Button 2b</button>
</div>
<div class="buttons1aa-ab">
<button id="btn1aa" class="btn btn1a">Button 1aa</button>
<button id="btn1ab" class="btn btn1b">Button 1ab</button>
</div>
<div class="buttons1ba-bb">
<button id="btn1ba" class="btn btn2a">Button 1ba</button>
<button id="btn1bb" class="btn btn2b">Button 1bb</button>
</div>
WAY 2 .JS
function buttonPushed(btn) {
/* btn = Id: btn1, btn2, btn1a or btn1b */
let replacewith = "buttons1a-b";
if (btn.id == "btn2") {
replacewith = "buttons2a-b";
}
else if (btn.id == "btn1a") {
replacewith = "buttons1aa-ab";
}
else if (btn.id == "btn1b") {
replacewith = "buttons1ba-bb";
}
}
let allChildren = document.getElementsByClassName('buttons')[0].children;
for (let i = 0; i < allChildren.length; i++) {
let child = allChildren[i];
if (child.className != replacewith) {
child.style.display = "none";
} else {
child.style.display = "inline";
}
}
.CSS for BOTH WAYS:
.buttons1a-b {
display: none;
}
.buttons2a-b {
display: none;
}
.buttons1aa-ab {
display: none;
}
.buttons1ba-bb {
display: none;
}
Sorry for the long post, hope you can help me out :) If you know a better way to do this, please also do let me know.
Building on your example, and the one from Michael, you could also use another approach of declaring what div you want displayed by attaching an attribute to the button, and then add an event listener to all buttons with that attribute. This makes the HTML slightly smaller and more declarative, and makes it easier to switch what element you want to display next instead of relying on a particular schema of id's.
(function(document) {
// get all buttons that have the attribute data-next
const buttons = document.querySelectorAll('[data-next]');
for (const item of buttons) {
// get references to the parent item and next item to hide/show
const parentId = item.getAttribute('data-parent');
const parent = document.querySelector(`#${parentId}`);
const nextDivId = item.getAttribute('data-next');
const nextDiv = document.querySelector(`#${nextDivId}`);
if (!nextDiv) {
console.error('could not find next div for button ', item);
}
// attach an event listener for click that toggles visibility of the above elements
item.addEventListener('click', function() {
nextDiv.classList.toggle('hidden');
parent.classList.toggle('hidden');
});
}
})(document);
.hidden {
display: none;
}
<div id="base">
<button data-next="option-a" data-parent="base">Option A</button>
<button data-next="option-b" data-parent="base">Option B</button>
</div>
<div id="option-a" class="hidden">
<p>Option A</p>
</div>
<div id="option-b" class="hidden">
<p>Option B</p>
</div>
If you want to add new buttons dynamically (or change what your next items should be) you will need to attach the event listener when you create your other buttons. For instance, you can do something like the following:
(function(document) {
function onButtonClicked(event) {
const item = event.target;
// get references to the next item to show
const nextDivId = item.getAttribute('data-next');
const nextDiv = document.querySelector(`#${nextDivId}`);
if (!nextDiv) {
console.error('could not find next div for button ', item);
}
// The function toggle on classList either removes a class if it exists
// or adds it if it does not exist in the list of classes on the element
nextDiv.classList.toggle('hidden');
// check if container has an attribute for loading next buttons lazily
const lazyLoadLevel = nextDiv.getAttribute('data-level');
// if we found the attribute, load the contents
if (lazyLoadLevel) {
// cast lazyLoadedLevel to an integer (with +) since getAttribute returns a string
loadLevel(+lazyLoadLevel, nextDiv);
// since we have populated the container we can remove the attribute so that elements do not get added again
nextDiv.removeAttribute('data-level');
}
// get references to the parent item to hide
const parentId = item.getAttribute('data-parent');
const parent = document.querySelector(`#${parentId}`);
if (parent) {
parent.classList.toggle('hidden');
}
}
function addButton(parent, nextElementId, text) {
const newItem = document.createElement('button');
newItem.setAttribute('data-next', nextElementId);
newItem.setAttribute('data-parent', parent.getAttribute('id'));
newItem.textContent = text;
newItem.addEventListener('click', onButtonClicked);
parent.appendChild(newItem);
}
function loadLevel(level, container) {
switch (level) {
// depending on level you can define other buttons to add here
case 2:
{
addButton(container, 'option-a', 'Goto option a');
break;
}
}
}
// get all *existing* buttons that have the attribute data-next
// this is run once when the script loads, and will not attach listeners to dynamically created buttons
const buttons = document.querySelectorAll('[data-next]');
for (const item of buttons) {
// attach an event listener for click that toggles visibility of parent and next elements
// notice that we pass a reference to onButtonClicked. Even though it is a function we shouldn't call it *here*
item.addEventListener('click', onButtonClicked);
}
})(document);
.hidden {
display: none;
}
<div id="base">
<button data-next="option-a" data-parent="base">Option A</button>
<button data-next="option-b" data-parent="base">Option B</button>
</div>
<div id="option-a" class="hidden">
<p>Option A</p>
<button data-next="option-b" data-parent="option-a">Option B</button>
</div>
<div id="option-b" class="hidden" data-level="2">
<p>Option B. The contents of this div is loaded lazily based on the value of the attribute data-level</p>
</div>
At first, I was thinking this should be done entirely dynamically -- where the next container of buttons is created and inserted into the DOM when the button is clicked. But judging by your current attempts, it seems like you want to have all the buttons hardcoded into the source, hidden with CSS, and shown with DOM during the click event. Here is one way you can achieve that:
function handleButtonClick(button) {
const clickedID = button.id.substring(3);
const nextDiv = document.getElementById("buttons" + clickedID);
if (nextDiv) {
nextDiv.style.display = "block";
}
}
.hidden {
display: none;
}
<div id="buttons">
<button id="btn1" onclick="handleButtonClick(this)">Button 1</button>
<button id="btn2" onclick="handleButtonClick(this)">Button 2</button>
</div>
<div id="buttons1" class="hidden">
<button id="btn1a" onclick="handleButtonClick(this)">Button 1a</button>
<button id="btn1b" onclick="handleButtonClick(this)">Button 1b</button>
</div>
<div id="buttons2" class="hidden">
<button id="btn2a" onclick="handleButtonClick(this)">Button 2a</button>
<button id="btn2b" onclick="handleButtonClick(this)">Button 2b</button>
</div>
<div id="buttons1a" class="hidden">
<button id="btn1aa" onclick="handleButtonClick(this)">Button 1aa</button>
<button id="btn1ab" onclick="handleButtonClick(this)">Button 1ab</button>
</div>
<div id="buttons1b" class="hidden">
<button id="btn1ba" onclick="handleButtonClick(this)">Button 1ba</button>
<button id="btn1bb" onclick="handleButtonClick(this)">Button 1bb</button>
</div>
<div id="buttons2a" class="hidden">
<button id="btn2aa" onclick="handleButtonClick(this)">Button 2aa</button>
<button id="btn2ab" onclick="handleButtonClick(this)">Button 2ab</button>
</div>
<div id="buttons2b" class="hidden">
<button id="btn2ba" onclick="handleButtonClick(this)">Button 2ba</button>
<button id="btn2bb" onclick="handleButtonClick(this)">Button 21bb</button>
</div>
This just identifies which button was clicked, and uses that information to determine the next div to show, until there are no more divs that correspond to the one that was clicked.