Showing/Hiding Divs with Javascript on click - javascript

I'm trying to show/hide tabs on click using just Javascript but I'm getting errors ("Uncaught TypeError: Cannot set property 'className' of undefined tabs.(anonymous function).onclick"). Can someone give me an idea of what the issue might be?
<style>
a { text-decoration: none; }
li { list-style: none; }
li.selected { font-weight: bold; }
.panels div { display: none; }
.panels .selected { display: block; }
</style>
<div id="tabs" class="tabs">
<ul>
<li class="selected">One</li>
<li class="">Two</li>
<li class="">Three</li>
</ul>
</div>
<div id="panels" class="panels">
<div class="selected">This is panel one.</div>
<div class="">This is panel two.</div>
<div class="">This is panel three.</div>
</div>
<script>
var tabs = document.getElementById("tabs").getElementsByTagName("li");
var panels = document.getElementById("panels").getElementsByTagName("div");
for (var i = 0; i < tabs.length; i++) {
new function(i) {
tabs[i].onclick = function() {
tabs[i].className = panels[i].className = "selected";
for (var i = 0; i < panels.length; i++) {
tabs[i].className = panels[i].className = "";
}
}
}(i);
}
</script>

Your inner for loop has an i variable that conflict with the outter variable for loop with the same name.
You should also remove selected class from all elements before setting the clicked element 'selected'.
Try:
<script>
var tabs = document.getElementById("tabs").getElementsByTagName("li");
var panels = document.getElementById("panels").getElementsByTagName("div");
for (var i = 0; i < tabs.length; i++) {
new function(i) {
tabs[i].onclick = function() {
for (var j = 0; j < panels.length; j++) {
tabs[j].className = panels[j].className = "";
}
tabs[i].className = panels[i].className = "selected";
}
}(i);
}
</script>

You've got a couple of problems:
Multiple i variables
new function(i) {...} isn't the best syntax. I've used a closure below
multiple assignments per line isn't good
I've given your <li> elements values so that we can tell which li element has been clicked
var tabs = document.getElementById("tabs").getElementsByTagName("li");
var panels = document.getElementById("panels").getElementsByTagName("div");
for (var i = 0; i < panels.length; i++) {
(function(i) {
tabs[i].onclick = function() {
var j;
var panelIndex;
// remove styles from other tabs
for (j = 0; j < tabs.length; j++) {
tabs[j].className = "";
}
// apply style to the current tab: 'this'
this.className = "selected";
// hide other panels
for (j = 0; j < panels.length; j++) {
panels[j].className = "";
}
// show the selected panel
panelIndex = +this.value; // convert value to number
panels[panelIndex-1].className="selected"; // arrays are 0-indexed, so subtract 1
}
})(i);
}
a { text-decoration: none; }
li { list-style: none; }
li.selected { font-weight: bold; }
.panels div { display: none; }
.panels .selected { display: block; }
<div id="tabs" class="tabs">
<ul>
<li value="1" class="selected">One</li>
<li value="2" class="">Two</li>
<li value="3" class="">Three</li>
</ul>
</div>
<div id="panels" class="panels">
<div class="selected">This is panel one.</div>
<div class="">This is panel two.</div>
<div class="">This is panel three.</div>
</div>

Below here will work, as you are expecting. Two issues I found for accessing HTML Element inside for loop, you need to use .item() as its HTMLCollection you are getting instead of an array. Also your inner for loop needs to use different looping index, with one additional if condition to leave clicked one as shown and rest hidden.
<style>
a { text-decoration: none; }
li { list-style: none; }
li.selected { font-weight: bold; }
.panels div { display: none; }
.panels .selected { display: block; }
</style>
<div id="tabs" class="tabs">
<ul>
<li class="selected">One</li>
<li class="">Two</li>
<li class="">Three</li>
</ul>
</div>
<div id="panels" class="panels">
<div class="selected">This is panel one.</div>
<div class="">This is panel two.</div>
<div class="">This is panel three.</div>
</div>
<script>
var tabs = document.getElementById("tabs").getElementsByTagName("li");
var panels = document.getElementById("panels").getElementsByTagName("div");
for (var i = 0; i < tabs.length; i++) {
new function(i) {
tabs[i].onclick = function() {
tabs.item(i).className = panels.item(i).className = "selected";
for (var j = 0; j < panels.length; j++) {
if(i!=j){
tabs.item(j).className = panels.item(j).className = "";
}
}
}
}(i);
}
</script>

Related

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.

tabs don't work properly

