0

I'm having trouble figuring out why my CSS transition isn't firing when I programmatically remove a class from an element.

Essentially, I am trying to create a progressively enhanced, infinitely-scrolling carousel using pure Javascript.

The basic idea is that as the user clicks to scroll either left or right, the first or last element is plucked from the parent ul element, and either prepended or appended to the proper location, depending on the scroll-direction of the user's click.

Here is the relevant code:

HTML:

<div id="scroller">
    <ul>
        <li>
            <a>
                <img src="...">
            </a>
        </li>
        ..... more li elements
        <li>
            <a>
                <img src="...">
            </a>
        </li>
    </ul>   
</div>

CSS:

#scroller {
    position: absolute;
    left: 0em;
    height: 8em;
    width: 400em;
}
#scroller ul {
    list-style: none;
}
#scroller ul li {
    overflow: hidden;
    float: left;
    height: 8em;
    width: 8em;
    transition: width 1s ease;
}
#scroller ul li.hide {
    width: 0em;
}
#scroller ul li a img {
    width: 8em;
}

JS (scroll right click event, for example):

/** Remove the element from the end of the list, add the hide class */
var node = this.list.removeChild(this.items[(this.items.length - 1)]);
/** Add the hide class to the node */
node.className += ' hide';
/** Insert the node at the beginning of the scroller */
this.list.insertBefore(node, this.items[0]);
/** Remove the hide class to trigger the transition animation */
node.className = node.className.replace('hide', '');

Everything is working well, in terms of the items being shifted around the ul correctly, so that is not the problem.

The issue is that the CSS transition is not being applied when the width of the li elements are changed by removing the "hide" class.

I had hoped to create a smooth scrolling effect in browsers than can support the CSS transition.

Thanks in advance for not suggesting that I use a JS library! :)

ineedhelp
  • 227
  • 1
  • 15
  • 1
    Can you put up a code snippet (Ctrl+M), or a http://jsfiddle.net so we can work with it? – LcSalazar Oct 14 '14 at 20:01
  • 2
    I'm pretty sure you can't do what you're trying without giving the browser some time to repaint in between. All of your code above is within one execution cycle, which means when the screen is repainted, the browser doesn't care that you added the class then removed it - all it cares about is the final state. You'll probably need to use `setTimeout` to stagger the changes to the UI – Ian Oct 14 '14 at 20:03
  • @Ian Great, thanks for the tip, I will test it out and report back shortly. If anyone else has another idea in the meantime feel free to chime in. – ineedhelp Oct 14 '14 at 20:09
  • 1
    @Ian You were right! Thanks, that solved it, though now I realize that it's an aesthetically unfit approach. Thanks anyway though this was driving me nuts and I learned a bit more about js in the process. – ineedhelp Oct 14 '14 at 20:29

1 Answers1

1

Use a combo of setTimeout and the transitionend event.

Look here for more info on transitionend: CSS3 transition events

/** Remove the element from the end of the list, add the hide class */
one = document.getElementById('one');
two = document.getElementById('two');
list = document.getElementById('list');

/** Add the hide class to the node */
two.addEventListener('transitionend', end, false);
setTimeout(function(){
  two.className += ' hide';
}, 0)

function end(){
  /** Insert the node at the beginning of the scroller */
  list.insertBefore(two, one);
  /** Remove the hide class to trigger the transition animation */
  setTimeout(function(){
    two.className = two.className.replace('hide', '');
  }, 0)
}
#scroller {
    position: absolute;
    left: 0em;
    height: 8em;
    width: 400em;
}

#scroller ul {
    list-style: none;
}

#scroller ul li {
    overflow: hidden;
    float: left;
    height: 8em;
    width: 8em;
    transition: width 1s ease;
}

#scroller ul li.hide {
    width: 0em;
}

#scroller ul li a {
    width: 8em;
  background-color:red;
}
<div id="scroller">
    <ul id="list">
        <li id="one">
            <a>
              One
            </a>
        </li>
        <li id="two">
            <a>
              Two
            </a>
        </li>
    </ul>   
</div>
Community
  • 1
  • 1
bmceldowney
  • 2,307
  • 13
  • 19