Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery show() fail with last(), after() and "blind" effect

I'm getting a bug from jQuery 1.10.2, the last one, and I would know if anybody have any (other) solution for this issue.

My script create multiple DIV blocks (named items) from one model (item model), add the current after the last one and display it with a "blind" effect.

Here is the code but you can also test it online at this link.

<div id="item_model" style="display: none;" class="item">MODEL</div>
<button class="addBtn">Add 5 items</button>

<script>
$(".addBtn").click(function() {
    for( var i=0; i<5; i++ ) {
        // Clone model
        var p = $("#item_model").clone(true, true);

        // Modify item
        p.removeAttr("id");
        p.text("ITEM n°"+(i+1));

        // Add item to the DOM
        $(".item").last().after(p);

        // Show item
        $(p).show("blind");
        //$(p).show();
    }
});
</script>

The issue is the same with :last and insertAfter().

The logic:

  • First item is well displayed and its effect occurred (or not, another bug ? but the time elapses)
  • During an effect animation, the element is outsourced ou replaced.
  • The next items are inserted out of the DOM (event if after() should insert it in the DOM), so there are not in the page.

This behavior is an error from jQuery but I have to overcome this problem.

The solutions i know:

  • Don't use any effect.
  • Use a container and append().
  • Use slow effect instead of blind. (Thanks to A. Wolff)
  • Add elements to the DOM and AFTER, show all. (Thanks to A. Wolff)

Thanks to all for your contribution.

like image 676
Loenix Avatar asked Sep 23 '13 08:09

Loenix


2 Answers

This snippet fixes it:

$(".addBtn").click(function () {
    var p = $("#item_model").clone(true),
        tmp = $();
    p.removeAttr("id");
    for (var i = 0; i < 5; i++) {
        tmp = tmp.add(p.clone(true).text("ITEM n°" + (i + 1)));
    }
    $(".item").last().after(tmp);
    tmp.show("blind");
});

DEMO

like image 108
A. Wolff Avatar answered Nov 13 '22 18:11

A. Wolff


I found a rational (if intricate) explanation for your bug.

The root cause : you are using a jquery-ui effect (not a basic jquery effect), and you add elements after last inserted item, before the animation has finished.
The gotcha : jquery-ui uses wrappers during its animations, and if a wrapper is already present, it reuses it.

Here is the detailed walkthrough :

  • When animating the first item : for the duration of the effect, the item is wrapped in a div with class ui-effects-wrapper, and this wrapper div is animated to give the blind effect
  • When adding the second item : adding it after "last" (<- in this case : the first item) actually adds it inside the wrapper.
  • When animating the second item: jquery-ui reuses the existing wrapper (shortcut in createWrapper, see below)
  • Same goes for items 3-5
  • When first animation ends, the wrapper element is removed and replaced whith the first animated elements. The other elements (remember : they were attached as children of this wrapper) end up dangling with no parent.

Relevant pieces of code :
jquery-ui code : snippet 1 - blind() function
jquery-ui code : snippet 2 - createWrapper() inner function
jquery-ui code : snippet 3 - blind() code when animation completes

I don't think this should be considered as a jquery-ui bug - your use case is, IMHO, pretty isolate, and I wouldn't imagine what "correcting" this would trigger elsewhere.

Solutions :

  • slideDown will work (fiddle - it animates the element, no wrapper there)
  • add a <span id="beacon"></span> item and insert new elements before #beacon
  • A. Wolff's solution
  • as you already found out, .append() on a common container
like image 23
LeGEC Avatar answered Nov 13 '22 17:11

LeGEC