I created tabs on JS and my script decided to deny working.
I click on tabs - they don't work and content also is not hidden.
Chrome shows NO bugs. Code should work without doubts but it seems an error somewere. All the classes such as 'hide', 'show' are created on external style.css file.
Please, help !!
var tab;
var content;
window.onload = function() {
content = document.querySelector('.content');
tab = document.querySelector('.tab');
hideTabsContent(1);
}
function hideTabsContent(a) {
for (var i = a; i < content.length; i++) {
content[i].classList.remove('show');
content[i].classList.add("hide");
tab[i].classList.remove('active');
}
}
document.querySelector('.container').onclick = function(event) {
var target = event.target;
if (target.className == 'tab') {
for (var i = 0; i < tab.length; i++) {
if (target == tab[i]) {
showTabsContent(i);
break;
}
}
}
}
function showTabsContent(b) {
if (content[b].classList.contains('hide')) {
hideTabsContent(0);
tab[b].classList.add('active');
content[b].classList.remove('hide');
content[b].classList.add('show');
}
}
.tab {
cursor: pointer;
}
.active {
color: white;
background-color: green;
}
.show {
display: block;
}
.hide {
display: none;
}
<div class="container">
<div class="tab active">Summer</div>
<div class="tab">Autumn</div>
<div class="tab">Winter</div>
<div class="content">
<img src="img/pic-1.jpg">
<img src="img/pic-2.jpg">
<img src="img/pic-3.jpg">
</div>
<div class="content">
<img src="img/pic-4.jpg">
<img src="img/pic-5.jpg">
<img src="img/pic-6.jpg">
</div>
<div class="content">
<img src="img/pic-7.jpg">
<img src="img/pic-8.jpg">
<img src="img/pic-9.jpg">
</div>
</div>
Use querySelectorAll to return an array of elements or it returns just the first found element.
window.onload=function() {
content=document.querySelectorAll('.content');
tab=document.querySelectorAll('.tab');
hideTabsContent(1);
}
Change the selection from querySelctor() to querySelectorAll() working example is below
var tab;
var content;
window.onload = function() {
content = document.querySelectorAll('.content');
tab = document.querySelectorAll('.tab');
hideTabsContent(1);
}
function hideTabsContent(a) {
for (var i = a; i < content.length; i++) {
content[i].classList.remove('show');
content[i].classList.add("hide");
tab[i].classList.remove('active');
}
}
document.querySelector('.container').onclick = function(event) {
var target = event.target;
if (target.className == 'tab') {
for (var i = 0; i < tab.length; i++) {
if (target == tab[i]) {
showTabsContent(i);
break;
}
}
}
}
function showTabsContent(b) {
if (content[b].classList.contains('hide')) {
hideTabsContent(0);
tab[b].classList.add('active');
content[b].classList.remove('hide');
content[b].classList.add('show');
}
}
.tab {
cursor: pointer;
}
.active {
color: white;
background-color: green;
}
.show {
display: block;
}
.hide {
display: none;
}
<div class="container">
<div class="tab active">Summer</div>
<div class="tab">Autumn</div>
<div class="tab">Winter</div>
<div class="content">
<span>1</span>
<span>2</span>
<span>3</span>
</div>
<div class="content">
<span>4</span>
<span>5</span>
<span>6</span>
</div>
<div class="content">
<span>7</span>
<span>8</span>
<span>9</span>
</div>
</div>

Why does one calculation for my onClick get applied to all elements that I'm looping through

