交替的行颜色,如果有些是不可见的

时间:2015-09-24 15:49:56

标签: javascript jquery html css knockout.js

我有一个包含由foreach绑定生成的行的表。

与'通常'KO表的唯一区别在于,对于行的动态过滤,我在visible元素上使用tr绑定,该绑定绑定到使用某些{的值的函数{1}}来判断当前行是否应该可见:

observable

我正在使用这种方法,因为它在性能方面比使用<table> <thead> ... </thead> <tbody data-bind="foreach: unfilteredItems"> <tr data-bind="visible: $root.rowFilter($data)"> ... </tr> </tbody> </table> 绑定的集合的传统操作要好得多,并且导致不断插入\删除DOM节点。

这里唯一的问题是没有用于交替行颜色的纯CSS解决方案。 foreach节点保留在DOM中,tr选择器赢了“如果某些:nth-child()不可见,则无法正常工作。

所以,我不得不坚持使用jQuery解决方案:

tr

function stripeVisibleRows(tableElem) { var $visibleRows = $(tableElem).find('tr:visible'); $visibleRows.filter(':odd').css('background-color', '#EEEAE7'); $visibleRows.filter(':even').css('background-color', '#DED7D1'); }; 使用的任何可观察对象触发更新visible绑定时,如何在执行所有行的rowFilter绑定后,如何在Knockout中调用此函数?吗

我已尝试visiblesubscribe取决于computed功能:

rowFilter

,编写像这样的自定义绑定:

self.rowFiltering = ko.computed(function () {
        return self.rowFilter();
    })
    .subscribe(function () {
        tableHelpers.stripeVisibleRows('.tbl');
    });

及其用法:

// Stripes visible tr elements.
// Must be applied to a table element. 
// Must be bound to the same expression as used to change visibility of the tr elements.
ko.bindingHandlers.stripeVisibleRows = {
    update: function (element, valueAccessor) {
        // Read provided binding value to establish dependencies tracking
        ko.utils.unwrapObservable(valueAccessor());

        tableHelpers.stripeVisibleRows(element);
    },
    after: ['visible']
};

但是使用这两种方法我的jQuery条带化函数在应用<table data-bind="stripeVisibleRows: $root.rowFilter()"> <thead> ... </thead> <tbody data-bind="foreach: unfilteredItems"> <tr data-bind="visible: $root.rowFilter($data)"> ... </tr> </tbody> </table> 绑定之前被调用,因此无法正常工作。

有人可以建议如何实现我想要做的事情吗?
也许我甚至需要改变过滤和条带化行的整个方法,但它应该没有插入\删除DOM节点,并尽可能干净和可重复使用。

5 个答案:

答案 0 :(得分:2)

您可以订阅rowFilter的更改,然后将通话的执行推迟到stripeVisibleRows,以确保淘汰赛使用setTimeout(...,0) pattern更新所有内容:

self.rowFilter.subscribe(function() {
    setTimeout(function() {
        tableHelpers.stripeVisibleRows('.tbl');
    }, 0);
});

如果您不介意从DOM中删除/添加元素,则可以使用if binding代替visible - 这会从DOM中完全删除标记,而不是只是隐藏它:

<table>
    <thead>
        ...
    </thead>
    <tbody data-bind="foreach: unfilteredItems">
        <tr data-bind="if: $root.rowFilter($data)">
            ...
        </tr>
    </tbody>
</table>

答案 1 :(得分:2)

你必须在幕后做所有事情,并通过功能公开它。条带化可以使用CSS类完成,因此您不必担心使用jQuery。

由于数据项不会发生变化,因此它们只是一个不可观察的数组。在幕后,我们有一个基于rowFilter的计算器,它访问一些可观察的。 foreach调用函数来查看计算中与当前项对应的项,以设置可见性和类。

在此示例中,您将按字母数过滤元素。

&#13;
&#13;
var tracker;

