javascript tabs without passing any parameters to function - javascript

Hello so I have tabs using javascript:
function openScreen(screen, button) {
let i;
const tabContent = document.getElementsByClassName("tab-content");
const tabButton = document.getElementsByClassName("tab-button");
for (i = 0; i < tabContent.length; i++) {
tabContent[i].style.display = "none";
}
document.getElementById(screen).style.display = "block";
for (i = 0; i < tabButton.length; i++) {
tabButton[i].classList.remove('active');
}
document.getElementById(button).classList.add('active');
}
<div class="tab-links">
<button id="login-button" class="active tab-button" onclick="openScreen('Login', 'login-button')">Login</button>
<button id="register-button" class="tab-button" onclick="openScreen('Register', 'register-button')">Register</button>
</div>
<div>
<div id="Login" class="tab-content">
<p>Login</p>
</div>
<div id="Register" class="tab-content" style="display:none">
<p>Register</p>
</div>
</div>
And this is working, but I want to do this without passing any parameters to function and do it without using onclick function. But I dont image how I should achieve this.

This is a perfect task for event delegation. You register one even listener for the click event on the element that wraps the buttons and the content. In that event listener, you can use e.target is the element from which the event originated. e.currentTarget is the one on which the listener was attached.
In the listener, you need to figure out if it was really the button. Which could be done by checking for a certain property (in this case for the data-target attribute)
function openScreen(tabContainer, button) {
// remove active class from tab-content and tab-button in the current tab container
tabContainer.querySelectorAll(".tab-content.active, .tab-button.active").forEach(elm => {
elm.classList.remove('active');
})
// add the active class to the button and the container set by the target
let screen = button.dataset.target;
document.getElementById(screen).classList.add('active');
button.classList.add('active');
}
// Add the delegate event listener to all tab-conainers
document.querySelectorAll(".tab-container").forEach(container => {
container.addEventListener('click', (e) => {
// check if the event happened on an element with the `data-target` attribute
if (e.target.dataset.target) {
const tabContainer = e.currentTarget
const button = e.target
openScreen(tabContainer, button)
}
})
})
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
<div class="tab-container">
<div class="tab-links">
<button id="login-button" data-target="Login" class="tab-button active">Login</button>
<button id="register-button" data-target="Register" class="tab-button">Register</button>
</div>
<div>
<div id="Login" class="tab-content active">
<p>Login</p>
</div>
<div id="Register" class="tab-content">
<p>Register</p>
</div>
</div>
</div>
<hr>
<div class="tab-container">
<div class="tab-links">
<button id="login-button2" data-target="Login2" class="tab-button active">Login2</button>
<button id="register-button2" data-target="Register2" class="tab-button">Register2</button>
</div>
<div>
<div id="Login2" class="tab-content active">
<p>Login2</p>
</div>
<div id="Register2" class="tab-content">
<p>Register2</p>
</div>
</div>
</div>

You can do it with data attributes https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes
short example
<div class="tab" data-type="login"></div>
<div class="tab" data-type="register"></div>
Then
const tabs = document.querySelectorAll('.tab');
for (let tab of tabs) {
tab.onclick = function () {
const type = this.dataset.type;
/*now you can do all needed actions*/
}
}

Do you mean something like this?
function openScreen(e) {
const button = e.target;
const { screen } = button.dataset;
const tabs = document.querySelectorAll('.tab-content');
tabs.forEach(tab => tab.classList.toggle('active', screen === tab.id));
}
const buttons = document.querySelectorAll('button');
buttons.forEach(button => button.addEventListener('click', openScreen));
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
<div class="tab-links">
<button
id="login-button"
class="active tab-button"
data-screen="Login"
>Login</button>
<button
id="register-button"
class="tab-button"
data-screen="Register"
>Register</button>
</div>
<div>
<div id="Login" class="tab-content active">
<p>Login</p>
</div>
<div id="Register" class="tab-content">
<p>Register</p>
</div>
</div>

