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

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.

Related

Tab function to improve.

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)

.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));

Javascript, switching images through both mouseover and click

I have some buttons( "ul.subjects li" ) and...
1. change buttons' background-images through mouseover actions
2. can be clicked one button at once.
 So once clicked a button, others are cancelled.
I have two this button lists and switch contents by clicked items(subjects and ages).
I can switch background-images by changing "class" of (class="mat" to class="mat_c").
$(function(){
$(document).delegate("ul.subjects li a", "mouseover", function(){
$(this).data("imgc", $(this).attr("class"));
var regex = /_s/;
if (regex.test($(this).data("imgc")) == false ) {
$(this).attr("class", $(this).data("imgc")+"_c")
}
});
$(document).delegate("ul.subjects li a", "mouseout", function(){
$(this).attr("class", $(this).attr("class").replace(/_c/, ""));
});
});
<ul class="subjects">
<li><a id="mat" class="mat" href="/age=all/sub=mat/"></a>算数・数学</li>
<li><a id="soc" class="soc" href="/age=all/sub=soc/"></a>社会</li>
<li><a id="sci" class="sci" href="/age=all/sub=sci/"></a>理科</li>
<li><a id="eng" class="eng" href="/age=all/sub=eng/"></a>英語</li>
</ul>
While button-click action doesn't work right, trying to change attribute into class="..._s",
but immediately changed into class="..._c".
$(function(){
$(document).delegate("ul.subjects li a", "click", function(){
$("ul.subjects").html($("ul.subjects").html().replace(/_s/g, ""));
$(this).attr("class", $(this).attr("class").replace(/_c/, "_s"));
});
});
Maybe I should control the events or order of them...
and there should be more simple ways to do this. How should I figure this out?
As you can see it on http://jsfiddle.net/MsdGS/1/,
After you click the under list , mouseover action doesn't work right.
Clicked buttons' "class" should be changed to "..._s",
(class="mat" to class="mat_s")
but "..._s" immediately changed to "..._c".
Thanks,
The result (i've removed the href in the bottom ul list)
If i have understood your question correct this is what you need to change:
$(function(){
$("#course_tb").load("/age=all/sub=all/"+"#course_tb");
$(document).delegate("ul.subjects li a", "click", function(){
// iterate through `subjects`'s "a"
$("ul.subjects li a").each(function(index, el){
$(el).attr("class", $(el).attr("class").replace(/_s/g, ""));
});
//html($("ul.subjects").html().replace(/_s/g, ""));
// ...
return false;
} );
} );
Try to use hover to implement mouseover and mouseout, like
$("ul.subjects li a").hover(function() {
$(this).css("background-image", "www.google.com");
}, function() {
$(this).css("background-image", "www.google.com");
});
As you can see, just try to use css:background-image to directly change the background image. This would bypass your problem.
Hope this works for you.
.hover() API

returning clicked li class in an ul javascript/jquery

My code (the html page):
<nav>
<ul>
<li id="homeLink">Home</li>
<li id="rekenLink">Rekenmachine</li>
<li id="bakkerLink">Parkeergarage</li>
<li id="garageLink">Bij de bakker</li>
<ul>
</nav>
The javascript/jquery behind it:
$(function () {
$("ul").click(function () {
// here I want to get the clicked id of the li (e.g. bakkerLink)
});
});
How do I do that?
Use the .on() method with signature $(common_parent).on(event_name, filter_selector, event_listener).
Demo: http://jsfiddle.net/gLhbA/
$(function() {
$("ul").on("click", "li", function() {
// here I want to get the clicked id of the li (e.g. bakkerLink)
var id = this.id;
alert(id);
});
});
Another method is to bind the event to li instead of ul:
$(function() {
$("li").click(function() {
// here I want to get the clicked id of the li (e.g. bakkerLink)
var id = this.id;
alert(id);
});
});
Use jQuery on() instead of click and pass li as selector.
$(function() {
$("ul").on('click', 'li', function() {
//Here this will point to the li element being clicked
alert(this.id);
});
});
on() reference - http://api.jquery.com/on/
$(function() {
$("li").click(function() {
alert(this.id);
});
});
edit: jsfiddle link
Handle the click event of the <li> instead of the <ul>.
You can then get this.id.
Use the event's target (The anchor that was clicked) and then grab its parent's id:
$(function() {
$("ul").click(function(e) {
alert(e.target.parentNode.id);
});
});
JSFiddle
here is one of the way to do. Make sure your using the latest jquery file.
$("ul li").on('click', function() {
console.log($(this).attr("id"));
});
You may try
$(function () {
$("li").click(function () {
var id = $(this).attr("id");
alert(id);
});
});
or
$(document).ready( function() {
$("li").click(function () {
var id = $(this).attr("id");
alert(id);
});
});

Adding a click event inside jquery plugin while maintain access to main jquery object

In the below code I have a couple of unordered lists, and in the plugin I am attempting to fire a click event when the li element is clicked. How can I do this inside the plugin and still have access to the main ul jquery object?
See comments in code for further explanation
<ul class="testing">
<li>clicking this should fire a click event</li>
</ul>
<ul class="testing">
<li>clicking this should fire a click event</li>
</ul>
(function($) {
$.fn.myPlugin = function(options) {
return this.each(function() {
//how should I trigger the onClick when the li is clicked???
});
function onClick(){
console.log('you clicked an li');
//I also need access to the main ul element inside here
}
}
})(jQuery);
$(function() {
$('.testing').myPlugin();
});
(function($) {
$.fn.myPlugin = function(options) {
return this.find('li').click(onClick)
function onClick(){
console.log('you clicked an li');
$(this).parent(); // This is the <ul>
}
}
})(jQuery);
$(".testing").myPlugin()
Copy the following example and see if this helps solidify what you are attempting to accomplish.
The proof of getting to the main ul tags are the alerts. You will see I have created 'testing1' and 'testing2' classes. Depending on which li tag is clicked, the user will receive an alert of what class is associated to the parent ul tag of the clicked li tag.
I hope this helps!
<ul class="testing1">
<li>clicking this should fire a click event</li>
</ul>
<ul class="testing2">
<li>clicking this should fire a click event</li>
</ul>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
$('ul')
.on('click', 'li', function() {
$(this).myPlugin();
});
});
$.fn.myPlugin = function(options) {
return this.each(function() {
//how should I trigger the onClick when the li is clicked???
onClick(this);
});
function onClick(jqObj) {
console.log('you clicked an li');
//I also need access to the main ul element inside here
alert($(jqObj).parent('ul').attr('class'));
}
}
</script>

Categories