Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS transition not working after element is appended

I have encountered an issue with CSS transitions and before I try something else, I want to understand what the problem is.

There are 3 boxes in a container and a "next" button. The goal is for the next box top appear on top and to fade in when the "next" button is pressed. The box is positioned on top by appending it to the container, so that it is added as the last element and thus visible on top, and should fade in through css transition.

The problem is that the css transition does not seem to work after the box was appended. The css transition works well if tested on a box element that is not appended.

Fiddle here, code below:

HTML:

<div class="container">
    <div class="box red"></div>
    <div class="box blue"></div>
    <div class="box green"></div>
</div>

<div id="next">Next</div>

JS:

var container = $(".container");

// Save the current list order
var list = container.children(".box");
// The current index
var current = 0;

// Put the first on top
container.append(list[0]);

function next() {
    // Figure out what is the index of the next box
    if (current + 1 < list.length) current++;
    else current = 0;

    // Save it in a variable
    var target = $(list[current]);

    // Put it on top
    container.append(target);

    // Hide it and then fade it in
    target.css("opacity", 0).css("transition", "opacity 1000ms ease-out").css("opacity", 1);

    // The fading in is not working
}

$("#next").click(next);

Update:

A basic solution to this problem was to call offset() on the target after setting the opacity to 0 and before setting the transition css:

target.css("opacity", 0);
target.offset();
target.css("transition", "opacity 1000ms ease-out").css("opacity", 1);

Updated version of the above fiddle here

like image 924
jsgroove Avatar asked Feb 01 '13 20:02

jsgroove


2 Answers

The "list" variable is a jQuery object, but the elements you pull out of it as "target" are not jQuery objects - they're the DOM nodes. Thus your calls to ".css()" are failing (which is reported in the error console for me).

Once you've fixed that, then the next thing is the issue of how the browser deals with a sequence of CSS updates. It's not clear to me what exactly I'm seeing (from Firefox 18 on Linux), but I think the basic issue is that because no layout reflow is done between the changes, the net effect is that the styles are "collapsed" so that there's no net change.

In this update to the fiddle I took a different approach. I put the transition rules in the "box" class, and then added a "prefade" class:

.prefade {
  transition-duration: 0;
  opacity: 0;
}

Then, instead of messing with the element style, I add "prefade" before appending, and then trigger a layout update by asking for the element's offset. Then I can remove the "prefade" class, and the box fades in.

target.addClass('prefade');
// Put it on top
container.append(target);

var p = target.offset();

target.removeClass('prefade'); 

I don't know whether that's the "right" way to do things. edit — to make it work in Chrome, the "transition" properties need to be repeated with the -webkit- prefix.

like image 93
Pointy Avatar answered Oct 11 '22 14:10

Pointy


Is there any particular reason to use CSS transitions, when you could use jQuery fades instead and have your code work on browsers that don't support CSS transitions?

var $container = $(".container");

function next() {
    var $next = $container.children().first();
    $next.hide().appendTo($container).fadeIn();
}

$("#next").click(next);

Note that you can avoid a lot of the state you're maintaining merely by taking the first element from the container and moving it to the back - the DOM maintains your state for you!

See http://jsfiddle.net/alnitak/w6y3B/

like image 40
Alnitak Avatar answered Oct 11 '22 15:10

Alnitak