Tab function to improve. - javascript

I have this script that I need to run a tab (jquery). Mainly I need to hide some div and add class (you sure have understood).
How should it be written in a more elegant and readable?
function fun1(){
$('#tab1 a').addClass('selected');
$('#tab2 a').removeClass('selected');
$('#tab3 a').removeClass('selected');
document.getElementById('div1').style.display='block';
document.getElementById('div2').style.display='none';
document.getElementById('div3').style.display='none';
}
function fun2(){
$('#tab1 a').removeClass('selected');
$('#tab2 a').addClass('selected');
$('#tab3 a').removeClass('selected');
document.getElementById('div1').style.display='none';
document.getElementById('div2').style.display='block';
document.getElementById('div3').style.display='none';
}
function fun3(){
$('#tab1 a').removeClass('selected');
$('#tab2 a').removeClass('selected');
$('#tab3 a').addClass('selected');
document.getElementById('div1').style.display='none';
document.getElementById('div2').style.display='none';
document.getElementById('div3').style.display='block';
}
window.onload = function() {
document.getElementById('tab1').onclick=fun1;
document.getElementById('tab2').onclick=fun2;
document.getElementById('tab3').onclick=fun3;
document.getElementById('div1').style.display='block';
document.getElementById('div2').style.display='none';
document.getElementById('div3').style.display='none';
}

You should avoid repeating your code. How about a single function that will take care of everything:
function tab(id){
$('#tab1').parent().children().removeClass('selected'); // remove selected class from all tabs
$('#tab' + id).addClass('selected'); // add just to one we need
$('#div1').parent().children().hide(); // hide all the #div elements
$('#div' + id).show(); // show the one we need
}
Notes for the changes I made:
selected class is now applied to #tab elements, not the anchors inside them
I assumed all the #tabs and #divs are the only siblings within their containers
to change the active tab, just call tab(1), tab(2), etc...
Here's a simple example with my approach: http://jsfiddle.net/CkpwT/1/

You could try something like this:
var tabs, divs;
function handler(n) {
return function fun() {
for(var i=0, l=tabs.length; i<l; ++i)
tabs[i].find('a').toggleClass('selected', n==i);
for(var i=0, l=divs.length; i<l; ++i)
divs[i].toggle(n==i);
};
}
window.onload = function() {
tabs = [$('#tab1'), $('#tab2'), $('#tab3')];
divs = [$('#div1'), $('#div2'), $('#div3')];
for(var i=0, l=tabs.length; i<l; ++i)
tabs[i].on('click', handler(i));
tabs[0].click();
}
Demo

So, you have a tab for every page. And on click you want to also add a 'selected' class to the clicked element.
PLAYGROUND
All you need is basically this simple HTML markup:
<ul id="tabs">
<li>Tab1</li>
<li>Tab2</li>
<li>Tab3</li>
</ul>
<div id="divs">
<div>Div1</div>
<div>Div2</div>
<div>Div3</div>
</div>
than you can simply get the index value of the clicked tab and open the same indexed DIV element:
$tabs = $('#tabs a'); // Cache your selectors
$divs = $('#divs > div');
$tabs.click(function(e) {
e.preventDefault(); // prevent browser from following anchor href
$tabs.removeClass('active');
$(this).addClass('active');
$divs.hide().eq( $tabs.index(this) ).show(); // Get the Tab element index and refer to the DIV using .eq()
}).eq(0).click(); // Trigger the initial click on a desired tab index(0)

Related

How to use addClass and removeClass repeatedly on a single element?

