【问题标题】:knockoutjs and isotope adding new item causing first element to jump around淘汰赛和同位素添加新项目导致第一个元素跳来跳去
【发布时间】:2014-10-03 08:47:15
【问题描述】:

我使用的是knockoutjs和同位素砌体布局,自定义绑定基于代码blog link here,如下图所示:

查看:

<div id="container" class="isotope" data-bind="foreach: bills">
    <div class="item" data-bind="isotope: {container: '#container', itemSelector: '.item'}, style: {height: RandomHeight() + 'px'}">
        <h3 data-bind="text: title"></h3>
        <p>Votes: <span data-bind="text: votes"></span><p>
        <p data-bind="text: desc"></p>
    </div>
</div>

以及 ViewModel 和自定义绑定:

ko.bindingHandlers.isotope = function() {
    var self = this;

    self.init = function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        console.log("init...");
        var value = ko.utils.unwrapObservable(valueAccessor());

        var $container = $(value.container);

        $container.isotope({
            itemSelector: value.itemSelector,
            masonry: {
              columnWidth: 100
            }
        });
    };

    self.update = function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var $el = $(element);
        var value = ko.utils.unwrapObservable(valueAccessor());
        console.log("updating..." + value);
        var $container = $(value.container);
        $container.isotope('appended', $el);
    };

    return {
        init: self.init,
        update: self.update
    }
}();

问题是当我添加一个新项目并将其附加到列表时,第一个项目从左到右跳转。顶部的项目应保持静态定位,只有底部的项目应随机移动,这是发生这种情况的示例屏幕截图(mid-jump_ 位于帖子底部。

请知道我做错了什么?

小提琴在这里:http://jsfiddle.net/g18c/zoohcveh/4/

【问题讨论】:

  • 你想要怎样......你能告诉我你想要什么输出吗?
  • 嗨 Devendra,如果您多次单击“添加”按钮,您将看到第一个项目从左向右跳跃,而它应该保持静态定位...唯一的运动应该是在添加最后一块砖的网格
  • 您使用的是什么浏览器?对我来说,它在 Firefox 中运行良好。或者我没有解决这个问题。
  • 第一个元素在 IE 和 Chrome 中跳来跳去,已经发布了该问题的屏幕截图。
  • 那么问题是第一行应该有两个元素,还是你只想要一个元素但让它保持静止?

标签: javascript jquery knockout.js jquery-isotope knockout-3.0


【解决方案1】:

您的同位素绑定在item,如果您仔细阅读日志,每次添加新项目时,同位素都会再次初始化。这肯定会引起麻烦。

正确的做法是在顶层容器上使用同位素绑定,只初始化一次。 但我们还需要保持(又名扩展)foreach 绑定的行为。

要在添加新项目后触发isotope 的动画,您需要使用foreachafterAdd 回调。在此处阅读更多信息http://knockoutjs.com/documentation/foreach-binding.html

http://jsfiddle.net/zoohcveh/27/

HTML

<div id="container" class="isotope" data-bind="isotope: bills, isotopeOptions: {
                itemSelector: '.item',
                masonry: {
                  columnWidth: 182
                }
            }">
    <div class="item" data-bind="style: {height: RandomHeight() + 'px'}">
        <h3 data-bind="text: title"></h3>
        <p>Votes: <span data-bind="text: votes"></span></p>
        <p data-bind="text: desc"></p>
    </div>
</div>

JS

// use a factory method since most code of 'init' and 'update' are shared.
function initOrUpdate(method) {
    return function(element, valueAccessor, allBindings, viewModel, bindingContext) {

        function isotopeAppend(ele) {
            // runs isotope animation
            console.log('nodeType ' + ele.nodeType);
            // nodeType 3 is Text (the empty space before and after div.item)
            // nodeType 1 is element
            if (ele.nodeType === 1) { // Element type
              console.log("appended isotope");
              $(element).isotope('appended', ele).isotope('layout');
            }
        }

        function attachCallback(valueAccessor) {
            return function() {
                return {
                    data: valueAccessor(),
                    afterAdd: isotopeAppend,
                };
            };
        }

        // extend foreach binding
        ko.bindingHandlers.foreach[method](element,
            attachCallback(valueAccessor), // attach 'afterAdd' callback
            allBindings, viewModel, bindingContext);

        if (method === 'init') {
            console.log('init...');
            var options = allBindings.get('isotopeOptions') || {};
            console.log("options " + JSON.stringify(options));
            $(element).isotope(options);

            ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                $(element).isotope("destroy");
            });
        } else {
            console.log("updating...");
        }
    }
}

ko.bindingHandlers.isotope = {
    init: initOrUpdate('init'),
    update: initOrUpdate('update')
};

更新

做个好公民,为$(element).isotope("destroy");添加dispose回调。

【讨论】:

  • 非常感谢这是完美的,试图理解代码你能否扩展 ko.bindingHandlers.foreach[method]() 这是一个函数数组,它是如何工作的?我现在将奖励赏金:)
  • obj["init"]obj.init 相同。这只是一种从 javascript 对象中获取属性的灵活方法。记住每个 js 对象都是一个哈希。
  • 更新了 dispose 回调的答案。
  • 玩了一段时间的同位素,要让它与淘汰赛一起工作并不容易。 Knockout 想基于模型来控制视图,但是 isotope 没有数据层,它的所有功能都是基于 DOM 的。我还没有找到支持同位素过滤器的干净方法(同位素过滤器基于 css 类,而不是数据)。
  • 我没想到会这样。对于 3 个不同的节点,一个“文本”节点(div 之前的空白区域)、一个“div”节点和另一个“文本”节点(div 之后的空白区域),在添加 3 次之后触发 Knockout。这是在 DOM 标准中定义的,每个空白区域都是 DOM 结构中的一个节点。解决方案是在运行附加同位素之前检查nodeType。更新了答案。
