I have put together some code to make this work, but my Jquery skills are limited, can anyone let me know where i'm going wrong? I assume my syntax is totally incorrect. Thanks in advance for your assistance :)
// jQuery selector to get an element
var query = $('#menu .sub-menu');
// check if element is Visible
var isVisible = query.is(':visible');
if (isVisible === true) {
// element is Visible
$("#menu").click(function(e) {
query.hide();
e.stopPropagation();
} else {
// element is Hidden
}
Your same code is working just wrapped in .ready() method and added }) after e.stopPropagation();. That was the error in your code responsible for your problem You could check that error in the console of your browser.
$(document).ready(function() {
// jQuery selector to get an element
var query = $('#menu .sub-menu');
// check if element is Visible
var isVisible = query.is(':visible');
if (isVisible === true) {
// element is Visible
$("#menu").click(function(e) {
query.hide();
e.stopPropagation();
});
} else {
// element is Hidden
}
});
$(document).ready(function() {
// jQuery selector to get an element
var query = $('#menu .sub-menu');
// check if element is Visible
var isVisible = query.is(':visible');
if (isVisible === true) {
// element is Visible
$("#menu").click(function(e) {
query.hide();
e.stopPropagation();
});
} else {
// element is Hidden
}
});
.sub-menu {
background: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="menu">
Menu
<div class="sub-menu">
Sub Menu
</div>
</div>
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.
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)
I don't quite understand why this isn't working. I'm trying to make it so .on() click will removeClass() .active-title from a list-item; Then addClass() .active-title to the next() list-item.
$(".next").on({
click: function(){
$('.active-title').removeClass(function(){
$(this).next().addClass('active-title');
}
});
});
Demo:
Link to JSFiddles Demo
Final Solution Demo in JSFIDDLES
You have some errors in your code
}
});
});
supposed to be
}); <--- closing of removeClass
} <-- closing for click function
});
Try this
$(".next").on({
click: function () {
// Find the actuve element
var $active = $('.active-title', 'ul');
// If the next element exists the get the element
// otherwise get the first li from the ul
var $nextElem = $active.next('li').length ? $active.next('li')
: $('ul').find('li:first');
// Add and remove class
$active.removeClass('active-title');
$nextElem.addClass('active-title');
}
});
Check Fiddle
I'm developing an app where you can display all monuments and places to be in Ghent (Belgium). There's a fullscreen map on the homepage (programmed in jQuery with Google Maps API v3) and on the lefthand side, there's a list of the items that you can display. Users can choose whether they want the category to be displayed alone, or to be added to the categories that they clicked before.
This is what happens: I load the page, I click a category. Everything works fine. But only if I click spans in the same category. Whenever I click something in another category, firebug throws the error: parent is null (or this.parentNode is null)
I initially tried to do it in jQuery with $(this).parent().html()..., but it gives me a similar error: 'TypeError: parent.html(...) is undefined'.
Here's my code...
HTML
<section class="category transport">
<h1>Transport</h1>
<ul class="clearfix">
<li><span class="category_li">Parkings</span></li>
<li><span class="category_li">Stations</span></li>
</ul>
</section>
JQUERY (HOVER EFFECT)
this is where I add the 'add_on_map' span to the list-items
$('.category ul li').hover(
//mouse-enter
function()
{
if($(this).children('.add_on_map').length == 0)
{
$(this).append(add_marker_sign);
$('.add_on_map').click(showCategory);
}
else
{
$(this).children('.add_on_map').show();
}
},
//mouse-leave
function()
{
$(this).children('.add_on_map').hide();
}
);
JQUERY (CLICK EVENT)
function showCategory(e)
{
var parent = null;
parent = this.parentNode;
var add_marker_sign = '<span class="add_on_map plus"> +</span>';
var remove_marker_sign = '<span class="add_on_map minus"> -</span>';
var element;
var to_removed_marker_sign = parent.innerHTML.replace(add_marker_sign, remove_marker_sign);
var to_add_marker_sign = parent.innerHTML.replace(remove_marker_sign, add_marker_sign);
//User clicks span -> If you click on the word:
// All markers are deleted and the clicked category is added
//So when the clicked item doesn't have the 'add_on_map' class:
if(!$(this).hasClass('add_on_map')){
console.log('remove all markers and add new');
removeAllMarkers();
element = $(this).text().toLowerCase();
$(this).parent().html(to_removed_marker_sign);
$('.category ul li').removeClass('active_subcategory');
addMarkers(element);
$('.category ul li span').click(showCategory);
}
//When clicked item DOES have 'add_on_map' class:
else
{
element = $(this).parent().children('.category_li').text().toLowerCase();
//Check if markers should be ADDED or DELETED
if($(this).hasClass('plus')) {
console.log('add markers');
$(this).parent().html(to_removed_marker_sign);
addMarkers(element);
$('.category ul li span').click(showCategory);
}
if($(this).hasClass('minus')) {
console.log('remove markers');
$(this).parent().html(to_add_marker_sign);
removeMarkers(element);
$('.category ul li span').click(showCategory);
}
}
}
I made a small live demo on jsfiddle!
If anyone can help me with this, I'd be greatful!
Thanks in advance.
Helena S.
I've based on your JSFiddle and come to that solution: http://jsfiddle.net/98gmn/6/ (only the jQuery part has changed)
jQuery
var add_marker_sign = '<span class="add_on_map plus"> +</span>';
var remove_marker_sign = '<span class="add_on_map minus"> -</span>';
$('.category ul li').click(showCategory);
$('.category ul li').hover(
//mouse-enter
function () {
if ($(this).children('.add_on_map').length == 0) {
$(this).append(add_marker_sign);
} else {
$(this).children('.add_on_map').show();
}
},
//mouse-leave
function () {
$(this).children('.add_on_map').hide();
});
function showCategory() {
var element;
var $this = $(this);
var $marker = $this.find('span.add_on_map');
element = $(this).text().toLowerCase();
$this.toggleClass('show_on_map');
$marker.toggleClass('plus').toggleClass('minus');
if($marker.hasClass('plus'))
{
$marker.html(' +');
}
else
{
$marker.html(' -');
}
}
It simplifies the DOM and event manipulation, sets the class "show_on_map" on list elements which are to be show on map.