D3 散点图:工具提示出现在初始加载时,但在使用下拉列表更新过滤器后消失

D3 Scatterplot: Tooltip appears on initial load, but disappears after filter updated using dropdown

在此 D3 代码中,我绘制了一个散点图,并根据下拉选择筛选数据。我还为此散点图添加了工具提示。在初始加载时,我看到工具提示工作正常。但是,在选择下拉框后,图形会更新,此后工具提示将停止工作。

我也尝试在更新函数中添加 .on mouseover 事件,但这也不起作用,实际上导致 SelectAll 选项仅显示一组数据的问题。感谢任何帮助我指出代码问题的帮助。 我在更新功能中对此进行了注释,以便图表显示我的意图。

<!DOCTYPE html>
<html lang="en"> 
<head>
<meta charset="utf-8">
<title>MathsVsEnglishScore</title>
<script src="https://d3js.org/d3.v4.js"></script>
<style>
        body {
            font: 15px sans-serif;
        }

        .axis path,
        .axis line {
            fill: none;
            stroke: #000;
            shape-rendering: crispEdges;
        }

        .dot {
            stroke: none;
        }
        .tooltip {
            position: absolute;
            font-size: 12px;
            width:  auto;
            height: auto;
            pointer-events: none;
            background-color: white;
        }
         </style>
</head>

<body>
<!-- Initialize a select button -->
<p><font size = "5px"><b>Analysis of Maths And English Score</b></font></p>
<p><b><font color = "172E8E">Gender: <select id="selectButton"></select></font></b></p>
<!-- Create a div where the graph will take place -->

<div id="my_dataviz"></div>
<a href='indexNew.html'><b>Back</b></a>;
<!-- Color Scale -->
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>

<script>


// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 60},
    width = 460 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var canvas  = d3.select("#my_dataviz")
  .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom);
var svg = canvas
  .append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

