单击表格,更新行,将鼠标悬停在行上,更新表格

时间:2014-01-30 23:16:39

标签: d3.js

我是D3的新手,但到目前为止还很喜欢它。但我知道我的解决方案缺乏优雅。

我正在尝试使用2个控件,一个表格和一个图表来显示表格单元格所代表的数据。如果单击表格上的单元格,则应突出显示关联的行。如果将鼠标悬停在某一行上,则关联表格单元格将更改颜色。最终会有第三个控件显示特定于该cel的详细数据。不幸的是,如果我使用静态调用更新函数,我只能设法完成这项工作。如果我试着变得聪明而充满活力,整个事情就会破裂。

我试图尽量减少我的例子。 click table->更新行有效,因为对SelectData()的更新所有内容的调用都使用常量数据。然而,线上的鼠标悬停不起作用。最终我需要表格更具活力,但就目前而言,我该如何解决这个问题呢?

<!DOCTYPE html>
<meta charset="utf-8">
<style>
    .lineDefault {
        fill: none;
        stroke: red;
        stroke-width: 1.5px;
        stroke-dasharray: 4,4;
    }
    .axis path,
    .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
    }
</style>
<body>
    <div id="wrap">
        <table>
            <tr>
                <td id="dataBlock" onclick="SelectData(0)">1</td>
                <td id="dataBlock" onclick="SelectData(1)">2</td>
            </tr>
            <tr>
                <td id="dataBlock" onclick="SelectData(2)">3</td>
                <td id="dataBlock" onclick="SelectData(3)">4</td>
            </tr>
        </table>
        <div>
            <svg class="chart"></svg>
        </div>
    </div>
    <script src="http://d3js.org/d3.v3.min.js"></script>
<script>
    var width = 600, height = 600;
    var maxx = 100,
        maxy = 100;

    var linedata = {};
    linedata[0] = [[0, 50 ],[ 50, 60 ],[100, 100]];
    linedata[1] = [[0, 40 ],[ 40, 40 ],[100, 90 ]];
    linedata[2] = [[0, 20 ],[ 50, 30 ],[100, 90 ]];
    linedata[3] = [[0, 0  ],[ 60, 30 ],[100, 30 ]];
    var activeElement = 0;
    var graphlines = {};
    var numlines = 0;

    chart = d3.select(".chart").attr("viewBox", "0 0 600 600").append("g");

    var x = d3.scale.linear().domain([0, maxx]).range([0, width]);

    var y = d3.scale.linear().domain([0, maxy]).range([height, 0]);

    var xAxis = d3.svg.axis().scale(x).orient("bottom");

    var yAxis = d3.svg.axis().scale(y).orient("left");

    var line = d3.svg.line()
        .x(function(d) { return x(d[0]); })
        .y(function(d) { return y(d[1]); });

    for (var i = 0; i < 4; i++) {
        graphlines[i] = chart
            .append("path")
            .datum(linedata[i])
            .attr("class", "lineDefault")
            .attr("id", "linedata")
            .attr("d", line)
            .on("mouseover", SelectData(i));
        numlines++;
    }

    function SelectData(n) {
        d3.selectAll("td").transition()
            .style("background-color", function(d, i) {
                return i == n ? "#c99" : "#fff";
            });
        activeElement = n;
        for (var i = 0; i<numlines; i++) {
            if (i == n) {
                graphlines[i]
                    .style("stroke-dasharray", "1,0")
                    .transition()
                    .style("stroke-width", "3")
                    .style("stroke", "steelblue");
            } else {
                graphlines[i]
                    .style("stroke-dasharray", "4,4")
                    .transition()
                    .style("stroke-width", "1.5")
                    .style("stroke", "red");
            }
        }
    }

</script>

桌面上的鼠标点击会影响线条,线条上的鼠标悬停不会影响表格。此外,我会接受任何对我的优雅和指针的指责。

2 个答案:

答案 0 :(得分:3)

首先,你的鼠标悬停方法无效的原因是因为在这一行:

        .on("mouseover", SelectData(i));

您在调用.on()方法时调用SelectData方法因此,当您完成初始化时,您的最后一项已被选中,但没有任何功能正在侦听鼠标悬停事件。您想要的是将函数 name 传递给.on()方法。如果你使用两个参数(通常命名为di),那么d3会自动将索引值作为第二个参数传递。我写了一篇相当长的讨论,关于最近将函数作为参数传递给其他人you may find it useful

