D3.js Multi-Series Chart with Y value tracking

后端 未结 1 566
独厮守ぢ
独厮守ぢ 2020-12-12 00:42

Working solution: Right now I\'m working on styling and on solving some of the issues regarding my problem with creating chart consisting of multiple-data s

1条回答
  •  醉梦人生
    2020-12-12 00:58

    You've got all the basic code there, you just need to get it all to run at the same time.

    The first problem is that you're setting two different "mousemove" event handlers on the same elements. Unless you use namespaces to distinguish them, the second function just replaces the first, so your first function is never getting called. Rather than creating two event handlers with different namespaces, it's much easier to just put all your event-handling code into one function.

    The second problem is that you only have one "focus" element, and so even if you did run both functions to set the two different tooltip contents and position, only the second version would be displayed, because it just replaces the first.

    So to recap, you need to: create a tooltip/focus element for each path, and then have one event-handling function that sets all the values and positions according to the appropriate column of your data file.

    To keep the code concise, and to allow you to quickly switch from two lines to four or more, I'm going to suggest that you create the focus elements as a data-joined selection, where the data is an array of column names:

    var columnNames = d3.keys( data[0] ) //grab the key values from your first data row
                                         //these are the same as your column names
                      .slice(1); //remove the first column name (`date`);
    
    var focus = svg.selectAll("g")
        .data(columnNames)
      .enter().append("g") //create one  for each columnName
        .attr("class", "focus")
        .style("display", "none");
    
    focus.append("circle") //add a circle to each
        .attr("r", 4.5);
    
    focus.append("text")  //add a text field to each
        .attr("x", 9)
        .attr("dy", ".35em");
    

    Now, when you show or hide focus in the mouseover/mouseout events, it will show or hide all the tooltips:

    svg.append("rect")
      .attr("class", "overlay")
      .attr("width", width)
      .attr("height", height)
      .on("mouseover", function() { focus.style("display", null); })
      .on("mouseout", function() { focus.style("display", "none"); })
      .on("mousemove", mousemove);
    

    But what should you do in your mousemove function? The first part, figuring out the nearest x-value (date) is the same. But then you have to set the text and position of each focus tooltip according to the values in the correct column. You can do this because each focus element has a column name bound to it as a data object, and d3 will pass that data object as the first parameter to any function you pass in to a d3 method:

    function mousemove() {
      var x0 = x.invert(d3.mouse(this)[0]),
        i = bisectDate(data, x0, 1),
        d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.date > d1.date - x0 ? d1 : d0; 
           //d is now the data row for the date closest to the mouse position
    
       focus.attr("transform", function(columnName){
             return "translate(" + x( d.date ) + "," + y( d[ columnName ] ) + ")";
       });
       focus.select("text").text(function(columnName){
             //because you didn't explictly set any data on the 
             //elements, each one inherits the data from the focus 
    
             return formatCurrency(d[ columnName ]);
       });
    }
    

    By the way, you can use this same structure -- use the column names as data, and then use that name in a function to grab the correct data value -- to create all your lines with the same code, without having to create separate data arrays. Let me know if you have difficulty implementing that.

    0 讨论(0)
提交回复
热议问题