Problems with Tabs - javascript

I need help with tabs. When is selected first button background color is gray and text in first button is white that okay, but on my second button background color is gray-white and text is white. I want to add grey text color to that second button?
HTML:
<button class="tablink" id="defaultOpen" onclick="openPage('Home', this, '#6D6E70')">First Button</button>
<button class="tablink" onclick="openPage('News', this, '#6D6E70')">Second Button</button>
<div id="Home" class="tabcontent">
<h3>Home</h3>
<p>Home is where the heart is..</p>
</div>
<div id="News" class="tabcontent">
<h3>News</h3>
<p>Some news this fine day!</p>
</div>
CSS:
.tablink {
display: inline-block;
min-width: 128px;
font-size: 16px;
padding: 8px;
color: #fff;
font-weight: 500;
text-decoration: none;
background-color: #F2F2F2;
}
.tablink:hover {
background-color: #777;
}
/* Style the tab content (and add height:100% for full page content) */
.tabcontent {
color: white;
display: none;
padding: 100px 20px;
height: 100%;
}
Java Script:
<script>
function openPage(pageName,elmnt,color) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablink");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].style.backgroundColor = "";
}
document.getElementById(pageName).style.display = "block";
elmnt.style.backgroundColor = color;
}
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
</script>
I try add to java script code in document.getElementsByClassName("tablink")
tablinks[i].style.color = "#fff";
But not working

If you want to change second button text color after clicking on it, you can simply add this line:
if (pageName === 'News') {
elmnt.style.color = 'black';
}
Here's JSfiddle.
However you'd be better off using CSS to properly style those elements the way you want.
Let me know if you need further assistance with that.
EDIT:
Since it has been cleared out what you're trying to achieve, the best way to tackle your problem would be to do it by toggling classes. Here's JSfiddle.
Basically, I've added two extra classes .selected, and .visible, one for buttons, other for divs, like so:
HTML:
<button class="tablink selected" onclick="openPage('Home', this)">First Button</button>
/* ... */
<div id="Home" class="tabcontent visible">
<h3>Home</h3>
<p>Home is where the heart is..</p>
</div>
CSS:
.tablink.selected {
color: #fff;
background-color: #6D6E70;
}
.tabcontent.visible {
display: block;
}
And shortened your JS code:
function openPage(pageName, elmnt) {
document.querySelector('.tablink.selected').classList.remove('selected');
elmnt.classList.add('selected');
document.querySelector('.tabcontent.visible').classList.remove('visible');
document.getElementById(pageName).classList.add('visible');
}
So whenever you click, it looks for a selected/visible element, removes those extra classes, and adds them back to the button that has been pressed and div that corresponds to it.

Related

How to hide navigation bar on link click?

I have 3 buttons and a responsive hamburger menu. Everything works as expected, but I can't think of a way to make a navigation bar go away as soon as I click on a button.
The program is supposed to work like this: clicking hamburger menu activates 3 buttons, whenever user clicks on any of those 3 buttons it hides the buttons and only leaves the button that was clicked.
This is the wanted outcome:
This is my code so far.
html:
<div class="selectSection">
<button type="button" data-number="1" class="active">1</button>
<button type="button" data-number="2">2</button>
<button type="button" data-number="3">3</button>
</div>
<div class="hamburger">
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
</div>
<div>
<div class="content" data-number="1">
<p>1st page</p>
</div>
<div class="content" data-number="2">
<p>2nd page</p>
</div>
<div class="content" data-number="3">
<p>3rd page</p>
</div>
</div>
css
.content:not(:first-child) {
display: none;
}
.active {
color: orange !important;
}
.hamburger {
display: none;
}
#media all and (max-width: 800px) {
.hamburger {
display: inline-block;
cursor: pointer;
z-index: 7;
}
.hamburger .line {
width: 30px;
height: 3px;
background: black;
margin: 6px 0px;
}
.selectSection {
display: none;
overflow: hidden;
}
.selectSection.active {
display: block;
}
}
js
// change active class, show the clicked element only and hide the others
// grab all the buttons
let Buttons = document.querySelectorAll(".selectSection button");
// loop through the buttons using for..of
for (let button of Buttons) {
// listen for a click event
button.addEventListener("click", (e) => {
// et = event target
const et = e.target;
// slect active class
const active = document.querySelector(".active");
// check for the button that has active class and remove it
if (active) {
active.classList.remove("active");
}
// add active class to the clicked element
et.classList.add("active");
// select all classes with the name content
let allContent = document.querySelectorAll(".content");
// loop through all content classes
for (let content of allContent) {
// display the content if the class has the same data-attribute as the button
if (
content.getAttribute("data-number") ===
button.getAttribute("data-number")
) {
content.style.display = "block";
}
// if it's not equal then hide it.
else {
content.style.display = "none";
}
}
});
}
hamburger = document.querySelector(".hamburger");
hamburger.onclick = function () {
navBar = document.querySelector(".selectSection");
navBar.classList.toggle("activate");
};
This is the demo:
https://codepen.io/f4kermak3r/pen/ExRPKzJ
you are using the wrong css class in your js file. At line 44, you must change navBar.classList.toggle("activate") to navBar.classList.toggle("active"). That should work.