as your request. not passing any parameter. let me know at comment section if something doesn't work correctly.
$('#login-button').click(function(){
switchTab( $(this), $('#Login') );
});
$('#register-button').click(function(){
switchTab( $(this), $('#Register') );
});
function switchTab(this_, target_){
$('.tab-content').hide();
target_.show();
$('.tab-button').removeClass('active');
this_.addClass('active');
}
button.active{
border:1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="tab-links">
<button id="login-button" class="active tab-button">Login</button>
<button id="register-button" class="tab-button">Register</button>
</div>
<div>
<div id="Login" class="tab-content">
<p>Login</p>
</div>
<div id="Register" class="tab-content" style="display:none">
<p>Register</p>
</div>
</div>

Related

How to achieve level 3 div with javascript and apply styling

Hello I would like to reach a level 3 div and change the style of this div
in my example I would therefore like to be able to apply disply:none on style color red
to make the word Warning invisible
<div id="Zone">
<div class="MR-Widget ">
<div class="Title"> </div>
<div class="Errors" style="display: none"></div>
<div class="Content">
<div class="search"> </div>
<div class="resultat" style="width: 120px;"></div>
<div class="MR" id="Lock" style="display: none;"> </div>
<div style="color: red"> Warning </div>
</div>
</div>
</div>
To select 3rd level div:
document.querySelector('#Zone > div > div > div')
Now the problem is you have 4 div at 3rd level. So needed to select all and check style color. That gives:
const warningNone = () => {
Array.from(document.querySelectorAll('#Zone > div > div > div')).forEach(el => {
if (el) {
if (el.style.color === 'red') {
el.style.display = 'none';
}
}
})
}
window.addEventListener('load', warningNone);
<div id="Zone">
<div class="MR-Widget ">
<div class="Title"> </div>
<div class="Errors" style="display: none"></div>
<div class="Content">
<div class="search"> </div>
<div class="resultat" style="width: 120px;"></div>
<div class="MR" id="Lock" style="display: none;"> </div>
<div style="color: red"> Warning </div>
</div>
</div>
</div>
I modified the snippet to check the >div>div>div existence
By the way, I put the function to be fired when document loaded, otherwise your red will not apply
3...
try to split the query line in 2:
const warningNone = () => {
const els = document.querySelectorAll('#Zone > div > div > div');
els.forEach(el => {
if (el.style.color === 'red') {
el.style.display = 'none';
}
})
}
window.addEventListener('load', warningNone);
now in dev tools check which line fire the error

Change the Page layout when a button is clicked

I want to change the layout of a page that has 3 columns:
<div>
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
... to 4 columns when a button is clicked:
<div>
<div class="container">
<div class="row" >
<div class="col-md-3"></div>
<div class="col-md-3"></div>
<div class="col-md-3"></div>
<div class="col-md-3"></div>
</div>
</div>
</div>
I have no clue on how to do this.
There are many ways you can add another div. Here is my approach :
function appendDiv(){
let row = document.getElementsByClassName('row');
// change className for all the col-md-4 div
document.querySelectorAll('.col-md-4').forEach(function(item) {
item.className = 'col-md-3';
})
//create new div;
let col = document.createElement('div');
// add classname to div
col.className = "col-md-3"
row[0].appendChild(col)
}
.col-md-4{
border : 1px solid blue;
height : 20px;
}
.col-md-3{
border : 1px solid green;
height : 20px;
}
<div>
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
<button onClick='appendDiv()'>click</button>
</div>
</div>
There's a few ways this could be done depending on your data, however, here's one angle.
If you have both your 4 column & 3 column versions of the data loaded on the page (but one hidden with css). You could run something like this.
HTML
<div id="colsThree" class="displayArea show">
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
<div id="colsFour" class="displayArea">
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
<button id="changeColumns">Click Me To Change Columns</button>
Javascript
const buttonEl = document.querySelector("#changeColumns");
buttonEl.addEventListener('click', () => {
const outputEls = document.querySelectorAll('.displayArea')
outputEls.forEach((outputEl) => {
outputEl.toggle("show")
})
});
CSS
.displayArea {
display: none;
}
.displayArea.show {
display: block;
}
Use forEach and appendChild method.
const btn = document.querySelector('#btn')
btn.onclick = function() {
const targetClasses = document.querySelectorAll('.col-md-4')
targetClasses.forEach((tag, idx) => {
tag.className = 'col-md-3'
const lastIdx = targetClasses.length - 1
if (idx === lastIdx) {
const tag = document.createElement('div')
, row = document.querySelector('.row')
tag.className = 'col-md-3'
tag.innerText = '4'
row.appendChild(tag)
}
})
console.log(targetClasses)
return
}
<div>
<button id="btn">Click me</button>
<div class="container">
<div class="row" >
<div class="col-md-4">1</div>
<div class="col-md-4">2</div>
<div class="col-md-4">3</div>
</div>
</div>
</div>
If you're only using vanilla HTML, CSS, and JavaScript, then one of the ways to achieve this is by adding a click listener to the button beforehand. FYI: for brevity's sake, I'll call the div element with row class as parent. When user clicks the button, then it should
remove col-md-4 class and add col-md-3 class to all the children elements of parent.
add a new div element with col-md-3 class into parent.
Here's a link to the codepen for your reference.
const button = document.querySelector('button');
const rowDiv = document.querySelector('.row');
button.addEventListener('click', (e) => {
Array.from(rowDiv.children).forEach(childDiv => {
childDiv.classList.remove('col-md-4');
childDiv.classList.add('col-md-3');
});
const newDiv = document.createElement('div');
newDiv.classList.add('col-md-3');
rowDiv.appendChild(newDiv);
// I disabled the button to prevent the user
// from clicking it the second time.
e.target.disabled = true;
});
.button-parent {
margin: 15px 0;
}
.row {
height: 100vh;
}
.row > div:nth-child(1) {
background: red;
}
.row > div:nth-child(2) {
background: blue;
}
.row > div:nth-child(3) {
background: yellow;
}
.row > div:nth-child(4) {
background: green;
}
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
<div>
<div class="container">
<div class="button-parent">
<button class="btn btn-primary">Add div</button>
</div>
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
</body>

why is my console returning id is not defined

I am trying to make simple tabs program in js, i am new to js and dont have much knowlage about it,so be understandable. I set my code up using this tutorial https://wesbos.com/javascript/06-serious-practice-exercises/tabs. And this is my code
HTML
<body>
<div class="wrapper">
<div class="tabs">
<div role="tablist" aria-label="Programming Languages">
<button role="tab" aria-selected="false" id="js">
JavaScript
</button>
<button role="tab" aria-selected="false" id="ruby">Ruby
</button>
<button role="tab" aria-selected="false" id="php">
PHP
</button>
</div>
<div role="tabpanel" aria-labelledby="js" hidden>
<p>JavaScript is great!</p>
</div>
<div role="tabpanel" aria-labelledby="ruby" hidden>
<p>Ruby is great</p>
</div>
<div role="tabpanel" aria-labelledby="php" hidden>
<p>PHP is great!</p>
</div>
</div>
</div>
<script src="./tabs.js"></script>
</body>
CSS
button[aria-selected="true"] {
background: orange;
box-shadow: none;
}
JS
const tabs = document.querySelector('.tabs');
const tabButtons = tabs.querySelectorAll('[role="tab"]');
const tabPanels = tabs.querySelectorAll('[role="tabpanel"]');
function handleTabClick(event) {
console.log(event);
}
tabButtons.forEach(button => button.addEventListener('click', handleTabClick));
function handleTabClick(event) {
// hide all tab panels
tabPanels.forEach(panel => {
panel.hidden = true;
});
// mark all tabs as unselected
tabButtons.forEach(tab => {
// tab.ariaSelected = false;
tab.setAttribute('aria-selected', false);
});
// mark the clicked tab as selected
event.currentTarget.setAttribute('aria-selected', true);
// find the associated tabPanel and show it!
const { id } = event.currentTarget;
}
const tabPanel = tabs.querySelector(`[aria-labelledby="${id}"]`);
console.log(tabPanel);
tabPanel.hidden = false;

How to select the content from the element that I clicked on (using data attributes)

I am working with HTML and JS and I am trying to add a click event listener so that the index = 0 value updates automatically on click.
Depending on which .tab element I click, the content inside of the div.tab should change to display either Hello1, Hello2 or Hello3.
The first tab should display the tab content hello1 accordingly.
I have provided both with data attributes in the HTML. I want the corresponding div.tab content to be output if both have the same data attribute with vanilla JS.
JS
const tabbarContent = document.querySelectorAll(".tab-content");
const tabbarButtons = document.querySelectorAll(".tab");
const index = 0;
tabbarContent[index].classList.add('active');
tabbarButtons[index].classList.add('active');
const dataAttribute1 = tabbarButtons[index].getAttribute("data-tab");
const dataAttribute2 = tabbarContent[index].getAttribute("data-tab");
function clickTab() {
for (let index = 0; index.length; index++) {
if (dataAttribute1[index] === dataAttribute2[index]) {
dataAttribute2.classList.add("active");
}
}
}
tabbarButtons[index].addEventListener("click", clickTab);
HTML
<div class="container">
<div class="inner-container">
<div class="tab tab1" data-tab="1">
1
</div>
<div class="tab tab2" data-tab="2">
2
</div>
<div class="tab tab3" data-tab="3">
3
</div>
</div>
<div class="inner-container">
<div class="tab-content content1" data-tab="1">
<h1>Hello1</h1>
</div>
<div class="tab-content content2" data-tab="2">
<h1>Hello2</h1>
</div>
<div class="tab-content content3" data-tab="3">
<h1>Hello3</h1>
</div>
</div>
</div>
Thanks
Jessy
If I understand correctly what you want to achieve, you could do something like:
const tabbarButtons = document.querySelectorAll(".tab");
const tabbarContents = document.querySelectorAll(".tab-content");
tabbarButtons.forEach((tabBtn) => {
tabBtn.addEventListener('click', () => {
tabbarContents.forEach(tabCnt => {
if (tabCnt.dataset.tab === tabBtn.dataset.tab) {
tabCnt.classList.add('active');
} else {
tabCnt.classList.remove('active');
}
})
});
})
.tab {
display: inline-block;
padding: 5px;
border: 1px solid #ccc;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
<div class="container">
<div class="inner-container">
<div class="tab tab1" data-tab="1">
1
</div>
<div class="tab tab2" data-tab="2">
2
</div>
<div class="tab tab3" data-tab="3">
3
</div>
</div>
<div class="inner-container">
<div class="tab-content content1" data-tab="1">
<h1>Hello1</h1>
</div>
<div class="tab-content content2" data-tab="2">
<h1>Hello2</h1>
</div>
<div class="tab-content content3" data-tab="3">
<h1>Hello3</h1>
</div>
</div>
</div>

Get next element with class (that's not a child or sibling)

With the press of a button, I want to toggle the class .active on the next div.bottom. These are basically accordions, but with a different structure.
Using nextElementSibling I guess won't work here to select the target element. How would one select such an element, that's neither a child nor a sibling (in plain JS)?
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button></button></div>
</div>
</div>
<div class="bottom"></div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button></button></div>
</div>
</div>
<div class="bottom"></div>
</div>
I'd do it by using closest to go up to the container .wrapper element, then querySelector to find the bottom element:
function onClick(event) {
const wrapper = event.target.closest(".wrapper");
const bottom = wrapper && wrapper.querySelector(".bottom");
if (bottom) {
bottom.classList.toggle("active");
}
}
Live Example:
// I've added event delegation here
document.body.addEventListener("click", function onClick(event) {
const button = event.target.closest(".inner button");
const wrapper = button && button.closest(".wrapper");
const bottom = wrapper && wrapper.querySelector(".bottom");
if (bottom) {
bottom.classList.toggle("active");
}
});
.active {
color: blue;
border: 1px solid black;
}
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button A</button></div>
</div>
</div>
<div class="bottom">Bottom A</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button B</button></div>
</div>
</div>
<div class="bottom">Bottom B</div>
</div>
Or the same thing using optional chaining (relatively new):
function onClick(event) {
const wrapper = event.target.closest(".wrapper");
const bottom = wrapper?.querySelector(".bottom");
bottom?.classList.toggle("active");
}
Live Example:
// I've added event delegation here
document.body.addEventListener("click", function onClick(event) {
const button = event.target.closest(".inner button");
const wrapper = button?.closest(".wrapper");
const bottom = wrapper?.querySelector(".bottom");
bottom?.classList.toggle("active");
});
.active {
color: blue;
border: 1px solid black;
}
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button A</button></div>
</div>
</div>
<div class="bottom">Bottom A</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button B</button></div>
</div>
</div>
<div class="bottom">Bottom B</div>
</div>
By using closest() you can traverse the DOM upwards. With this it's easy to just get the relevant .bottom and toggle the active class on this element.
document.querySelectorAll('button').forEach(button => {
button.addEventListener('click', (e) => {
e.currentTarget.closest('.wrapper').querySelector('.bottom').classList.toggle('active');
});
});
.bottom {
display: none
}
.bottom.active {
display: block
}
<div class="wrapper">
<div class="top">
<div class="inner">
<button type="button">Toggle</button>
</div>
</div>
<div class="bottom">Hidden content</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<button type="button">Toggle 2</button>
</div>
</div>
<div class="bottom">Hidden content 2</div>
</div>

Categories

Resources