Currently doing some exercise for CSS/Javascript animation. I'm attempting to make a Carousel slider from scratch.. I have 4 divs with 550px in width nested in a wrapper of 2200px, which is then nested in a 550px wrapper with overflow hidden.
I then created 4 LI's that I want to make clickable so that it'll translate the wrapper -550*I degrees for every LI.
I performed a queryselectorall to get all the li's, looped through it with a for loop, and created a function that should apply onclick functionality for each LI button.
The issue that I'm running into is that the first calculation of this transform property is applied to all LI's (the 550 * i for [1] [2] and [3] aren't applied).
Here's the HTML that I'm currently using.
<div id="container">
<div id="wrapper">
<div id="itemOne" >
</div>
<div id="itemTwo">
</div>
<div id="itemThree">
</div>
<div id="itemFour">
</div>
</div>
</div>
<ul>
<li class="button"></li>
<li class="button"></li>
<li class="button"></li>
<li class="button"></li>
</ul>
The Javascript
var wrapper = document.querySelector("#wrapper");
var buttons = document.querySelectorAll(".button");
for(var i = 0; i < buttons.length; i++){
var curBut = buttons[i];
curBut.addEventListener("click", function(){
wrapper.style[transformProperty] = 'translate3d(-'+((0-i) * 550) +'px,0,0'
})
console.log(((0-i) * 550));
}
console.log(buttons);
var transforms = ["transform",
"msTransform",
"webkitTransform",
"mozTransform",
"oTransform"];
var transformProperty = getSupportedPropertyName(transforms);
function getSupportedPropertyName(properties) {
for (var i = 0; i < properties.length; i++){
if(typeof document.body.style[properties[i]] != "undefined") {
return properties[i];
}
}
return null;
}
If anyone could explain why the function isn't applying the different changes for the wrapper for each LI, that'd be great! Thanks!!
The global variable i is not copied into each listener, it's shared between the listeners. When you click a button, i is already set to its final value which is 4. As a possible workaround you could override the global variable with a local variable, and get the index on click using indexOf :
var wrapper = document.querySelector("#wrapper");
var buttons = document.querySelectorAll("button");
for (var i = 0; i < buttons.length; i++) {
var curBut = buttons[i];
curBut.addEventListener("click", function() {
var i = Array.prototype.indexOf.call(buttons, this);
wrapper.style[transformProperty] = 'translate3d(-' + (i * 260) + 'px,0,0)';
});
}
var transforms = ["transform",
"msTransform",
"webkitTransform",
"mozTransform",
"oTransform"];
var transformProperty = getSupportedPropertyName(transforms);
function getSupportedPropertyName(properties) {
for (var i = 0; i < properties.length; i++) {
if (typeof document.body.style[properties[i]] != "undefined") {
return properties[i];
}
}
return null;
}
#container {
overflow: hidden;
background: gray;
margin-bottom: 1em;
width: 260px;
height: 100px;
}
#wrapper {
width: calc(4 * 260px);
height: 100px;
}
#wrapper div {
padding: 0 1em;
width: calc(260px - 2em);
line-height: 100px;
height: 100px;
float: left;
color: white;
font-size: 3em;
font-weight: bold;
text-align: center;
}
<div id="container">
<div id="wrapper">
<div id="itemOne">1</div>
<div id="itemTwo">2</div>
<div id="itemThree">3</div>
<div id="itemFour">4</div>
</div>
</div>
<div>
<button type="button">button 1</button>
<button type="button">button 2</button>
<button type="button">button 3</button>
<button type="button">button 4</button>
</div>

How to loop a JavaScript nodelist and apply a JavaScript style?