Nested element in button element are not clickable

I am having this problem, I created a button, and inside I have a for Icon, and inside the element I have span to style the text next to the Icon (the Icon from is humberger from awesome font)
the issue is:
in javascript, I created an onclick function for the button element using the ID btnm, but when I click on the text or the icon in the button does work though when I click around the text and the icon in the button the onclick works fine.
I cannot understand why the icon and text are in the button.
please help
document.addEventListener('DOMContentLoaded', function(){
var menubtn = document.getElementById('btnm');
var mobilemenu = document.getElementById('navigation-mobile');
// When the user clicks on the button, open the modal
menubtn.onclick = function() {
if (mobilemenu.style.display == 'block') {
mobilemenu.style.display = "none";
}
else {
mobilemenu.style.display = 'block';
}
}
}
.mobile-menu-btn {
float: right;
display: block;
padding: 3px 3px 0px 0px;
}
.humberger {
background-color: $identity-color;
font-size: 20px;
border: $identity-color;
border: none;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
.menu-pargraph {
font-size: 14px;
vertical-align: middle;
padding-left: 5px;
margin: 0;
width: 100%;
}
<div class ="mobile-menu-btn">
<button class="humberger" id="btnm">
<i class="menu-btn fas fa-bars">
<span class="menu-pargraph">Menu</span>
</i>
</button>
</div>
<div id="navigation-mobile">
<ul>
<li>home</li>
<li>video</li>
<li>contact</li>
</ul>
</div>
Explanations in comments below. You had the id (and click listener) on the div, not the button and your 'Menu' text was probably looking funky b/c it was inside the icon element, inheriting the icon font family.
<div class ="mobile-menu-btn">
<button class="humberger" id="btnm"> <!-- put the id here -->
<i class="menu-btn fas fa-bars"></i>
<span class="menu-pargraph">Menu</span> <!-- move outside of the fontawesome icon -->
</button>
</div>
Also you can make your life easier with the show/hide using a class
css:
#navigation-mobile{
display:none;
/* and whatever other styles you have here */
}
.show {
display:block;
}
then in your script:
menubtn.onclick = function() {
mobilemenu.classList.toggle('show');
}
You missed a ")" in your JS code.
document.addEventListener('DOMContentLoaded', function(){
var menubtn = document.getElementById('btnm');
var mobilemenu = document.getElementById('navigation-mobile');
// When the user clicks on the button, open the modal
console.log("ok")
}
) // Here you have to add parenthesis

Tab content not showing properly with CSS Grid areas and JS

I want to set up a JS based tab navigation but somehow the Tab content doesnt show properly.
The JS code seems off but I cannot find the error.
I just want the Tab Content to show that belongs to one tab.
The Code looks like this:
function openPage(e, pageName) {
// Declare all variables
let i, content, tabLinks;
// Get all elements with class="tabs_content" and hide them
content = document.getElementsByClassName("tabs_content");
for (i = 0; i < content.length; i++) {
content[i].style.display = "none";
}
// Get all elements with class="tabs_button" and remove the class "active"
tabLinks = document.getElementsByClassName("tabs_button");
for (i = 0; i < tabLinks.length; i++) {
tabLinks[i].className = tabLinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the link that opened the tab
document.getElementById(pageName).style.display = "block";
e.currentTarget.className += " active";
}
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
.wrapper{
display: grid;
grid-template-columns: 150px 1fr;
border:1px solid #cccccc;
padding: 15px;
margin: 0 auto;
grid-template-areas:
'nav main '
'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
flex-shrink: 0;
min-height: 400px;
}
.tabs_content {
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
}
.tabs_button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_button:active {
background: #dddddd;
}
.tabs_button:not(:last-of-type){
border-bottom: 1px solid #cccccc;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button class="tabs_button" onclick="openPage(e, 'Home')" id="defaultOpen">Home</button>
<button class="tabs_button" onclick="openPage(e, 'News')">News</button>
<button class="tabs_button" onclick="openPage(e, 'Contact')">Contact</button>
</nav>
<section class="tabs_content tabs_content--active">
<div id="Home" class="tabs_content">
<h2>Tab #1</h2>
<p>Content Page 1 </p>
</div>
<div class="tabs_content">
<h2>Tab #2</h2>
<p>Content Page 2 </p>
</div>
<div class="tabs_content">
<h2>Tab #3</h2>
<p>Content Page 3</p>
</div>
</section>
</div>
I cannot find the error, but the problem seems to be the <section>. Any help is much appreciated.
Your code has more than one problem.
The one you're currently stuck on is that e is not defined in this template bit:
<button class="tabs_button" onclick="openPage(e, 'Home')" id="defaultOpen">Home</button>
The default name of the event when you want to pass it to the function is event, not e (and you can't change it, it's a convention - you can only change its name in the function). In other words, even if in the function it's named e, you still have to reference it as event in the template.
Here are the rest of the problems, in the order I encountered them as I worked my way into making it work:
the .tabs_button:active selector is wrong. You probably want to use .tabs_button.active (since you're applying the class active to it).
you have tabs_content class on the tabs wrapper, so the entire wrapper is hidden, when you probably only want to set display:none onto its children.
you're missing the ids on second and third tab
you need width: 100% on .tabs_content
See it here:
function openPage(e, pageName) {
let i, content, tabLinks;
content = document.getElementsByClassName("tabs_content");
for (i = 0; i < content.length; i++) {
content[i].style.display = "none";
}
tabLinks = document.getElementsByClassName("tabs_button");
for (i = 0; i < tabLinks.length; i++) {
tabLinks[i].className = tabLinks[i].className.replace(" active", "");
}
document.getElementById(pageName).style.display = "block";
e.currentTarget.className += " active";
}
document.getElementById("defaultOpen").click();
.wrapper {
display: grid;
grid-template-columns: 150px 1fr;
border: 1px solid #cccccc;
padding: 15px;
margin: 0 auto;
grid-template-areas: 'nav main ' 'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
flex-shrink: 0;
min-height: 400px;
}
.tabs_content {
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
width: 100%;
}
.tabs_button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_button.active {
background: #dddddd;
}
.tabs_button:not(:last-of-type) {
border-bottom: 1px solid #cccccc;
}
body {
margin: 0;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button class="tabs_button" onclick="openPage(event, 'Home')" id="defaultOpen">Home</button>
<button class="tabs_button" onclick="openPage(event, 'News')">News</button>
<button class="tabs_button" onclick="openPage(event, 'Contact')">Contact</button>
</nav>
<section class="tabs_content--active">
<div id="Home" class="tabs_content">
<h2>Tab #1</h2>
<p>Home</p>
</div>
<div id="News" class="tabs_content">
<h2>Tab #2</h2>
<p>News</p>
</div>
<div id="Contact" class="tabs_content">
<h2>Tab #3</h2>
<p>Contact</p>
</div>
</section>
</div>
Apart from that, in my estimation, you're doing too much by JavaScript. Instead of applying inline styles, you should only apply and remove classes which, in turn, apply styles. You end up writing less code and it's easier to manage & debug.
Another thing I would personally change is replace className usage with classList, which has a more powerful and more expressive syntax.
This doesn't mean it's not doable the way you set out to do it.
The main problem when mixing stylesheet CSS with inline styles is that inline styles can only be overwritten with !important, which leads to a whole new level of problems. By only applying classes you keep the specificity battle inside your stylesheet, which is where it should take place.
Here's how I'd write it:
function openPage(e) {
const target = e.target.dataset['target'];
if (target) {
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
el.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
document.getElementById(el.dataset['target'])
.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
})
}
}
document.querySelector('.tabs_sidebar').addEventListener('click', openPage);
.wrapper {
min-height: 100vh;
display: grid;
grid-template-columns: 150px 1fr;
border: 1px solid #cccccc;
padding: 15px;
grid-template-areas: 'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
}
.tabs_content--active > div {
width: 100%;
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
display: none;
}
.tabs_content--active > div.active {
display: block;
}
.tabs_sidebar > button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_sidebar > button.active {
background: #dddddd;
}
.tabs_sidebar > button:not(:last-of-type) {
border-bottom: 1px solid #cccccc;
}
body {
margin: 0;
}
* {
box-sizing: border-box;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button data-target="Home" class="active">Home</button>
<button data-target="News">News</button>
<button data-target="Contact">Contact</button>
</nav>
<section class="tabs_content--active">
<div id="Home" class="active">
<h2>Tab #1</h2>
<p>Home</p>
</div>
<div id="News">
<h2>Tab #2</h2>
<p>News</p>
</div>
<div id="Contact">
<h2>Tab #3</h2>
<p>Contact</p>
</div>
</section>
</div>
Note I've also made changes to markup (HTML) and CSS, not just JavaScript.
As requested in comments, here's an explanation on what the JavaScript code does:
Instead of placing a click handler on each individual button, I've placed one on their parent. It has the advantage of working on future buttons as well, should your page be dynamic. If you bind on each button, once the buttons change you have to figure out which ones are new and only bind the handler on those (you don't want to bind the handler twice on the same element).
When a click is performed inside that button container, you first have to determine which button was clicked (or if the click was outside of any existing buttons). See how data attributes work here. If the click was performed on a button (if (target)), instead of having two loops (one through the buttons and one through the tabs), I only used one loop (through the buttons).
Instead of doing a classic for loop I chose to cast the NodeList returned by querySelectorAll to an array which I can then iterate using .forEach().
Note: To be totally fair, I suspect the for loop (what you used) is more performant but the difference is quite small and I tend to go for the shorter syntax:
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
// do stuff with each looped element. In this case, a button
})
Now that we have the element, let's do something with it: Add or remove the class active based on whether or not the current button's target matches the one that was clicked (which we already have saved in target const).
I personally prefer classList to className. It works on the classes array, rather than on the resulting classes string. Has add, remove and replace methods and you don't end up having to worry about adding spaces manually (or trimming them). I'm using
el.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
instead of
if (el.dataset['target'] === target) {
el.classList.add('active');
} else {
el.classList.remove('active');
}
I used the array notation to call add and remove methods on classList. (i.e: in Javascript you can write foo.bar(arg) as foo['bar'](arg) - all methods are still object properties). The advantage is that inside the array notation you can switch the method being called, based on a ternary condition, which is exactly what I'm doing above.
Using the same technique, I'm also adding/removing the active class from the corresponding tab, which I'm selecting by id.
document.getElementById(el.dataset['target'])
.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
The only thing that could slightly be improved about my code is saving the result of the ternary condition instead of doing it twice. Like this:
if (target) {
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
const isActive = el.dataset['target'] === target;
el.classList[isActive ? 'add' : 'remove']('active');
document.getElementById(el.dataset['target'])
.classList[isActive ? 'add' : 'remove']('active');
})
}
Hope that makes sense.

