jQuery .toggleClass () is not working as expected

2

I have this code but I do not understand why the .toggleClass() method is not working 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();        
    })
})
    
asked by anonymous 27.02.2014 / 11:02

3 answers

1

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

In short, when you pass styles for the #admin p selector you are being very strict, and when a non-strict class is assigned, the browser will not respect it to override the settings.

Two simple ways to solve your question:

  • Classes for each definition

    Instead of using a class to override the definition of an element, if this definition is changeable, the most practical thing is to have two classes, each with the desired state and to 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 .toggleClass() (English) to switch two classes. Since it was in the element the .adminPnearInvisible class, when switching between it and .adminPvisible , we are effectively removing one and adding another, thus assigning the element the style that each one contains without having to worry about the "base" of the element.

  • Strictly define the style to subscribe

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

    Example in JSFiddle

    CSS

    We get to have your class .adminPvisible with the same rigor as the element styles where you define the initial opacity. Since you already have the value in :hover , I'm putting together two things 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 find 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 to the style sheet because it is not being manipulated, ie you are saying that from the first click the element is 50 pixels wide.

        
    27.02.2014 / 14:33
    3

    What's happening is not a jQuery flaw, but a CSS function.

    It is organized in order of specificity, rules with higher priority are applied even if declared before those of less specificity. Applies a scoring rule:

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

    To solve the problem you have a few options:

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

    More information: MDN | SmashingMagazine - Demo: JSFiddle

        
    27.02.2014 / 12:46
    1

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

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

    jsfiddle

        
    27.02.2014 / 11:59