How can I get the JavaScript loop associated with the liList variable to effect the DOM whist not over-riding any other styles?
I would like my menu items to still be orange and for the first list item to hold onto the styleAbbr variable that was created in JavaScript.
//JavaScript style to effect HTML abbr
var styleAbbr = document.createElement('style');
styleAbbr.type = 'text/css';
styleAbbr.innerHTML = '.myAbbr {font-style: italic; letter-spacing: .4em;}';
document.getElementsByTagName('head')[0].appendChild(styleAbbr);
document.querySelector('abbr').className = 'myAbbr';
//the JavaScript for the loop
var msg = '';
var i = 0;
var liList = document.querySelectorAll('li.menuItem');
if (i < liList.length) {
for (var i = 0; i < liList.length; i++) {
//liList[i].textContent gets the text form the li element in the DOM
var listWording = liList[i].textContent;
//liList.innerHTML applies my style and pushes listWording into the DOM
msg += '<li style="background: red; margin-top: 10px;">'
+ listWording
+ '</li>';
}
}
//I believe that the issue is that .innerHTML = msg.
document.getElementById('mMain').innerHTML = msg;
ul {
font-size: 1.4em;
list-style: none;
}
ul li a {
text-decoration: none;
color: #e69b1e;
}
<ul id="mMain">
<li class="menuItem">
<a href="#">
<abbr title="HyperText Markup Language">HTML</abbr>
</a>
</li>
<li class="menuItem">
About
</li>
<li class="menuItem">
Gallery
</li>
<li class="menuItem">
Contact
</li>
</ul>
Ideally I would like to have my JavaScript style in its own variable and loop that into the "menuItem" class whilst not effecting any other predefined class's however I am not quite sure how I would call that variable into a loop.
var menuItemClass = document.createElement('style');
menuItemClass.type = 'text/css';
menuItemClass.innerHTML = '.menuStyle {background: red; margin-top: 10px;}';
document.getElementsByTagName('head')[0].appendChild(menuItemClass);
Im also aware I could have a class in my CSS do all of this and then reference that.
However I am interested how this can be achieved in JavaScript.
Thank you.
I found a solution to referencing my JavaScript defined style.
var styleAbbr = document.createElement('style');
styleAbbr.type = 'text/css';
styleAbbr.innerHTML = '.myAbbr {font-style: italic; letter-spacing: .4em;}';
document.getElementsByTagName('head')[0].appendChild(styleAbbr);
document.querySelector('abbr').className = 'myAbbr';
var menuItemClass = document.createElement('style');
menuItemClass.type = 'text/css';
menuItemClass.innerHTML = '.menuStyle {background: red; margin-top: 10px;}';
document.getElementsByTagName('head')[0].appendChild(menuItemClass);
var msg = '';
var i = 0;
var liList = document.querySelectorAll('li.menuItem');
if (i < liList.length) {
for (var i = 0; i < liList.length; i++) {
liList[i].className = 'menuStyle';
}
}
ul li {
font-size: 1.4em;
list-style: none;
}
ul li a {
text-decoration: none;
color: #e69b1e;
}
<ul id="mMain">
<li class="menuItem">
<a href="#">
<abbr title="HyperText Markup Language">HTML</abbr>
</a>
</li>
<li class="menuItem">
About
</li>
<li class="menuItem">
Gallery
</li>
<li class="menuItem">
Contact
</li>
</ul>
I am answering how to add class to each li element created dynamically without effecting predefined. So create class according to the menu item innerText and take background color for the class from array defined colors
//JavaScript style to effect HTML abbr
var styleAbbr = document.createElement('style');
styleAbbr.type = 'text/css';
styleAbbr.innerHTML = '.myAbbr {font-style: italic; letter-spacing: .4em;}';
document.getElementsByTagName('head')[0].appendChild(styleAbbr);
document.querySelector('abbr').className = 'myAbbr';
//the JavaScript for the loop
var msg = '';
var i = 0;
var liList = document.querySelectorAll('li.menuItem');
//defining background colours
var colors = ['red', 'orange', 'green', 'brown', 'blue'];
var j = 0;
if (i < liList.length) {
for (var i = 0; i < liList.length; i++) {
//creating class as the innerText content
if (j >= colors.length) {
j = 0
}
var menuItemClass = document.createElement('style');
menuItemClass.type = 'text/css';
menuItemClass.innerHTML = '.' + liList[i].innerText + ' {background: ' + colors[j] + '; margin-top: 10px;}';
document.getElementsByTagName('head')[0].appendChild(menuItemClass);
j++;
//liList[i].textContent gets the text form the li element in the DOM
var listWording = liList[i].textContent;
//liList.innerHTML applies my style and pushes listWording into the DOM
if (i == 0) {
msg += liList.innerHTML =
'<li class="myAbbr ' + listWording + '">' +
listWording +
'</li>';
} else {
msg += liList.innerHTML =
'<li class=" ' + listWording + '">' +
listWording +
'</li>';
}
}
}
//I believe that the issue is that .innerHTML = msg.
document.getElementById('mMain').innerHTML = msg;
ul {
font-size: 1.4em;
list-style: none;
}
ul li a {
text-decoration: none;
color: #e69b1e;
}
<ul id="mMain">
<li class="menuItem">
<a href="#">
<abbr title="HyperText Markup Language">HTML</abbr>
</a>
</li>
<li class="menuItem">
About
</li>
<li class="menuItem">
Gallery
</li>
<li class="menuItem">
Contact
</li>
</ul>

How to target individual div & apply class or attribute id

How can i select each div inside parent div & apply class (om_0 & go on) with increasing index number. Here I am unable to target each div.
Or how can I add attribute id="om_0", id="om_1", id="om_2" etc. to each div
The problem is it's applying all classes in one div & repeat it
var cirLength = $("div#circleBox > div").length;
for(var i=0; i<cirLength; i++){
$("div#circleBox").find('div').addClass('om_'+i);
}
<div id="circleBox"><div class="om_0 om_1 om_2"><span>AcessGreen</span></div><div class="om_0 om_1 om_2"><span>AccessBlue</span></div><div class="om_0 om_1 om_2"><span>AccessOrange</span></div></div>
You can use each() to iterate jQuery objects
$("div#circleBox").find('div').each(function(i) {
$(this).addClass('om_' + i);
// If you need to add it as id then use `this.id= 'om_' + i ` instead of `$(this).addClass('om_' + i)`
});
.om_0 {
color: red;
}
.om_1 {
color: green;
}
.om_2 {
color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="circleBox">
<div><span>AcessGreen</span>
</div>
<div><span>AccessBlue</span>
</div>
<div><span>AccessOrange</span>
</div>
</div>
With your own code you need to select individual item using eq()
var cirLength = $("div#circleBox > div").length;
for (var i = 0; i < cirLength; i++) {
$("div#circleBox").find('div').eq(i).addClass('om_' + i);
}
.om_0 {
color: red;
}
.om_1 {
color: green;
}
.om_2 {
color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="circleBox">
<div><span>AcessGreen</span>
</div>
<div><span>AccessBlue</span>
</div>
<div><span>AccessOrange</span>
</div>
</div>

Categories

Resources