//Read the data
d3.csv("https://raw.githubusercontent.com/aknemani/test-repo/master/exams100.csv", function(data) {

    // List of groups (here I have one group per column)
    var allGroup = d3.map(data, function(d){return(d.gender)}).keys().sort()
allGroup.push('SelectAll')
    // add the options to the button
    d3.select("#selectButton")
      .selectAll('myOptions')
        .data(allGroup)
      .enter()
        .append('option')
      .text(function (d) { return d; }) // text showed in the menu
      .attr("value", function (d) { return d; }) // corresponding value returned by the button

    // A color scale: one color for each group
    var myColor = d3.scaleOrdinal()
      .domain(allGroup)
      .range(d3.schemeSet2);

    // Add X axis --> it is a date format
    var x = d3.scaleLinear()
      .domain([0, 100])
      .range([ 0, width]);
    svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).ticks(10));

    // Add Y axis
    var y = d3.scaleLinear()
      .domain([0, 200])
      .range([height, 0]);
    svg.append("g")
      .call(d3.axisLeft(y));

            svg.append("text")             
        .attr("transform", "translate(" + (width/2) + " ," + (height+30) + ")")
        .style("text-anchor", "middle")
        .text("Maths Score");

        svg.append("text")             
        .attr("transform", "translate(" + (-25) + " ," + (height/2) + ") rotate(-90)")
        .style("text-anchor", "middle")
        .text("Reading And Writing Score");

            var tooltip = d3.select("#my_dataviz").append("div")
                  .attr("class", "tooltip")
                  .style("opacity", 0);

            // tooltip mouseover event handler
              var tipMouseover = function(d) {
                  var color = "red";//colorScale(d.manufacturer);
                  var html  = d.gender + "<br/>" +
                              "<b>" + d.mathScore + "</b> Math, <br/>" 
                              + "<b>" + d.readingScore + "</b> Reading, <br/>"
                              + "<b>" + d.writingScore + "</b> Writing";

                  tooltip.html(html)
                      .style("left", (d3.event.pageX + 15) + "px")
                      .style("top", (d3.event.pageY - 28) + "px")
                    .transition()
                      .duration(200)
                      .style("opacity", .9)
              };
              // tooltip mouseout event handler
              var tipMouseout = function(d) {
                  tooltip.transition()
                      .duration(300) // ms
                      .style("opacity", 0); // don't care about position!
              };                  

    // Initialize line with first group of the list
    var dataFilter = data.filter(function(d){return d.gender==allGroup[0]})
    var scatterGraph = svg
      .append('g')
        .selectAll('circle')
        .data(dataFilter)
        .enter()
        .append('circle')
        .attr('cx', function(d){return x(d.mathScore);})
        .attr('cy', function(d){return y(Number(d.readingScore)+Number(d.writingScore));})
        .attr('r', function(d){return Number(5);})
        .style("fill", myColor(allGroup[0]))
        .style("stroke", "black")
        .on("mouseover", tipMouseover)
        .on("mouseout", tipMouseout);

    // A function that update the chart
    function update(selectedGroup) {
        var tooltip = d3.select("#my_dataviz").append("div")
                  .attr("class", "tooltip")
                  .style("opacity", 0);

        var tipMouseover = function(d) {
                  var color = "red";//colorScale(d.manufacturer);
                  var html  = d.gender + "<br/>" +
                              "<b>" + d.mathScore + "</b> Math, <b/>" 
                              + d.writingScore + "</b> Writing";

                  tooltip.html(html)
                      .style("left", (d3.event.pageX + 15) + "px")
                      .style("top", (d3.event.pageY - 28) + "px")
                    .transition()
                      .duration(200)
                      .style("opacity", .9)
              };
              // tooltip mouseout event handler
              var tipMouseout = function(d) {
                  tooltip.transition()
                      .duration(300) // ms
                      .style("opacity", 0); // don't care about position!
              };
        var dataFilter = data.filter(function(d){return d.gender==selectedGroup})
        svg
        .append('g')
        .selectAll('circle')
        .data(dataFilter)
        .enter()
        .append('circle')
        .attr('cx', function(d){return 0;})
        .attr('r', function(d){return Number(0);})
        .attr('cy', function(d){return height;})
        .style("fill", myColor(selectedGroup))  
        .style("stroke", 'black')
        .transition().duration(1000)
        .attr('cx', function(d){return x(d.mathScore);})
        .attr("r", function(d,i) { return 5; })
        .attr('cy', function(d){return y(Number(d.readingScore)+Number(d.writingScore));})
        //.on("mouseover", tipMouseover)
        //.on("mouseout", tipMouseout);

    }

    // When the button is changed, run the updateChart function
    d3.select("#selectButton").on("change", function(d) {
        canvas.selectAll('circle').remove();
        var selectedOption = d3.select(this).property("value")
        if(selectedOption == 'SelectAll') 
        {
            allGroup.forEach(function(element) {
            console.log(element);
            selectedOption = element;
            update(selectedOption);
                });
        }
        // run the updateChart function with this selected option
        else
        {
           update(selectedOption);
        }

    })

})
</script>
</body>
</html>

update 函数上,您对 mouseovermouseout 事件进行了注释,如果您删除注释,则会引发错误,这是因为您正在尝试添加事件过渡或更好的听众,您应该在设置过渡之前输入这些行。像这样:

     ...
    .attr('cy', function(d){return height;})
    .style("fill", myColor(selectedGroup))  
    .style("stroke", 'black')
    .on("mouseover", tipMouseover)
    .on("mouseout", tipMouseout)
    .transition().duration(1000)
    ...

但是当我尝试你的代码时,我注意到你的代码中有几个问题。

首先,您在每次调用该函数时都会执行 append,因此每次调用 update 函数时都会创建工具提示 div。

function update(selectedGroup) {
    var tooltip = d3.select("#my_dataviz").append("div")
              .attr("class", "tooltip")
              .style("opacity", 0);

我会在可视化初始化中添加它并保留对它的引用,然后使用该引用 show/hide 它。

tipMouseovertipMouseout 函数也是如此,你声明了它们两次,尽量不要创建重复代码,所以如果你创建一次,并保留对它们的引用。

最后,您还重复了追加圆圈的代码,您可以使用通用函数对其进行改进。我根据建议创建了 this jsfiddle,希望对您有所帮助。