How to achieve code folding / collapsing for a sql without losing the formatting

I have a sql code where I need to fold / collapse the code at specific sql clauses like SELECT, WHERE, FROM.
On click / hovering of the mouse, the rest of the sql needs to be displayed.
I tried collapsibles and few other features but when I make a specific sql clause as collapsible, I lose the formatting and it gets auto aligned to the left. didn't help.
I am very new to this and hence don't have much idea on how to proceed ahead.
Any help is much appreciated.
HTML collapsibles, bootstrap collapsible, HTML details & summary tags.
SELECT /* collapse here*/ column1
,column2
FROM /* collapse here*/ table1
,table2
,(
SELECT /* collapse here*/ column1
FROM dual
) table3
WHERE /* collapse here*/
table1.col1 = table2.col1
AND table2.col2 = table3.col1
SQL code should have similar features as Javascript code folding, where we can minimize and expand as required.
You can go like this,Here I just took button but instead of it you can use + and - sign.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.collapsible {
background-color: #777;
color: white;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
.active, .collapsible:hover {
background-color: #555;
}
.content {
padding: 0 18px;
display: none;
overflow: hidden;
background-color: #f1f1f1;
}
</style>
</head>
<body>
<h2>Collapsibles</h2>
<p>A Collapsible:</p>
<button class="collapsible">SELECT</button>
<div class="content">
<p>
column1
,column2
</p></div>
<button class="collapsible">FROM</button>
<div class="content">
<p>
table1
,table2
,(
SELECT column1
FROM dual
) table3
</p></div>
<button class="collapsible">WHERE</button>
<div class="content">
<p>
table1.col1 = table2.col1
AND table2.col2 = table3.col1
</p>
</div>
<script>
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
} else {
content.style.display = "block";
}
});
}
</script>
</body>
</html>
Hope this will help you.