So what I want to achieve is just change the classes of a HTML link on every click like this:
Remove .first class if it is present, then add .second class
Remove .second class if it is present, then add .third class
Remove .third class if it is present, then add .fourth class
And so forth...
No luck so far. What could I be doing wrong?
Here's the single line of HTML code where I'm trying my jQuery code on:
<a class="first" href="#">Test 1</a>
Here's my jQuery:
$( "#menu li a.first" ).click(function() {
$( "#menu li a.first" ).removeClass("first").addClass("second");
}
$( "#menu li a.second" ).click(function() {
$( "#menu li a.second" ).removeClass("second").addClass("third");
}
$( "#menu li a.third" ).click(function() {
$( "#menu li a.second" ).removeClass("third").addClass("fourth");
}
Thanks in advance!
The problem is you're trying to attach the event handler before it even has the class second or third.
Besides this approach is pretty verbose. I suggest simply providing an array of classes. Like so:
var classNames = ['first', 'second', 'third'];
Then add a different identifier to the button, for instance add a class class-changer. And attach the following event handler.
$('.class-changer').on('click', function() {
var $el = $(this)
for (var i= 0; i < classNames.length; i++) {
if ($el.hasClass(classNames[i]) && classNames[i+1]) {
$el.removeClass(classNames[i]).addClass(classNames[i+1]);
break;
}
}
});
Put all classes in an array and on click of the link add class one by one like following.
var classes = ["first", "second", "third", "fourth"];
$("#menu li a").click(function () {
var index = classes.indexOf(this.className);
var newIndex = (index + 1) % classes.length; //return to first after reaching last
$(this).removeClass(classes[index]).addClass(classes[newIndex]);
});
.first { color: red; }
.second { color: green; }
.third { color: blue; }
.fourth { color: purple; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="menu">
<li>
<a class="first" href="#">Test 1</a>
</li>
</ul>
Assuming you actually only have 1 link whose state you're trying to change, instead of a bunch of links in your menu that you want to ALL be moved from ".first" to ".second" when one is clicked, I would suggest this as the most idiomatic way (pun not intended).
// Only select the menu once
var $menu = $('#menu');
// Delegate to elements with the correct class.
// Specifying the "li a" is probably unnecessary,
// unless you have other elements with the same classes in "#menu".
$menu.on('click', '.first', function(e) {
// Inside a jQuery event handler,
// `this` refers to the element that triggered the event.
// If the event is delegated, it's the delegation target
// (".first" in this instance), not the bound element ("#menu").
$(this).removeClass('first').addClass('second');
});
$menu.on('click', '.second', function(e) {
$(this).removeClass('second').addClass('third');
});
$menu.on('click', '.third', function(e) {
$(this).removeClass('third').addClass('fourth');
});
Resources:
Why should you cache jQuery selectors?
Event Delegation in jQuery
"this" in jQuery events
General jQuery Optimization
You can do it with the usage of .data()
HTML:
<a class="first" href="#" id="test">Test 1</a>
JS:
$(".first").data("classes",["one","two","three","four"]).click(function() {
var elem = $(this);
var cnt = (elem.data("cnt") || 0)
var classes = elem.data("classes");
elem.removeClass().addClass(classes[cnt % classes.length] + " first").data("cnt",++cnt);
});
Demo
$(".first").data("classes",["one","two","three","four"]).click(function() {
var elem = $(this);
var cnt = (elem.data("cnt") || 0)
var classes = elem.data("classes");
elem.removeClass().addClass(classes[cnt % classes.length] + " first").data("cnt",++cnt);
});
.one{
color:red;
}
.two{
color:yellow;
}
.three{
color:green;
}
.four{
color:blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<a class="first" href="#" id="test">Test 1</a>
Not sure if this would solve your issue but I would shoot for a conditional statement and only one delegated event listener:
$("#menu li").on("click", "a", function () {
if ($(this).hasClass("first")) {
$(this).removeClass("first").addClass("second");
} else if ($(this).hasClass("second")) {
$(this).removeClass("second").addClass("third");
}
// etc...
});
If you want to bind an event the selected element must exist previously.
To bind an event handler to elements that does not yet exist (ex. dynamically created or modified) you can do this:
$(document).on('click', '#menu li a.first', function() {
$( "#menu li a.first" ).removeClass("first").addClass("second");
});
$(document).on('click', '#menu li a.second', function() {
$( "#menu li a.second" ).removeClass("second").addClass("third");
});
$(document).on('click', '#menu li a.third', function() {
$( "#menu li a.third" ).removeClass("third").addClass("fourth");
});
<a class="changable first" href="#">Test 1</a>
$( ".changable" ).click(function(event) {
classes = ['first','second','third','fourth']
changed=false
for (c in classes){
if (event.target.classList.contains(classes[c]) && changed==false){
$(this).removeClass((classes[c]));
index_to_add=classes.indexOf(classes[c])+1
class_to_add=classes[index_to_add]
$(this).addClass(class_to_add);
changed=true;
}
}
});
Okay so there is a few workaround for this, which wasn't mentioned yet.
You can use Javascript object for this not just array. Object could make it easier if you want a chain instead of list.
var classNames = {first:'second', second:'third', third:'fourth'};
$('#menu li a').on('click', function() {
if(typeof classNames[this.className] !== 'undefined'){
this.className = classNames[this.className];
}
});
Second method is to use .on('click', [selector], handler) instead click which can handle dynamicly loaded, added or changed elements.
$('#menu li').on('click', 'a.first', function() {
$(this).removeClass("first").addClass("second");
});
$('#menu li').on('click', 'a.second', function() {
$(this).removeClass("second").addClass("third");
});
$('#menu li').on('click', 'a.third', function() {
$(this).removeClass("third").addClass("fourth");
});
Not even close to perfect but still a working solution.
You can use if .. else or switch .. case inside a function to create a decision tree.
So basically there is a lot of solution. Pick the best.
Try binding event to parent,
My try,
var $classes = ['first', 'second', 'third'];
$(function(){
$('#subject').click(function(){
current = $(this).find('a:first');
index = $.inArray(current.attr('class'), $classes);
if($classes.length > index+1)
current.removeClass($classes[index]).addClass($classes[index+1])
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id='subject'>
<a class="first" href="#">Test 1</a>
</div>
No, you can't. As JavaScript only runs after the page loads ( if you put them inside the $( document ).ready() function ), further functions down below will never be executed. It can only detect the <a class="first" href="#">Test 1</a> but not the <a class="second" href="#">Test 1</a> because the <a class="second" href="#">Test 1</a> are generated after the page loads and, therefore, will never be executed, unless you are using Ajax.
Update: This can be done. Please see #i3b13's comment below.

Remove class from all and add to one clicked

I am writing a function where I want to remove an active class from all elements and add it to the one which was just clicked. Problem is that when I click the element all of them get the active class. Please see the code below.
var pagination = $('.pagination div');
function pager() {
pagination.removeClass('active', function(){
$(this).addClass('active');
});
}
$('.pagination div').on("click", function (){
pager();
});
I could use the code below, which works actually, but the reason I want to use the above one is to have possibility adding other functions in it which will be called later on click.
$('.pagination div').on('click',function(){
$('.pagination div').removeClass('active');
$(this).addClass('active');
});
HTML if needed
<div class="pagination">
<div class="pagination1"></div>
<div class="pagination2"></div>
<div class="pagination3"></div>
<div class="pagination4"></div>
</div>
By using a separate function, you are losing your reference to the current object (this). You will need to use a parameter to get your way working.
function pager(element) {
pagination.removeClass('active', function(){
element.addClass('active');
});
}
$('.pagination div').on("click", function (){
pager($(this));
});
I want to use the above one is to have possibility adding other
functions in it which will be called later
Try this:
function pager(el) {
pagination.removeClass('active', function(){
$(el).addClass('active');
});
}
$('.pagination div').on("click", function (){
pager(this);
});
$('div.pagination').on('click', 'div', function(){
$(this).addClass('active').siblings().removeClass('active');
});
How about this:
var pagination = $('.pagination div');
function pager(selector) {
$('.pagination div').removeClass('active');
$(selector).addClass('active');
}
$('.pagination div').on("click", function (){
pager(this);
});
var pagination = $('.pagination div');
function pager(that) {
pagination.removeClass('active', function(){
that.addClass('active');
});
}
$('.pagination div').on("click", function (){
pager($(this));
});
Editable JSFiddle
HTML
<div class="pagination">
<div class="pagination1">Pagination 1</div>
<div class="pagination2">Pagination 2</div>
<div class="pagination3">Pagination 3</div>
<div class="pagination4">Pagination 4</div>
JavaScript
$(function() { // DOM loaded event handler
function pager (element) {
$(".pagination div").removeClass("active");
element.addClass("active");
}
$(".pagination div").click(function() {
var element = $(this);
pager(element);
});
});
CSS
.active {
color : red;
}
I used your function in 2 steps :
First, I remove all active class by using $(".pagination div").removeClass("active"); which actually apply this effect on all sub div
then, I use the element passed through parameter to the function to scope it, and be able to add the proper class
Use id for all the element then add active class to each element which has been click or use e.target this will track the current element
$('.pagination').click(function(e){
$('.pagination').removeClass('active');
$(e.target).addClass('active');
});

adding active class to parent list when link is clicked

adding active class to parent list when link is clicked/active , am trying to inject that using JavaScript as follow:
$(document).ready(
function()
{
//injecting active status to the navigation bar lists
var element = document.getElementById("navbar-ul");
var links = element.getElementsByTagName("a");
for(var i = 0; i < links.length ; i++) {
links[i].onclick(function () {
links[i].parent().addClass('active');
});
}
}
);
but am getting the following error:
TypeError: links[i].onclick is not a function
how I supposed to achieve that?
A more verbose JQuery way to approach this
$('#navbar-ul a').each(function() {
$(this).on('click', function() {
$(this).parent().addClass('active');
});
});
This is very simply with jQuery:
$("#navbar-ul a").click(function(){
$(this).parent().addClass("active");
});
EXAMPLE 1
I'm guessing you're trying to add an active state to the link thats being clicked on and remove the others? If so you can use .siblings() to find the other links and remove their class:
$("#navbar-ul a").click(function(){
$(this).closest("li").addClass("active").siblings("li").removeClass("active");
});
EXAMPLE 2
You would be better of with adding a class to the tags who needs an eventListener. (with jquery)
$(document).ready(function() {
$("#navbar-ul a").on("click", function() {
var active = document.querySelector('#navbar-ul .active');
if(active){
$("#navbar-ul a").removeClass('active');
}
$(this).addClass("active");
});
);

JQuery toggle between different divs

I need a some sort of a practical solution for toggle between different divs, when i click on a anchor tag.
I have done a JSfiddle that is kind of the solution i want.
the problem there is when i first click on "show 1" and then "show 2" the two first placeholders content disappear, but nothing new shows up.
I want it this way:
When i click "show 1", Two Placeholders appear(PlaceHolder 1 and 2).
When clicking "show 2" WITHOUT closing Placeholder 1 and 2. The PlaceHolder 1 and 2 should close AND PlaceHolder 3 should appear.
Jsfiddle: http://jsfiddle.net/CY3tj/2/
HTML:
<a id="1" class="show">show 1</a>
<br/ ><br/ >
<a id="2" class="show">show 2</a>
<div class="content-wrapper">
<div id="item-1">
<div>
<h2>Placeholder1</h2>
<p>Placeholder1</p>
</div>
<div>
<h2>PLaceHolder2</h2>
<p>Placeholder2</p>
</div>
</div>
<div id="item-2">
<div>
<h2>Placeholder3</h2>
<p>Placeholder3</p>
</div>
</div>
</div>
JQuery:
$(document).ready(function () {
$(".content-wrapper").hide();
});
$(document.body).on("click", "a.show", function () {
var id = $(this).attr("id");
$(".content-wrapper > div").each(function () {
if ($(this).attr("id") == "item-" + id) {
$(this).show();
} else {
$(this).hide();
}
});
$(".content-wrapper").toggle();
});
You can simplify this to:
$(function(){
var $allItems = $(".content-wrapper > div"); //Cache the collection here.
$(document).on("click", "a.show", function () {
var id = this.id, itemId = "#item-" + id; //get the id and itemId
$allItems.not($(itemId).toggle()).hide(); //Hide all items but not the one which is the target for which you will do a toggle
});
});
Demo
Instead of hiding the wrapper via JS, you can just add a rule to hide its contents.
.content-wrapper >div{
display:none;
}
Your main problem is your using $(.content-wrapper).toggle() You only want to hide the content wrapper initially and then after one click you want it to show up. By toggling your content wrapper you were making it dissapear every other click which was why you had to click twice to see it.
$(document.body).on("click", "a.show", function () {
var id = $(this).attr("id");
$(".content-wrapper > div").each(function () {
if ($(this).attr("id") == "item-" + id) {
$(this).show();
} else {
$(this).hide();
}
});
$(".content-wrapper").show();
});
If you are looking to keep the toggle functionality (to hide a div that is already showing) here is a solution for that.
$(document.body).on("click", "a.show", function () {
var id = $(this).attr("id");
if($(".content-wrapper #item-"+id).is(':visible'))
$(".content-wrapper").hide();
else{
$(".content-wrapper").children("div").hide();
$(".content-wrapper #item-"+id).show();
$(".content-wrapper").show();
}
});
You would gain more flexibility and performance by selecting the ids of the anchor tags. You should also hide the specific divs that you want to be hidden rather than hiding the overall container. That way, you can easily target which div you want to show and which one to hide.
$(document).ready(function () {
$(".content-wrapper > div").hide();
});
$("#1").click(function(){
$("#item-2").hide();
$("#item-1").show();
});
$("#2").click(function(){
$("#item-1").hide();
$("#item-2").show();
});
However, if you're looking to add an unknown number of these items, then you will want to select (for readability) just the element and class, rather than having to go through the document, which is just redundant.
$(document).ready(function () {
$(".content-wrapper > div").hide();
});
$("a.show").click(function(){
$(".content-wrapper > div").hide();
$("#item-" + $(this).attr("id").show();
});

.addClass to element adds a styling then removes, how do I fix

I have a code
var prev;
function addClass( classname, element ) {
prev = cn;
var cn = document.getElementById(element);
$(cn).addClass("selected");
}
The element in the dom look like this:
<div class="arrowgreen">
<ul>
<li>Manager</li>
<li>Planner</li>
<li>Administrator</li>
</ul>
</div>
For 'arrowgreen' I have a styling which changes the li styling on rollover and click.
When an element is clicked on, I want to apply the 'selected' classname to the element.
It does this for a split second and then reverts back.
The css looks like
.arrowgreen li a.selected{
color: #26370A;
background-position: 100% -64px;
}
Working jsFiddle Demo
In usage of $ in your code, I see that you are using jQuery.
There is no need to set onclick internally.
Let's jQuery handle it for you:
// wait for dom ready
$(function () {
// when user clicks on elements
$('.arrowgreen li a').on('click', function (e) {
// prevent default the behaviour of link
e.preventDefault();
// remove old `selected` classes from elements
$('.arrowgreen li a').removeClass('selected');
// add class `selected` to current element
$(this).addClass('selected');
});
});
Working JSFiddle
There was an error in your HTML, a " that opened a new string after onclick.
var prev;
function addClass(classname, element) {
var cn = document.getElementById(element);
prev = cn; //does nothing useful really
$(cn).addClass("selected");
}
<div class="arrowgreen">
<ul>
<li>Manager
</li>
<li>Planner
</li>
<li>Administrator
</li>
</ul>
</div>
Remember to include jQuery in your page!
There is a way to do this without jQuery anyway:
function addClass(classname, element) {
var cn = document.getElementById(element);
prev = cn; //does nothing useful really
cn.className += " " + classname;
}
Similar way to do it:
(function ($) {
$('.arrowgreen > ul > li > a').on('click', function (e) {
e.preventDefault();
$(this).addClass('selected').siblings().removeClass('selected');
});
}(jQuery));

Categories