此外,你真的没有利用d3选择结构,它可以在一次调用中完成你正在使用的所有事情for - 循环。我建议花点时间read through some tutorials了解d3选项的工作原理。

现在,问你的主要问题:

选择数据与被点击元素数据匹配的元素的常用解决方案是根据数据中的唯一ID为所有元素提供类。然后,您可以轻松选择与给定数据对象关联的所有元素。如果用户选择了d.name=Sue的对象,并且您使用该名称将所有元素初始化为类名,那么您可以d3.select("path.Sue")d3.select("td.Sue")找到正确的对象。< / p>

您的示例数据似乎没有任何唯一的数据ID值,只有索引号。所以你甚至不需要一个独特的类,你可以使用nth-of-type(i+1) CSS selector。 (它是i+1,因为CSS计数从1开始,而数据计数从0开始。)

但是,我建议您使用特殊的CSS类来应用所有突出显示样式。这样,很容易选择当前突出显示的值来删除该类,而不必循环遍历所有内容以测试它是否匹配。您可以使用CSS过渡来在类更改后转换样式。

这是您的代码的小提琴版本,整理使用d3并使用上述方法突出显示数据。

http://fiddle.jshell.net/g8z5h/

我还没有实现您的表的实时版本,但我建议您阅读tutorial on nested selections以了解它的工作原理。

我确实为每个数据块提供了自己的行,这样nth-of-type()选择器才能正常工作(元素的CSS编号只有在它们都是兄弟节点时才有效,它们不能分成多行的表数据元素)。如果您需要使用原始布局,则必须根据索引值为元素提供类名,并使用它们进行选择。我还将click事件绑定移动到代码中,因为JSFiddle将其所有代码包装在窗口加载事件中,因此SelectData函数在其外部不可见。

答案 1 :(得分:1)

我一直在指这个,因为我想使用d3在angular2中实现相同的功能。这是我的代码......

  private drawLine() {
    this.line = d3Shape.line()
        .y((d: any) => this.y(d.rank))
        .defined(function(d : any) { return d.rank})
        .x((d: any) => this.x(d.year));

   var color = function(i) {
        var colors = ["#35559C","#D9469C","#70D45B","#915ABB","#FF7C26","#50C5F6","#ECBE4B"];
        return colors[i % colors.length];
    };

    var i = 0;
    var j=1;
    for(let d of this.lineData){
        this.g.append('path')
            .attr('d', this.line(d.values))
            .attr("class", "line")
            .attr("stroke", color(i))
            .attr("transform", "translate(0,"+this.margin.top+")")
            .attr("id","row-"+j)
            .on("mouseover",function(){
                d3.select(this)
                    .attr("class","line1");
                d3.select("tr#"+this.id)
                    .style("background-color","gainsboro")
                //console.log( d3.select("tr#"+this.id))
            })
            .on('mouseout', function(){
                d3.select(this)
                    .attr("class","line");
                d3.select("tr#"+this.id)
                    .style("background-color","")
            });

            j++;
            i++;
            if(i > 9) {
                i = 0;
            }
  }
}
private mouseover(event){
d3.select(event.currentTarget)
    .style("background-color","gainsboro")
d3.select("path#"+event.currentTarget.id)
    .attr("class","line1");

}

private mouseout(event){
d3.select(event.currentTarget)
    .style("background-color","")
d3.select("path#"+event.currentTarget.id)
    .attr("class","line");
}

创建第i行时,我为其指定了id,并为表中的每一行指定了相同的id。

<tr *ngFor="let object of jsonFile; let i = index" id="{{'row-'+[i+1]}}" 
     (mouseover)="mouseover($event)" (mouseout)="mouseout($event)">
    <td *ngFor="let column of columnHeaders" [ngStyle]="{'width': 
           column.length}">
        <div *ngIf="isString(column.columnType) == true">
          {{object[column.id] | trim}}
        </div>
        <div *ngIf="isString(column.columnType) == false">
          {{object[column.id]}}
        </div>
    </td>
  </tr>

并在表格行中调用mouseover和mouseout函数。 如果我在某处错了,我想提出一些建议.. :)