我遇到了淘汰赛的beforeRemove回调和CSS过渡问题

时间:2014-08-08 20:43:22

标签: knockout.js

我正在使用foreach绑定和beforeRemove回调,以便为我的列表项的移除设置动画。当我删除之前删除的项目上方的项目时工作正常,但当我尝试删除之前删除的项目下方的项目时,转换无法正常工作。

这是一个说明我的问题的jsfiddle:http://jsfiddle.net/tylerweeres/2szjxzs0/2/

HTML

<h3>Animations</h3>
<div data-bind="foreach: { data: people, beforeRemove: animateOut }">
    <div style="padding: 5px;" class="person">
        <span data-bind="text: name" style="display: inline-block; width: 200px;"></span>
        <button style="padding: 5px 15px;" data-bind="click: $parent.removePerson">Remove</button>
    </div>
</div>

CSS

.person {
    font-family: georgia;
    padding: 5px;
    transition: all 600ms ease-in-out;
    transform-origin: 75px;
}

.person.remove {
    transform: translateX(2000px) rotateZ(540deg);
}

JS

function vm () {
    "use strict";
    var me = this;

    me.people = ko.observableArray([
        { "name": "Irma Olsen" },
        { "name": "Winifred Cabrera" },
        // ...
        { "name": "Lucius Howard" },
        { "name": "Hedda Santana" }
    ]);

    me.removePerson = function (person) {
        me.people.remove(person);
    };

    me.animateOut = function animateOut(node, i, person) {
        // Make sure it's not a text node
        if (node.nodeType == Node.ELEMENT_NODE) {
            node.classList.add("remove");
        }
    }
}

ko.applyBindings(new vm());

3 个答案:

答案 0 :(得分:2)

这是一个奇怪的,但我认为它与疯狂的浏览器渲染逻辑的东西有关。你需要强制重绘不会发生。有一点我们有:

<div class="person remove">
<div class="person">

然后我们去

<div class=person>
<div class=person>

同一个div不再有remove类了 - 当我们添加remove类时,Knockout会删除数据,但是浏览器不知道更新这个div,所以它不会执行转换,这意味着transitionEnd不会触发,也不会删除div。

那么我们如何强制重绘? Obviously by calling node.offsetHeight(一个有效的表达式)。

http://jsfiddle.net/ExplosionPIlls/2szjxzs0/6/

我相信有人可以更有说服力地解释这是如何运作的,以及为什么需要这样做,但至少应该解决你当前的问题。

答案 1 :(得分:2)

来自beforeRemove事件的documentation

  

beforeRemove - 在删除数组项时调用,但是   在删除相应的DOM节点之前。如果你指定一个   beforeRemove回调,然后你有责任删除   DOM节点。

beforeRemove中,如果DOM节点不是文本节点,则只删除所有节点。

因此,您需要使用else添加node.parentElement.removeChild(node);案例:

me.animateOut = function animateOut(node, i, person) {
    // Make sure it's not a text node
    if (node.nodeType == Node.ELEMENT_NODE) {
        node.addEventListener("transitionend", function () {
            this.parentElement.removeChild(this);
        });
        node.classList.add("remove");
    }
    else{
        node.parentElement.removeChild(node);
    }
}

演示JSFiddle

答案 2 :(得分:1)

您的转换在remove发生后结束,因此可能会使渲染变得混乱。

您可以通过通知people集合转换已结束来解决此问题。

me.animateOut = function animateOut(node, i, person) {
    // Make sure it's not a text node
    if (node.nodeType == Node.ELEMENT_NODE) {
        node.addEventListener("transitionend", function () {
            this.parentElement.removeChild(this);
            me.people.notifySubscribers(); // Add this line here
        });
        node.classList.add("remove");
    }
}

Edited Fiddle

请注意,如果您有手动订阅,他们将收到通知回调的undefined参数通知,因此您应该为此做好准备。