Page Tabs and JS: getElementbyId isn't working?

I was trying to implement full page tabs from here into my php code. I only have one problem: It seems that the script part with getElementbyId isn't working.
It basically should activate the button containing id="defaultOpen", so one tab is already opened by the browser. You can see the logic in the example above.
I double checked everything (especially the echoes), can't see what mistake I'm making here.
This is the script:
<script>
function openPage(pageName, elmnt, color) {
// Hide all elements with class="tabcontent" by default */
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// Remove the background color of all tablinks/buttons
tablinks = document.getElementsByClassName("tablink");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].style.backgroundColor = "";
}
// Show the specific tab content
document.getElementById(pageName).style.display = "block";
// Add the specific color to the button used to open the tab content
elmnt.style.backgroundColor = color;
}
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
</script>
This is my code:
echo'<button class="tablink" onclick="openPage('; echo"'Home', this, 'red')"; echo'">Home</button>';
echo'<button class="tablink" onclick="openPage('; echo"'Kader', this, 'green')"; echo'" id="defaultOpen">Kader</button>';
echo'<button class="tablink" onclick="openPage('; echo"'Statistik', this, 'blue')"; echo'">Statistik</button>';
echo'<button class="tablink" onclick="openPage('; echo"'Vertrage', this, 'orange')"; echo'">Vertrage</button>';
echo'<button class="tablink" onclick="openPage('; echo"'Archiv', this, 'orange')"; echo'">Archiv</button>';
// Menü
echo'<div id="Home" class="tabcontent">';
include 'all_home.php';
echo'</div>';
echo'<div id="Kader" class="tabcontent">';
include 'all_kader.php';
echo'</div>';
echo'<div id="Statistik" class="tabcontent">';
include 'all_statistik.php';
echo'</div>';
echo'<div id="Vertrage" class="tabcontent">';
include 'all_verträge.php';
echo'</div>';
echo'<div id="Archiv" class="tabcontent">';
include 'all_archiv.php';
echo'</div>';
The CSS is:
/* Style tab links */
.tablink {
color: white;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
font-size: 17px;
width: 20%;
}
/* Style the tab content (and add height:100% for full page content) */
.tabcontent {
color: white;
display: none;
padding: 55px 5px;
height: 100%;
}
#Home {
background-image: url("bg.png");
background-color: white;
}
#Kader {
background-image: url("bg.png");
background-color: white;
}
#Statistik {
background-image: url("bg.png");
background-color: white;
}
#Verträge {
background-image: url("bg.png");
background-color: white;
}
#Archiv {
background-image: url("bg.png");
background-color: white;
}
Since clicking any of your buttons just calls the function openPage, why not just call the openPage function on first load instead of trying to simulate button clicks to do the same thing? In other words change this:
document.getElementById("defaultOpen").click();
to this:
openPage('Kader', document.getElementById("defaultOpen"), 'green');

Categories