d3.js:将数据从父节点传递到子节点onclick事件 [英] d3.js: Passing data from parent node to child node onclick event

查看:229
本文介绍了d3.js:将数据从父节点传递到子节点onclick事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用d3来制作堆叠条形图。



感谢前面的问题我使用parentNode .__ data __。key将与父节点关联的数据绑定到子节点。



数据是一个数组,每个栏都有一个对象(例如likes)。然后每个对象包含一个值的数组,它驱动每个条的单个矩形:

  data = [{
key = 'likes',values = [
{key ='blue-frog',value = 1},
{key ='goodbye',value = 2}
]
} ,{
key ='dislikes,values = [
{key ='blue-frog',value = 3},
{key ='goodbye',value = 4}
]
}]

图表工作正常,数据到子svg属性:

  //创建canvas 
bars = svg.append(g);

//创建单个条形并添加数据
//'likes'绑定到第一个条形,'不喜欢'第二个
bar = bars.selectAll(。 )
.data(data)
.enter()
.append(g);

//每个bar创建矩形并添加数据
//'blue-frog'绑定到第一个矩形等
rect = bar.selectAll(rect )
.data(function(d){return d.values;})
.enter()
.append(rect);

//将父节点信息(例如likes)附加到每个矩形
//根据上面引用的SO问题
rect.attr(metric,function ,i,j){
return rect [j] .parentNode .__ data __。key;
});

这样就允许在每个矩形创建工具提示,到目前为止这么好。



问题是如何将此相同的信息与点击事件相关联,建立:

  rect.on(click,function(d){
return _this.onChartClick(d);
});

//或

rect.on(click,this.onChartClick.bind(this));

这有问题,因为onChartClick方法需要访问绑定数据(d)和图表执行上下文'这个')。如果没有,我只能切换执行上下文,并在onChartClick方法中调用 d3.select(this).attr(metric)



我有另一个想法是传递度量作为一个附加参数,但使用函数(d,i,j)的伎俩在这里似乎不工作,因为它不运行直到

解决方案

您可以建议一个解决方案吗? >你可以使用闭包保持对父数据的引用,如下所示:

  bar.each(function(dbar){ // dbar指绑定到bar的数据
d3.select(this).selectAll(rect)
.on(click,function(drect){// drect数据绑定到rect
console.log(dbar.key); // dbar.key将是'likes'或'dislikes'
});
});

更新:



访问DOM结构中不同级别的方法。连连看!查看此版本的实际版本,并尝试点击.rect divs: http://bl.ocks.org/4235050

  var data = [
{
key:'likes',
values: key:'blue-frog',value:1},{key:'goodbye',value:2}]
},
{
key:'dislikes',
values:[{key:'blue-frog',value:3},{key:'goodbye',value:4}]
}

var chartdivs = d3.select(body)。selectAll(div.chart)
.data([data])//如果要创建多个图表。数据([data1,data2,data3])
.enter()。append(div)
.attr(class,chart)
.style ,500px)
.style(height,400px);

chartdivs.call(chart); // chartdivs是一个或多个图表div的d3.selection。函数图表负责在这些div中创建内容

函数图表(选择){//选择是一个或多个图表div
selection.each(function(d,i){ //为每个chartdiv做以下
var chartdiv = d3.select(this);
var bar = chartdiv.selectAll(。bar)
.data(d)
.enter()。append(div)
.attr(class,bar)
.style(width,100px)
.style height,100px)
.style(background-color,red);

var rect = bar.selectAll(。rect)
。 data(function(d){return d.values;})
.enter()。append(div)
.attr(class,rect)
.text (function(dbar){return d.key;})
.style(background-color,steelblue);

bar.each $ b var bardiv = d3.select(this);
bardiv.selectAll(。rect)
.on(click,function(drect){
d3.select ).call(onclickfunc,bardiv);
});
});

function onclickfunc(rect,bar){//可以访问图表,bar和rect
chartdiv.style(background-color,bar.datum ='likes'?green:gray);
console.log(rect.datum()。key); //将打印'blue-frog'或'goodbye'
}
});
}


I am using d3 to make a stacked bar chart.

