Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A smart toggle class in jQuery

I am trying to implement a script to set different class name on a specific element…

Let's suppose the dom looks like this:

<body class='pre-existing-class-name'>

If I make

smartToogle('body', 'new-class');
// the dom should look like this
// <body class='pre-existing-class-name new-class'>
smartToogle('body', 'new-class-2');
// the dom should look like this
// <body class='pre-existing-class-name new-class-2'>

I did the following code but it does not work:

var smartToogle = function (element, newClassName) {
    var oldClassName;
    var $element = $(element);

    $element.addClass(newClassName);
    if (oldClassName !== newClassName) {
        $element.removeClass(oldClassName);
    }
    oldClassName = newClassName;
};

Requirements:
1) I am using query
2) I would like to pass just one class name, the new one.


Solution:
The following code works but I do not like it because it uses global variable.
Any hint to fix it?

function myToggle(newClassName) {
    if (window.oldClassName) {
         $('body').toggleClass(window.oldClassName);
    }
    window.oldClassName = newClassName;
    $('body').toggleClass(newClassName);
}
like image 648
Lorraine Bernard Avatar asked Oct 16 '12 08:10

Lorraine Bernard


5 Answers

You can use data attribute for the element, that is accessible using

$(element).data(attrib_name)

Just a small change is required in your method

function myToggle(newClassName) {
    if (window.oldClassName) {
         $('body').toggleClass(window.oldClassName);
    }
    window.oldClassName = newClassName;
    $('body').toggleClass(newClassName);
}

can be replaced with

function myToggle(element, newClassName) {
    if ($(element).data('oldClassName')) {
         $(element).toggleClass($(element).data('oldClassName'));
    }
    $(element).data('oldClassName', newClassName)
    $(element).toggleClass(newClassName);
}

Hope this solves it for you.

like image 126
Ankur Avatar answered Nov 17 '22 16:11

Ankur


Update:

There is one thing you need to understand. If you want two different behaviors you don't need 2 different classes for the change in behavior. One is enough, because you can change the behavior based on weither the class is on or off.

Let's say I want my element to have a red hover event in one way. And want it to have a blue hover event the other way with CSS. Then this is the way to go:

$('#toggle').click(function(){
    $('.normal').each(function(){
        $(this).toggleClass('active');
    });        
});

JSFiddle Demo

Here we use a button to toggle all the divs and change their CSS behavior, looks easy now right?

However if you need to toggle Javascript/jQuery events as well this won't do. In that case you will need to use 3 other methods to manage this; .on(), .off(), and .hasClass().

$('#toggle').click(function(){
    $('.normal').each(function(){
        if($(this).hasClass('active')){
            $(this).off('click');
        } else {
            $(this).on('click', function(){
                alert('You are clicking on an active div.');
            });
        }
        $(this).toggleClass('active');
    });        
});

JSFiddle Demo 2

As you can see we have added an if statement. If the element has the .active class we turn .off() the .click(). And if there isn't an active class we turn the .click() .on(). Under the if statement we always toggle the .active class. So this doesn't have to be placed inside the if statement.

I hope this clears everything up for you, good luck!


Old Answer:

It is better to use .toggleClass() here.

Use a first class on the element for the default properties and a second like .active for example for the interaction.

Also, using a .on('click', function(){}) bind will make you able to add interaction that will be bound instantly once the element is toggled.

like image 22
Sem Avatar answered Nov 17 '22 17:11

Sem


Here's a fiddle: http://jsfiddle.net/NCwmF/2/

I little jQuery plugin for that. Removes the current smart class (if any) and adds the new smart class. If called without parameter className the current smart class gets only removed.

$.fn.smartToggle = function (className) {

    var dataId = 'smartToggle';

    return this.each(function () {

        var $el = $(this);

        $el
            .removeClass($el.data(dataId) || '')
            .addClass(className)
            .data(dataId, className);
    });
};

​use it like every other jQuery method:

$('body').smartToggle('myClass');
like image 33
lrsjng Avatar answered Nov 17 '22 16:11

lrsjng


NEW, SIMPLER ANSWER Works similar to before, with 2 additions: 1.) works if there is no class initially and 2.) works if other functions change the elements class in between calls. I also changed the function name so it doesn't interfere with jQuerys native toggleClass.

$.fn.fancyToggleClass = function(new_class) {
return this.each(function() {

    // get the last class this function added (if exists) or false (if not)
    var $this = $(this),
        toggled_class = $this.data('toggled-class') || false;       

    // if we dont have an original class, then set it based on current class
    if (toggled_class) {
        $this.removeClass(toggled_class);
    }

    // add new class and store as data, 
    // which we check for next time function is called
    $this.addClass(new_class).data('toggled-class', new_class);

    // alert the class, just as a check to make sure everything worked! 
    // remove this for production, or switch to console.log
    alert('element class: ' + $this.attr('class'));

});
}

updated fiddle: http://jsfiddle.net/facultymatt/xSvFC/3/

OLD ANSWER I would suggest storing the original class in the elements data attribute. Then, your function can check if this data is set, and if so clear the elements class adding the original class from the elements data and also the new class you passed in the function.

If data is not set, the function will store the current class as data the first time it runs.

Check out this fiddle for a working example with comments: http://jsfiddle.net/facultymatt/xSvFC/

here is the code. It's a jquery function so it can be called on any element (and is chainable too!)

$.fn.toggleClass = function(new_class) {
    return this.each(function() {

        // cache selector for this
        $this = $(this);

        // get original class (if exists) or false (if not)
        var original_class = $this.data('original-class') || false;       

        // if we dont have an original class, then set it based on current class
        if (!original_class) {

            original_class = $this.attr('class');
            $this.data('original-class', original_class);

        // we do have an original class, so we know user is now trying to add class 
        // here we clear the class, add the original class, and add the new class           
        } else {

            // assign the original class, and new class, 
            // and a space to keep the classes from becoming one 
            $this.attr('class', original_class + ' ' + new_class);

        }

        // alert the class, just as a check to make sure everything worked! 
        // remove this for production, or switch to console.log
        alert('element class: ' + $this.attr('class'));

    });
}

Hope this helps!

like image 2
facultymatt Avatar answered Nov 17 '22 16:11

facultymatt


To avoid a global variable you can use data attribute as @ankur writes. Here is a working solution for your problem:

function myToggle(element, newClassName) { 
    if (!$(element).data('baseclassname')) { 
        $(element).data('baseclassname', $(element).attr('class')); 
    } 
    $(element)
        .attr('class', $(element).data('baseclassname'))
        .addClass(newClassName); 
}
like image 1
crackmigg Avatar answered Nov 17 '22 17:11

crackmigg