【问题标题】:d3 adding data attribute conditionallyd3有条件地添加数据属性
【发布时间】:2013-08-14 19:54:41
【问题描述】:

我正在创建一个带有 d3 的表,供FooTable jquery plugin 使用,这需要在标题行中有一些 data- 属性。但并非所有列都具有所有数据属性,并且想知道是否有办法做到这一点。

通过添加所有可能的数据属性并保留一些空白,这种方法有点工作,但我确信这不是一个好的做法。

var th = d3.select(selection).select("thead").selectAll("th")
            .data(colspec)
            .enter().append("th")
            .text(function(d) { return d["data-name"]; })
            .attr("data-class", function(d) {
                if ("data-class" in d) {
                    return d["data-class"];
                } else {
                    return "";
                }
            })
            .attr("data-hide", function(d) {
                if ("data-hide" in d) {
                    return d["data-hide"];
                } else {
                    return "";
                }
            })
            .attr("data-ignore", function(d) {
                if ("data-ignore" in d) {
                    return d["data-ignore"];
                } else {
                    return "";
                }
            })

       etc.

colspec 示例:

[{"data-name": "username"}, {"data-name": "Date Joined", "data-hide": "true"}]

目前获得:

  <th data-class="" data-hide="true" data-ignore="" data-type="">Joined</th>

想要

   <th  data-hide="true" >Joined</th>

有什么建议吗?

【问题讨论】:

  • +1 只是为了向我介绍 FooTable
  • 所以如果我的问题是直截了当的,当某个单元格没有数据时,您希望 显示表格的 缩小 版本领域,对吧?
  • 不要尝试变得那么聪明(还!)在上面添加了评论。
  • 是的,只是我自己才发现脚下!

标签: d3.js


【解决方案1】:

您可以使用.filter() 函数仅对您需要为其设置属性的选择子集进行操作,例如

var th = d3.select(selection).select("thead").selectAll("th")
        .data(colspec)
        .enter().append("th")
        .text(function(d) { return d["data-name"]; });
th.filter(function(d) { return ("data-class" in d); })
        .attr("data-class", function(d) {
            return d["data-class"];
        });

【讨论】:

  • 我希望避免不得不循环遍历元素六次(数据属性的数量),尽管过滤器的想法会使其不那么繁琐。没有办法一次内联吗?
  • 另一种方法是简单地在每个元素上调用一个函数来进行切换和属性设置。这样,您只需触摸每个元素一次。
【解决方案2】:

似乎是.each() 的好人选:

var th = d3.select(selection).select("thead").selectAll("th")
        .data(colspec)
    .enter().append("th")
        .text(function(d) { return d["data-name"]; })
        // now address each item individually
        .each(function(d) {
            var header = d3.select(this);
            // loop through the keys - this assumes no extra data
            d3.keys(d).forEach(function(key) {
                if (key != "data-name")
                    header.attr(key, d[key]);
            });
        });

我经常使用.each,因为拥有每个项目的范围比尝试为每个项目找出一堆属性更有意义。

对于属性的简短列表,特别是如果您担心对象中的额外数据,可能更容易遍历所需的键而不是所有内容:

        .each(function(d) {
            var header = d3.select(this);
            ['data-class', 'data-hide', 'data-ignore'].forEach(function(key) {
                if (key in d)
                    header.attr(key, d[key]);
            });
        });

【讨论】:

  • 为什么在 .each 中需要一个 forEach?
  • .each 循环遍历数据对象(行),而在此示例中 forEach 循环遍历要应用于每个项目(列)的属性。
【解决方案3】:

您不需要调用 each() 或 filter()... attr() 函数将在内部为您执行此操作。只需使用函数而不是值调用它,并让该函数为每个数据返回所需的值,如果特定数据不需要该属性,则返回 null,如下所示:

...
.attr('data-class', function(d) {
    return 'data-class' in d ? d['data-class'] : null;
});

如果您的函数返回 null,则不会添加该属性。您甚至可以通过向函数提供属性名称映射来将多个属性组合到一个调用中,如下所示:

...
.attr({
    'data-class': function(d) {
        return 'data-class' in d ? d['data-class'] : null;
    }, 
    'data-hide': function(d) {
        return 'data-hide' in d ? d['data-hide'] : null;
    },
    'data-ignore': function(d) {
        return 'data-ignore' in d ? d['data-ignore'] : null;
    }
});

或者如果你像我一样不想输入太多,你可以将属性名称列表减少到适当的映射中:

...
.attr(['data-class', 'data-hide', 'data-ignore'].reduce(function(result, attr) {
    result[attr] = function(d) {
        return attr in d ? d[attr] : null;
    }
    return result;
}, {}));

【讨论】:

  • +1 用于指定不添加空属性
  • 如果基于数据的渲染有细微的变化,这很有效。如果你想基于某个数据键渲染一个完全不同的对象,我认为 .each() 更简洁。
【解决方案4】:

清洁剂就是使用过滤器

.filter(d => !!d["data-class"]) // filter only data with the "data-class" property
.attr("data-class", d => d["data-class"])

【讨论】:

    【解决方案5】:

    投票最多的解决方案是完美的,因为.attr(a,b)b 为空时作为条件工作,

     d3chain.attr('data-class', d=>'data-class' in d ? d['data-class'] : null );
    

    但这个解决方案不是一般的,对其他链接方法无效,除了使用.each().filter.call()。一般最简单的就是call()

    .call(condFunc,param)

    假设param是一个全局变量,在条件中用作参数,而g是一个用于返回值的全局对象。

      // inconditional
      d3chain.attr(param, g[param])
    
      // conditional case using globals
      d3chain.call( s => { if (g[param]) s.attr(param,g[param]) })
    
      // conditional case passing the parameter
      d3chain.call( (s,p) => {
         if (g[p]) s.attr(p, g[p])
      }, param)
    

    .each(d => condFunc)

    典型用途:

     d3chain.each( d=> {
         if (param && d) d3.select(this).attr(d, g[param])
     })
    

    有关详细示例,请参阅 @nrabinowitz 答案。

    .filter(d=>condFunc).etc

    典型用途:

     d3chain.filter( d=> param in d ).attr(param, d=> g[param])
    

    有关详细示例,请参阅@LarsKotthoff 答案。

    【讨论】:

      猜你喜欢
      • 2018-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-15
      • 2016-09-26
      • 2017-08-10
      • 2015-07-04
      • 2021-05-01
      相关资源
      最近更新 更多