How to prevent div:hover transitions from being disabled by javascript div style attributes alterations

481 Views Asked by At

Here's the jsfiddle that I've been trying to debug:

http://jsfiddle.net/Neoheurist/x57fkzng/

HTML

<div id="birthday">Surprise Party</div>
<p></p>
<button onclick="blue()">Blue</button>
<button onclick="red()">Red</button>

<script>
function blue()
{
    element=document.getElementById("birthday");

    element.innerHTML="Happy";

    element.style.background="blue";
    element.style.width="150px";
    element.style.opacity="1.0";
    element.style.transition="width 2s,background 15s,opacity 2s";
}

function red()
{
    element=document.getElementById("birthday");

    element.innerHTML="Birthday";

    element.style.background="red";
    element.style.width="300px";
    element.style.opacity="0.0";
    element.style.transition="width 2s,background 4s,opacity 6s";
}
</script>

CSS

div
{
    width:100px;
    height:50px;
    background:blue;
    transition:width 2s,opacity 2s,background 15s;
}

div:hover
{
    width:200px;
    background:green;
    opacity:0.25;
    transition-timing-function:linear;
    transition:width 2s,background 4s,opacity 6s;
}

Questions:

Why does clicking a button disable div:hover?

How can I prevent this from happening?

3

There are 3 best solutions below

3
On

You could make the div:hover attributes all !important like so:

div:hover{
   width:200px !important;
}

However I've heard you'd want to avoid !important if possible, but this does what you want...

3
On

It's because HTML style attributes override element selectors in a css file. Any property set directly in an HTML style attribute will automatically be used over any property set in any css selector declaration.

Style attributes are much more specific than tag selectors (that's why they aren't recommended for use in fact).

According to the inspector in webkit this also includes the :hover state, so any inline style will stop a hover state from working.

You could use important, tempting as it might be, but that's not a good idea, because it takes the current problem with specificity that you're having and amplifies it even further, leading to a specificity nightmare. The only way to over-ride !important is with more !important, further down the document, or by using more specific selectors (like IDs) or longer chains of selectors and !important and so on, you can see how this can be horrible to maintain. Also any js that adds style to the HTML directly won't work either.

The best solution is to use javascript to add and remove css classes to trigger your changes. This will solve your problem as all your classes will have manageable specificity.

#birthday.blue {
    background: blue;
    width: 150px;
    opacity: 1.0;
    transition: width 2s,background 15s,opacity 2s;
}

#birthday.red {    
    background: red;
    width: 300px;
    opacity: 0.0;
    transition: width 2s,background 4s,opacity 6s;
}

Then make sure the hover state is defined for all the combinations, so any class will :hover. This is not possible with inline styles.

#birthday:hover,
#birthday.blue:hover,
#birthday.red:hover
{
    width: 200px;
    background: green;
    opacity: 0.2;
    transition-timing-function: linear;
    transition: width 2s,background 4s,opacity 6s;
}

I've put together a jsfiddle that demos this. I've used JQuery, for the sake of getting a demo together quickly and their addClass() method is great. Good effort to use pure js, it's a good habit to get into; this question will elaborate on how to add and remove classes in javascript

Plus; as an added bonus, you'll also have all your style in your style file and all your functionality in your javascript, which is better separation of concerns and makes the site styling DRYer and easier to re-use elsewhere in the project (you don't have styles stuck is js that you can't easily add elsewhere, which you copy instead, then try to change in one place and not the other ... we all do it, or our colleagues do!).

[Seen as I've brought up the subject of specificity you might also be interested to know that IDs are also pretty bad for that and unnecessary in style files]

3
On

You can also use

document.addEventListener('DOMContentLoaded', function () {
    var storedStyles;
    var elem = document.getElementById("birthday");
    elem.addEventListener('mouseenter', function () {
        if (elem.hasAttribute('style')) {
            storedStyles = elem.attributes.style.value;
            elem.removeAttribute('style');
        }
    });

    elem.addEventListener('mouseleave', function () {
        if (storedStyles) {
            elem.setAttribute('style', storedStyles);
        } else {
            storedStyles = null;
        }
    });
 });

and clear all styles on mouse enter restoring hover styles precendence and setting back your inline styles on mouse leave