jQuery . toggleClass() is not working as expected

Asked

Viewed 469 times

2

I have this code but I don’t understand why the method .toggleClass() is not functioning as expected to change the opacity of the element:

Example in Jsfiddle

HTML

<div id="admin">
    <p><br/>Admin</p>
    <form method="POST" action="loginAdmin.php">Username:
        <br/>
        <input type="text" name="username" />
        <br />Password:
        <br/>
        <input type="password" name="password" />
        <br />
        <div id="button">
            <button id="btn" name="send" type="submit" value="enviar">Login</button>
        </div>
    </form>
</div>

CSS

#admin {
    position: absolute;
    margin-top: 140px;
    right: 200px;
    width: 224px;
    z-index: 1;
    height: 140px;
}
#admin p {
    font-family: Lato-Light;
    float: right;
    cursor: pointer;
    color: blue;
    bottom: 0;
    right: 15px;
    position: absolute;
    font-size: 11px;
    opacity: 0.3;
}
.adminPvisible {
    opacity:1;
}
#admin p:hover {
    opacity: 1;
}
#admin form {
    display: none;
    font-family:Lato-Light;
    font-size: 11px;
    margin:35px 0 0 100px;
}
#btn {
    display: none;
    font-size: 12px;
    margin: 5px 0 0 0;
    font-family: Lato-Regular;
}
#admin input {
    width: 120px;
    font-size: 12px;
    margin: 0;
    height: 20px;
}

jQuery

$(document).ready(function () {
    $('#admin > p').click(function () {
        $(this).toggleClass('adminPvisible');
        var right = $('#admin > p').css('right') == '135px' ? '0' : '135px';
        $('#admin > p').animate({
            right: right,
            width: '50px'
        });
        $('#admin > form, #btn').stop(true).slideToggle();        
    })
})

3 answers

3

What is happening is not a jQuery fault, but a CSS function.

It is organized in order of specificity, rules with higher priority are applied even if declared before those with lower specificity. A scoring rule applies:

  • Styles from the attribute style are worth 1000 points, ids (#admin), 100 points, classes (.adminPvisible), 10 points, and tags(p) has minimum priority, 1 point.
  • Pseudo-classes, pseudo-elements, @media and similar do not influence the score.
  • At the end the scores are summed, or, if they are rules marked with !important ,become of top priority. If multiple rules fit in the same situation wins the one defined by style or the last.

To solve the problem you have some options:

  • (recommended) Never use Ids in CSS: because they have such a high specificity they usually cannot be overwritten.
  • When declaring a rule that will override a rule defined by an ID include it together: .adminPvisible flipped #admin > p.adminPvisible.
  • (not recommended) use !important.

More information: MDN | Smashingmagazine - Demonstration: Jsfiddle

1

Basically what was missing was just to put !important in the of .adminPvisible thus it forces the opacity to be 1 while the class is in the element.

In the example you posted the .toggleClass() works perfectly, you just need to say that the class .adminPvisible is more important than the #admin p

jsfiddle

  • In case it adds the ! Mportant is unnecessary and should usually be avoided. He just needs to specify better the rules to be applied, as said in Gustavo Rodrigues' reply

1


As already referenced in other answers, jQuery .toggleClass() (English) is working, the problem is the CSS class has a lower priority than the settings given previously via #admin p.

In short, when passing styles to the selector #admin p you’re being very strict, and when a class uncompromising is assigned, browser will not respect it to override settings.

Two simple ways to solve your question:

  1. Classes for each definition

    Instead of using a class to override the definition of an element, if this definition is mutable, the most practical is to have two classes, each with the desired state and switch between them:

    Example in Jsfiddle

    CSS

    .adminPnearInvisible {
        opacity: 0.3;
    }
    .adminPvisible {
        opacity:1;
    }
    

    HTML

    <div id="admin">
        <p class="adminPnearInvisible"><br/>Admin</p>
        ...
    

    jQuery

    ...
    $(this).toggleClass('adminPvisible adminPnearInvisible');
    ...
    

    What we’re doing is using the .toggleClass() (English) to toggle two classes. As it was in the element the class .adminPnearInvisible, by switching between it and the .adminPvisible, we are effectively removing one and adding another, thus assigning to the element the style that each contains without having to worry about the "base" styles of the element.

  2. Strictly defining the style to be subscribed to

    You can choose to define the two opacity states with the same accuracy, which will tell the browser that comes in 2nd place if applicable:

    Example in Jsfiddle

    CSS

    We get to have your class .adminPvisible with the same rigor as the styles of the element where you define the initial opacity. As you already have the value in :hover, I am putting the two together to avoid duplicating statements in the style sheet:

    #admin p:hover,
    #admin p.adminPvisible{
        opacity: 1;
    }
    

    HTML

    No changes to your initial...

    jQuery

    No changes to your initial...


Unrelated but can be useful is a slight optimization of your code to reduce it and at the same time avoid recurring queries to the DOM to locate the same element:

Example in Jsfiddle

$('#admin > p').click(function () {
    var $this = $(this);
    $this.toggleClass('adminPvisible').animate({
        right: $this.css('right') == '135px' ? '0' : '135px'
    });
    $('#admin > form, #btn').stop(true).slideToggle();        
});

Explanation

// colocar elemento em cache para não andarmos sempre à procura dele
var $this = $(this);

// jQuery permite encadeamento dos métodos, por isso numa linha podes
// fazer a alternância da classe e chamar a animação
$this.toggleClass('adminPvisible').animate(...

// podes ter a verificação do valor e atribuição do novo para a animação
// na declaração do valor pretendido para animar o right
right: $this.css('right') == '135px' ? '0' : '135px'

And I passed the width of 50px for the style sheet as it is not being manipulated, that is, you are saying that from the first click the element is 50 pixels wide.

  • Very obnoxious once again Zuul, very clear and direct to the question, you clarified more than a doubt. Cheers

Browser other questions tagged

You are not signed in. Login or sign up in order to post.