【解决方案2】:

http://jsfiddle.net/zombiesplat/zoohcveh/17/这是我在下面回答的工作版本。

问题似乎是同位素 v2 和基因敲除的某种兼容性问题。我的修复来自于找到这个示例 http://zubinraj.com/demo/isotope-knockout/isotope-knockout-binding.htm 并将您的代码转换为使用淘汰赛 v1.5

HTML

<div>
    <button data-bind="click: addNew">Add New</button>
</div>

<div id="container" class="isotope" data-bind="foreach: bills">
    <div class="item" data-bind="isotope: {container: '#container', itemSelector: '.item'}, style: {height: RandomHeight() + 'px'}">
        <h3 data-bind="text: title"></h3>
        <p>Votes: <span data-bind="text: votes"></span><p>
        <p data-bind="text: desc"></p>
    </div>
</div>

CSS

/* ---- .item ---- */

.item {
  float: left;
  width: 380px;
  background: #0D8;
  border: 2px solid #333;
  border-color: hsla(0, 0%, 0%, 0.7);
}


/* Begin Required Isotope Styles - see http://isotope.metafizzy.co for more info */
 .isotope-item {
    z-index: 2;
}
.isotope-hidden.isotope-item {
    pointer-events: none;
    z-index: 1;
}
/**** Isotope CSS3 transitions ****/
 .isotope, .isotope .isotope-item {
    -webkit-transition-duration: 0.8s;
    -moz-transition-duration: 0.8s;
    -ms-transition-duration: 0.8s;
    -o-transition-duration: 0.8s;
    transition-duration: 0.8s;
}
.isotope {
    -webkit-transition-property: height, width;
    -moz-transition-property: height, width;
    -ms-transition-property: height, width;
    -o-transition-property: height, width;
    transition-property: height, width;
}
.isotope .isotope-item {
    -webkit-transition-property: -webkit-transform, opacity;
    -moz-transition-property: -moz-transform, opacity;
    -ms-transition-property: -ms-transform, opacity;
    -o-transition-property: -o-transform, opacity;
    transition-property: transform, opacity;
}
/**** disabling Isotope CSS3 transitions ****/
 .isotope.no-transition, .isotope.no-transition .isotope-item, .isotope .isotope-item.no-transition {
    -webkit-transition-duration: 0s;
    -moz-transition-duration: 0s;
    -ms-transition-duration: 0s;
    -o-transition-duration: 0s;
    transition-duration: 0s;
}

Dom Ready 上的 Javascript

var $container = $("#container");
var data = [
    {
        title: "Test 1",
        desc: "Some description text goes here",
        votes: 1000,
        category: "catA",
    },
    {
        title: "Test 2",
        desc: "Some description text goes here",
        votes: 2000,
        category: "catB",
    },
    {
        title: "Test 3",
        desc: "Some description text goes here",
        votes: 100,
        category: "catC",
    }
];

function Bill(title, desc, votes, category)
{
    var self = this;
    self.title = ko.observable(title);
    self.desc = ko.observable(desc);
    self.votes = ko.observable(votes);
    self.category = ko.observable(category);
    self.RandomHeight = ko.observable(Math.floor(Math.random() * 200) + 400);
    console.log("Added new bill");
};

function ViewModel() {
    var self = this;

    self.bills = ko.observableArray();

    self.count = 1;

    self.search = function() {
        self.bills.removeAll();
        for(var i=0; i<data.length; i++)
        {
            var bill = new Bill(
                data[i].title,
                data[i].desc,
                data[i].votes,
                data[i].category
            );
            self.bills.push(bill);
        }
    };

    self.addNew = function() {
        for(var i=0; i<data.length; i++)
        {
            var bill = new Bill(
                self.count,
                data[i].desc,
                data[i].votes,
                data[i].category
            );
            self.bills.push(bill);
            self.count++;
        }
    };
};

ko.bindingHandlers.isotope = {
    init : function(element, valueAccessor, allBindings, viewModel, bindingContext) {

    },  
    update : function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var $el = $(element);
        var value = ko.utils.unwrapObservable(valueAccessor());
        console.log("updating..." + value);
        var $container = $(value.container);
        $container.isotope({
            itemSelector: value.itemSelector,
            masonry: {
              columnWidth: 100
            }
        });
        $container.isotope('appended', $el);
    }

};

ko.applyBindings(new ViewModel());

【讨论】:

  • 我能够通过在他完全相同的代码上使用同位素 v2 来复制 zubinraj.com 上示例的问题。
  • 很有趣,知道为什么吗?一直在想我的代码是我的代码
  • 老实说,我不知道为什么。我注意到的是,您可以通过简单地调整容器大小来复制,并且无论我做什么,同位素都会在开始时为 2 个元素留出空间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-24
  • 1970-01-01
  • 2013-07-02
  • 1970-01-01
  • 2017-01-25
  • 2013-06-18
  • 1970-01-01
相关资源
最近更新 更多