Thanks to this previous question I am binding data associated with a parent node to a child node using parentNode.__ data__.key.

The data is an array with one object for each bar (e.g 'likes'). Then each object contains an array of values which drive the individual rectangles per bar:

data =  [{
          key = 'likes', values = [
            {key = 'blue-frog', value = 1}, 
            {key = 'goodbye', value = 2}
          ]
        }, {
          key = 'dislikes, values = [
            {key = 'blue-frog', value = 3},
            {key = 'goodbye', value = 4}
          ]
        }]

The chart is working fine, and so is binding the parent metric data to a child svg attribute:

// Create canvas
bars = svg.append("g");

// Create individual bars, and append data
// 'likes' are bound to first bar, 'dislikes' to second
bar = bars.selectAll(".bar")
        .data(data)        
        .enter()
        .append("g");

// Create rectangles per bar, and append data
// 'blue-frog' is bound to first rectangle, etc.
rect = bar.selectAll("rect")
        .data(function(d) { return d.values;})
        .enter()
        .append("rect");

// Append parent node information (e.g. 'likes') to each rectangle    
// per the SO question referenced above        
rect.attr("metric", function(d, i, j) {
  return rect[j].parentNode.__data__.key;
});

This then allows the creation of tooltips per rectangle which say things like "likes: 2." So far so good.

The problem is how to associate this same information with a click event, building on:

rect.on("click", function(d) {
  return _this.onChartClick(d);
});

// or

rect.on("click", this.onChartClick.bind(this));

It's problematic because the onChartClick method needs access to the bound data (d) and the chart execution context ('this'). If it didn't I could just switch the execution context and call d3.select(this).attr("metric") within the onChartClick method.

Another idea I had was to pass the metric as an additional parameter but the trick of using function(d, i, j) here doesn't seem to work because it isn't run until a click event happens.

Can you suggest a solution?

解决方案

You could use the closure to keep a reference to the parent data like this:

bar.each(function(dbar) {            // dbar refers to the data bound to the bar
  d3.select(this).selectAll("rect")
      .on("click", function(drect) { // drect refers to the data bound to the rect
        console.log(dbar.key);       // dbar.key will be either 'likes' or 'dislikes'
      });
});

update:

See below for various ways to access different levels in your DOM structure. Mix and match! See the live version of this and try to click on the .rect divs: http://bl.ocks.org/4235050

var data =  [
    {
        key: 'likes',
        values: [{ key: 'blue-frog', value: 1 }, { key: 'goodbye', value: 2 }]
    }, 
    {
        key: 'dislikes',
        values: [{ key: 'blue-frog', value: 3 }, { key: 'goodbye', value: 4 }]
    }];

var chartdivs = d3.select("body").selectAll("div.chart")
    .data([data]) // if you want to make multiple charts: .data([data1, data2, data3])
  .enter().append("div")
    .attr("class", "chart")
    .style("width", "500px")
    .style("height", "400px");

chartdivs.call(chart); // chartdivs is a d3.selection of one or more chart divs. The function chart is responsible for creating the contents in those divs

function chart(selection) { // selection is one or more chart divs
  selection.each(function(d,i) { // for each chartdiv do the following
    var chartdiv = d3.select(this);
    var bar = chartdiv.selectAll(".bar")
        .data(d)
      .enter().append("div")
        .attr("class", "bar")
        .style("width", "100px")
        .style("height", "100px")
        .style("background-color", "red");  

    var rect = bar.selectAll(".rect")
        .data(function(d) { return d.values; })
      .enter().append("div")
        .attr("class", "rect")
        .text(function(d) { return d.key; })
        .style("background-color", "steelblue");

    bar.each(function(dbar) {
      var bardiv = d3.select(this);
      bardiv.selectAll(".rect")
          .on("click", function(drect) { 
            d3.select(this).call(onclickfunc, bardiv);
          });
    });

    function onclickfunc(rect, bar) { // has access to chart, bar, and rect
      chartdiv.style("background-color", bar.datum().key === 'likes' ? "green" : "grey");
      console.log(rect.datum().key); // will print either 'blue-frog' or 'goodbye'
    }
  });
}

这篇关于d3.js:将数据从父节点传递到子节点onclick事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