vm = {
  letters: ko.observable(3),
  unfilteredItems: ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten'],
  visible: function(idx) {
    return tracker()[idx].visible;
  },
  odd: function(idx) {
    return tracker()[idx].odd;
  }
};

function rowFilter(item) {
  return item.length === vm.letters();
}

tracker = ko.pureComputed(function() {
  var visibleCount = 0;
  var result = ko.utils.arrayMap(vm.unfilteredItems, function(item) {
    var visible = rowFilter(item);
    if (visible) ++visibleCount;
    return {
      visible: visible,
      odd: visibleCount % 2
    };
  });
  return result;
});

ko.applyBindings(vm);
&#13;
tr {
  background-color: lightgreen;
}
tr.odd {
  background-color: yellow;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="value:letters, options:[3,4,5]"></select>
<table border="1">
  <tbody data-bind="foreach: unfilteredItems">
    <tr data-bind="visible: $root.visible($index()) , css: {odd:$root.odd($index())} ">
      <td data-bind="text:$data"></td>
    </tr>
  </tbody>
</table>
&#13;
&#13;
&#13;

答案 2 :(得分:1)

而不是在表格渲染后计算所有行条纹(即:使用stripeVisibleRows),而是将当前循环$index传递给单独的$root函数,并让它确定当前条带对于该索引的作用。例如:

HTML:

<style>
  .On { background-color: #CCC }
  .Off { background-color: #FFF }
</style>

<table id='test'>
    <thead>
        ...
    </thead>
    <tbody data-bind="foreach: unfilteredItems">
        <tr data-bind="visible: IsVisible, css: $root.rowColour($index)">
            <td data-bind='text: Text'></td>
        </tr>
    </tbody>
</table>

使用Javascript:

var vmItem = function(visible, text) {
    this.IsVisible = visible;
  this.Text = text;
}

var vm = {
    unfilteredItems: [
        new vmItem(true, "Some"),
        new vmItem(true, "Data"),
        new vmItem(false, "Not Shown"),
        new vmItem(true, "Shown"),
        new vmItem(true, "To"),
        new vmItem(false, "Blah!"),
        new vmItem(true, "The User")
    ],
    rowColour: function(index) {
    var toggle = true;
        for(var c = 0; c < index(); c++) {
      // if the row at this index is visible, flip the row toggle
      if (this.unfilteredItems[c].IsVisible) toggle = !toggle;
    }
    return toggle ? "On" : "Off";
    }
};

ko.applyBindings(vm, $('#test')[0]);

Example CodePen

答案 3 :(得分:1)

使用@ JamesThorpe关于setTimeout(...,0) pattern的建议,我设法让我的问题中的自定义绑定正常工作:

// Stripes visible tr elements.
// Must be applied to a table element. 
// Must be bound to the same expression as used to change visibility of the tr elements.
ko.bindingHandlers.stripeVisibleRows = {
    update: function (element, valueAccessor) {
        // Read provided binding value to establish dependencies tracking
        ko.utils.unwrapObservable(valueAccessor());

        setTimeout(function () {
            tableHelpers.stripeVisibleRows(element);
        }, 0);
    }
};

function stripeVisibleRows(tableElem) {
    var $visibleRows = $(tableElem).find('tr:visible');

    $visibleRows.filter(':odd').css('background-color', '#EEEAE7');
    $visibleRows.filter(':even').css('background-color', '#DED7D1');
};

我像这样使用这个绑定:

<table data-bind="stripeVisibleRows: $root.rowFilter()">
    <thead>
        ...
    </thead>
    <tbody data-bind="foreach: unfilteredItems">
        <tr data-bind="visible: $root.rowFilter($data)">
            ...
        </tr>
    </tbody>
</table>

这是性能和可重用性方面的最佳解决方案我到目前为止已动态过滤表格及其行的替代颜色。

记住两件事:

  1. 您必须将stripeVisibleRows绑定绑定到同一表达式 当您用于tr s visibility绑定以保持过滤和 交替同步。他们之间唯一的区别就是你 不要通过$data绑定中的stripeVisibleRows 表达
  2. 在当前的实现中,您必须应用 stripeVisibleRows绑定到table元素。

答案 4 :(得分:1)

要实现完整的淘汰赛解决方案,您需要在行本身中公开可见性和条带化,并在视图模型中计算它们。

请记住,javascript是动态的,因此您可以轻松地向行添加新属性。

由于您没有显示您的视图模型,我将解释您如何做到这一点,并包含一个示例视图模型来执行此操作:

  1. 获取每一行,并添加一个可观察或计算的observable(取决于您的viewmodel)来控制行可见性,如下所示:row.visible = ko.computed(function() { /*your code here*/});
  2. 在每一行中,为条带添加一个observable,如下所示:row.visible = ko.observable(/*your initial value*/);
  3. 创建一个更新每行条带的函数
  4. 为每行visible可观察的订阅新函数,并使用它来更新每行中的条带
  5. 注意:为了避免在计算中使用循环,可以使用像lodash这样的库来简化数组/集合操作。

    您可以在this fiddle;

    中看到此解决方案

    HTML:

    <ul data-bind="foreach: rows">
        <li data-bind="text: val(), visible: visible, attr:{class: striping}">
        </li>
    </ul>
    Show only rows that contain:
    <input type="text" data-bind="value: mustContain"/>       
    

    Javascript(它使用lodash。请参见底部的注释):

    var Vm = function(_rows) {
        var self = this;
        // filter condition on view model
        self.mustContain = ko.observable('');
        // rows array
        self.rows = _rows;
        // this will update striping
        var updateStriping = function() {
            var visibleRows = _.filter(rows,function(r) {
                return r.visible();
            });
            _.forEach(visibleRows, function(r,i) {
                r.striping(i % 2 ? 'odd' : 'even');
            });
        };
        _.forEach(self.rows, function(row) {
            // make observable version of value
            row.val = ko.observable(row.value);
            // add visibility to each row
            row.visible = ko.computed(function() {
                return row.val().match(self.mustContain());
            });
            // add striping to each row
            row.striping = ko.observable('');
            // subscribe visible change
            row.visible.subscribe(updateStriping);
        });
        updateStriping();
        return self;
    };
    
    var  rows = [
        { value: 'alpha' },
        { value: 'beta' },
        { value: 'gamma' },
        { value: 'delta' },
        { value: 'epsilon' },
        { value: 'zeta' },
        { value: 'eta' },
        { value: 'theta' },
        { value: 'iota' },
        { value: 'kappa' },
        { value: 'lambda' },
        { value: 'mu' },
        { value: 'nu' },
        { value: 'xi' },
        { value: 'omicron' },
        { value: 'pi' },
        { value: 'ro' },
        { value: 'sigma' },
        { value: 'tau' },
        { value: 'upsilon' },
        { value: 'phi' },
        { value: 'chi' },
        { value: 'psi' },
        { value: 'omega' }];
    
    var vm = new Vm(rows);
    
    ko.applyBindings(vm);
    

    和CSS:

    .odd { background-color: silver; }
    .even {}
    

    注意:此代码使用attr绑定来设置条带化类。您通常会使用css代替。

    关于lodash和EcmaScript 5的注释(感谢Alexander Abakumov的评论):most modern browsers support ES5,它提供了一些与lodash相同的功能。在这种情况下,您可以删除lodash,并使用原生filterforEach以这种方式更改代码:

                var visibleRows = _.filter(rows,function(r) { // Line 9
                var visibleRows = rows.filter(function(r) {
    
            _.forEach(visibleRows, function(r,i) { // Line 12
            visibleRows.forEach(function(r,i) {
    
        _.forEach(self.rows, function(row) { // Line 16
        self.rows.forEach(function(row) {
    

    This is the fiddle with theses changes