javascript - 如何在悬停时显示每个元素的值

标签 javascript html d3.js frontend

我是 D3.js 的新手,并且创建了一个图表来显示一些卖家的销售历史记录。

下表显示了一年中的总销售结果,图表中显示了每月的销售变化,当我将鼠标悬停在每个圆圈下方时,我想显示相应月份的销售金额。

但它始终显示相同的值(这些值显示在图表顶部)。

    //#region Criando Tabela de Vendedores
    var newMapVendedores = [
        {
            "vendnm": "CHARNECA",
            "Vendas_Ano": 236009.2299999998,
            "Vendas_Ant": 282753.77999999997
        },
        {
            "vendnm": "JOÃO LUIS",
            "Vendas_Ano": 257733.04999999996,
            "Vendas_Ant": 332119.31
        },
    ]

    $(document).ready(function () {
        $("#tableVendedores").append('<tfoot><th></th><th></th><th></th></tfoot>');
        var table = $('#tableVendedores').DataTable({
            "data": newMapVendedores,
            "columns": [
                { "data": "vendnm", title: 'Vendedor' },
                { "data": "Vendas_Ano", title: 'Vendas Ano' },
                { "data": "Vendas_Ant", title: 'Vendas Ant' },
            ],
            "bLengthChange": false,
            "bPaginate": false,
            "bFilter": false,
            "info": false,
        });

        $('#tableVendedores').on('click', 'tr', function () {
            var data = table.row(this).data();
        });
    });

    //#endregion

    //#region GRÁFICO         

    var codes = ["VENDAS_ANO", "VENDAS_ANT"];
    $('span.values').html(codes.join(', '));

    modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;

    var data = null;
    var filtered_data = null;

    var margin = { top: 30, right: 20, bottom: 50, left: 50 };
    var width = 600 - margin.left - margin.right;
    var height = 350 - margin.top - margin.bottom;
    var duration = 250;

    var lineOpacity = "0.25";
    var lineOpacityHover = "0.85";
    var otherLinesOpacityHover = "0.1";
    var lineStroke = "1.5px";
    var lineStrokeHover = "2.5px";

    var circleOpacity = '0.85';
    var circleOpacityOnLineHover = "0.25";
    var circleRadius = 3;
    var circleRadiusHover = 6;

    var month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

    var x = d3.scaleBand().range([0, width]).domain(month).padding(1);
    var y = d3.scaleLinear().range([height, 0]).domain([0, 65000]);

    var color = d3.scaleOrdinal(d3.schemeCategory10);

    var xAxis = d3.axisBottom(x).ticks(12);
    var yAxis = d3.axisLeft(y);

    var svg = d3.select("#line-chart-container")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var g = null;

    var line = d3.line()
        .x(function (d) {
            return x(d.month);
        })
        .y(function (d) {
            return y(d.value);
        });
    // .curve(d3.curveBasis);

    let lines = svg.append('g')
        .attr('class', 'lines');

    var mouseG = svg.append("g") // this the black vertical line to folow mouse
        .attr("class", "mouse-over-effects");

    mouseG.append("path")
        .attr("class", "mouse-line")
        .style("stroke", "black")
        .style("stroke-width", "1px")
        .style("opacity", "0");

    // get data
    d3.queue()
        .defer(d3.json, 'http://www.json-generator.com/api/json/get/cjsmAgXIVu?indent=2')
        .await(makeLineChart);

    function makeLineChart(error, data) {

        if (error) {
            console.log(error);
        }

        color.domain(d3.keys(data[0])
            .filter(function (key) {
                return key == "CODE";
            })
        );

        createAxis();
        updateChart(data);

        // radio button change
        d3.selectAll("input[name='modality-selector']")
            .on("change", function () {
                modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;
                clearChart();
                createAxis();
                updateChart(data);
            });

    } // end makeLineChart function

    /**
     * Create (if is the firts time) or updates the line chart, 
     * based on radio button selection.
     */
    function updateChart(data) {

        // filter data
        filtered_data = data.filter(function (d) {
            return d.MODALITY == modalitySelected && codes.includes(d.CODE);
        });

        // first we need to corerce the data into the right formats
        filtered_data = filtered_data.map(function (d) {
            return {
                code: d.CODE,
                month: d.MONTH,
                modality: d.MODALITY,
                value: +d.VALUE
            };
        });

        filtered_data = d3.nest()
            .key(function (d) {
                return d.code;
            })
            .entries(filtered_data);

        var codesArray = svg.selectAll(".code")
            .data(filtered_data, function (d) {
                return d.key;
            })
            .enter()
            .append("g")
            .attr("class", "code")
            .on("mouseover", function (d, i) {
                codesArray.append("text")
                    .attr("class", "title-text")
                    .style("fill", color(d.key))
                    .text('')
                    .attr("text-anchor", "middle")
                    .attr("x", 200)
                    .attr("y", 20);
            })
            .on("mouseout", function (d) {
                codesArray.select(".title-text").remove();
            })

        codesArray.append("path")
            .attr("class", "line")
            .attr("d", function (d) {
                // console.log(d)
                return line(d.values);
            })
            .style("stroke", function (d) {
                return color(d.key);
            })
            .style('opacity', lineOpacity)
            .on("mouseover", function (d) {
                d3.selectAll('.line')
                    .style('opacity', otherLinesOpacityHover);
                d3.selectAll('.circle')
                    .style('opacity', circleOpacityOnLineHover);
                d3.select(this)
                    .style('opacity', lineOpacityHover)
                    .style("stroke-width", lineStrokeHover)
                    .style("cursor", "pointer");
            })
            .on("mouseout", function (d) {
                d3.selectAll(".line")
                    .style('opacity', lineOpacity);
                d3.selectAll('.circle')
                    .style('opacity', circleOpacity);
                d3.select(this)
                    .style("stroke-width", lineStroke)
                    .style("cursor", "none");
            });

        /* Add circles in the line */
        codesArray.selectAll("circle-group")
            .data(filtered_data, function (d) {
                return d.key;
            })
            .enter()
            .append("g")
            .style("fill", function (d, i) {
                return color(d.key);
            })
            .on("mouseover", function (d, i) {
                d3.select(this)
                    .style("cursor", "pointer")
                    .append("text")
                    .attr("class", "text")
                    .text(d.values[i].value)
                    .attr("x", d => x(d.key) + 5)
                    .attr("y", d => y(d.value) + 10);
            })
            .on("mouseout", function (d) {
                codesArray.select(".text").remove();
            })
            .selectAll("circle")
            .data(d => d.values).enter()
            .append("g")
            .attr("class", "circle")
            .append("circle")
            .attr("cx", function (d) {
                return x(d.month)
            })
            .attr("cy", d => y(d.value))
            .attr("r", circleRadius)
            .style('opacity', circleOpacity)
            .on("mouseover", function (d) {
                d3.select(this)
                    .transition()
                    .duration(duration)
                    .attr("r", circleRadiusHover);
            })
            .on("mouseout", function (d) {
                d3.select(this)
                    .transition()
                    .duration(duration)
                    .attr("r", circleRadius);
            });

    }

    function createAxis() {

        g = svg.append("g")
            .attr("class", "chartGroup")

        g.append("g")
            .attr("class", "axis x")
            .attr("transform", "translate(0, " + height + ")")
            .call(xAxis);

        g.append("g")
            .attr("class", "axis y")
            .call(yAxis);
    }

    /**
     * Remove old chart (axis and lines).
     */
    function clearChart() {
        d3.select(".chartGroup").remove();
        d3.selectAll(".code").remove();
    }

    //#endregion
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
        integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
        crossorigin="anonymous"></script>

    <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
    <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
    <script src="https://d3js.org/topojson.v2.min.js"></script>
    <script type="text/javascript" charset="utf8"
        src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
    <title>Index 9</title>
</head>

<style>
    svg {
        font-family: Sans-Serif, Arial;
    }

    .line {
        stroke-width: 1.5;
        fill: none;
    }

    .axis path {
        stroke: black;
    }

    .text {
        font-size: 12px;
    }

    .title-text {
        font-size: 12px;
    }

    table {
        font-family: Arial, Helvetica, sans-serif;
        border-collapse: collapse;
        font-size: 12px !important;
        margin-left: 10px;
        margin-top: 10px;
        /* width: 300px !important; */
    }

    .table td {
        padding: .0rem;
    }

    tr {
        line-height: 25px;
    }

    .axis path,
    .axis line {
        fill: none;
        stroke: grey;
        stroke-width: 1;
        shape-rendering: crispEdges;
    }

    th,
    td {
        border: 1px solid #cccccc;
        padding: 0px;
        text-align: center;
    }

    input {
        visibility: hidden;
    }

    label {
        cursor: pointer;
        margin-bottom: .0rem;
    }

    hr {
        margin-top: -5px;
        border-width: 1px;
        border-color: black;
    }
</style>

<body>

    <div style="width: 600px; margin-left: 10px;">
        <div id="line-chart-container"></div>
        <hr>
    <div style="width: 600px;" id="modality-selector-container">
        <form id="modality-selector">
            <div class="row table">
                <div class="col-6">
                    <h7 style="margin-left: 10px;">Sales</h7>
                    <table id="tableSales" class="table table-hover">
                        <thead>
                            <tr>
                                <th>Sale</th>
                                <th>Current Year</th>
                                <th>Last Year</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td>
                                    <input type="radio" name="modality-selector" id="rb-charneca" value="charneca"
                                        checked />
                                    <label for="rb-charneca">CHARNECA</label>
                                </td>
                                <td>236.009,23</td>
                                <td>282.753,78</td>
                            </tr>
                            <tr>
                                <td>
                                    <input type="radio" name="modality-selector" id="rb-joaoluis"
                                        value="joaoluis" />
                                    <label for="rb-joaoluis">JOÃO LUIS</label>
                                </td>
                                <td>257.733,05</td>
                                <td>332.119,31</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </form>
    </div>
</div>
    </div>
</body>

如您所见,将鼠标悬停在圆圈上始终显示相同的值。你能帮我解决这个问题吗?

提前非常感谢您。

最佳答案

您正在将 mouseover 监听器附加到组,希望索引 i 能够为您提供圆圈:

.on("mouseover", function (d, i) {
    d3.select(this)
        .append("text")
        .text(d.values[i].value)
        //etc...

但是,i的索引,而不是圆的索引,并且您只有两个组,索引分别为 0 和 1。

最简单的解决方案是将监听器附加到圆圈本身。这是经过更改的代码:

//#region Criando Tabela de Vendedores
var newMapVendedores = [{
    "vendnm": "CHARNECA",
    "Vendas_Ano": 236009.2299999998,
    "Vendas_Ant": 282753.77999999997
  },
  {
    "vendnm": "JOÃO LUIS",
    "Vendas_Ano": 257733.04999999996,
    "Vendas_Ant": 332119.31
  },
]

$(document).ready(function() {
  $("#tableVendedores").append('<tfoot><th></th><th></th><th></th></tfoot>');
  var table = $('#tableVendedores').DataTable({
    "data": newMapVendedores,
    "columns": [{
        "data": "vendnm",
        title: 'Vendedor'
      },
      {
        "data": "Vendas_Ano",
        title: 'Vendas Ano'
      },
      {
        "data": "Vendas_Ant",
        title: 'Vendas Ant'
      },
    ],
    "bLengthChange": false,
    "bPaginate": false,
    "bFilter": false,
    "info": false,
  });

  $('#tableVendedores').on('click', 'tr', function() {
    var data = table.row(this).data();
  });
});

//#endregion

//#region GRÁFICO         

var codes = ["VENDAS_ANO", "VENDAS_ANT"];
$('span.values').html(codes.join(', '));

modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;

var data = null;
var filtered_data = null;

var margin = {
  top: 30,
  right: 20,
  bottom: 50,
  left: 50
};
var width = 600 - margin.left - margin.right;
var height = 350 - margin.top - margin.bottom;
var duration = 250;

var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";

var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25";
var circleRadius = 3;
var circleRadiusHover = 6;

var month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

var x = d3.scaleBand().range([0, width]).domain(month).padding(1);
var y = d3.scaleLinear().range([height, 0]).domain([0, 65000]);

var color = d3.scaleOrdinal(d3.schemeCategory10);

var xAxis = d3.axisBottom(x).ticks(12);
var yAxis = d3.axisLeft(y);

var svg = d3.select("#line-chart-container")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var g = null;

var line = d3.line()
  .x(function(d) {
    return x(d.month);
  })
  .y(function(d) {
    return y(d.value);
  });
// .curve(d3.curveBasis);

let lines = svg.append('g')
  .attr('class', 'lines');

var mouseG = svg.append("g") // this the black vertical line to folow mouse
  .attr("class", "mouse-over-effects");

mouseG.append("path")
  .attr("class", "mouse-line")
  .style("stroke", "black")
  .style("stroke-width", "1px")
  .style("opacity", "0");

// get data
d3.queue()
  .defer(d3.json, 'http://www.json-generator.com/api/json/get/cjsmAgXIVu?indent=2')
  .await(makeLineChart);

function makeLineChart(error, data) {

  if (error) {
    console.log(error);
  }

  color.domain(d3.keys(data[0])
    .filter(function(key) {
      return key == "CODE";
    })
  );

  createAxis();
  updateChart(data);

  // radio button change
  d3.selectAll("input[name='modality-selector']")
    .on("change", function() {
      modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;
      clearChart();
      createAxis();
      updateChart(data);
    });

} // end makeLineChart function

/**
 * Create (if is the firts time) or updates the line chart, 
 * based on radio button selection.
 */
function updateChart(data) {

  // filter data
  filtered_data = data.filter(function(d) {
    return d.MODALITY == modalitySelected && codes.includes(d.CODE);
  });

  // first we need to corerce the data into the right formats
  filtered_data = filtered_data.map(function(d) {
    return {
      code: d.CODE,
      month: d.MONTH,
      modality: d.MODALITY,
      value: +d.VALUE
    };
  });

  filtered_data = d3.nest()
    .key(function(d) {
      return d.code;
    })
    .entries(filtered_data);

  var codesArray = svg.selectAll(".code")
    .data(filtered_data, function(d) {
      return d.key;
    })
    .enter()
    .append("g")
    .attr("class", "code")
    .on("mouseover", function(d, i) {
      codesArray.append("text")
        .attr("class", "title-text")
        .style("fill", color(d.key))
        .text('')
        .attr("text-anchor", "middle")
        .attr("x", 200)
        .attr("y", 20);
    })
    .on("mouseout", function(d) {
      codesArray.select(".title-text").remove();
    })

  codesArray.append("path")
    .attr("class", "line")
    .attr("d", function(d) {
      // console.log(d)
      return line(d.values);
    })
    .style("stroke", function(d) {
      return color(d.key);
    })
    .style('opacity', lineOpacity)
    .on("mouseover", function(d) {
      d3.selectAll('.line')
        .style('opacity', otherLinesOpacityHover);
      d3.selectAll('.circle')
        .style('opacity', circleOpacityOnLineHover);
      d3.select(this)
        .style('opacity', lineOpacityHover)
        .style("stroke-width", lineStrokeHover)
        .style("cursor", "pointer");
    })
    .on("mouseout", function(d) {
      d3.selectAll(".line")
        .style('opacity', lineOpacity);
      d3.selectAll('.circle')
        .style('opacity', circleOpacity);
      d3.select(this)
        .style("stroke-width", lineStroke)
        .style("cursor", "none");
    });

  /* Add circles in the line */
  codesArray.selectAll("circle-group")
    .data(filtered_data, function(d) {
      return d.key;
    })
    .enter()
    .append("g")
    .style("fill", function(d, i) {
      return color(d.key);
    })
    .selectAll("circle")
    .data(d => d.values).enter()
    .append("g")
    .attr("class", "circle")
    .append("circle")
    .attr("cx", function(d) {
      return x(d.month)
    })
    .attr("cy", d => y(d.value))
    .attr("r", circleRadius)
    .style('opacity', circleOpacity)
    .on("mouseover", function(d) {
      d3.select(this.parentNode)
        .style("cursor", "pointer")
        .append("text")
        .attr("class", "text")
        .text(d.value)
        .attr("x", d => x(d.month) + 10)
        .attr("y", d => y(d.value) - 10);
      d3.select(this)
        .transition()
        .duration(duration)
        .attr("r", circleRadiusHover);
    })
    .on("mouseout", function(d) {
    codesArray.select(".text").remove();
      d3.select(this)
        .transition()
        .duration(duration)
        .attr("r", circleRadius);
    });

}

function createAxis() {

  g = svg.append("g")
    .attr("class", "chartGroup")

  g.append("g")
    .attr("class", "axis x")
    .attr("transform", "translate(0, " + height + ")")
    .call(xAxis);

  g.append("g")
    .attr("class", "axis y")
    .call(yAxis);
}

/**
 * Remove old chart (axis and lines).
 */
function clearChart() {
  d3.select(".chartGroup").remove();
  d3.selectAll(".code").remove();
}

//#endregion
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

  <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
  <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
  <script src="https://d3js.org/topojson.v2.min.js"></script>
  <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
  <title>Index 9</title>
</head>

<style>
  svg {
    font-family: Sans-Serif, Arial;
  }
  
  .line {
    stroke-width: 1.5;
    fill: none;
  }
  
  .axis path {
    stroke: black;
  }
  
  .text {
    font-size: 12px;
  }
  
  .title-text {
    font-size: 12px;
  }
  
  table {
    font-family: Arial, Helvetica, sans-serif;
    border-collapse: collapse;
    font-size: 12px !important;
    margin-left: 10px;
    margin-top: 10px;
    /* width: 300px !important; */
  }
  
  .table td {
    padding: .0rem;
  }
  
  tr {
    line-height: 25px;
  }
  
  .axis path,
  .axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
  }
  
  th,
  td {
    border: 1px solid #cccccc;
    padding: 0px;
    text-align: center;
  }
  
  input {
    visibility: hidden;
  }
  
  label {
    cursor: pointer;
    margin-bottom: .0rem;
  }
  
  hr {
    margin-top: -5px;
    border-width: 1px;
    border-color: black;
  }
</style>

<body>

  <div style="width: 600px; margin-left: 10px;">
    <div id="line-chart-container"></div>
    <hr>
    <div style="width: 600px;" id="modality-selector-container">
      <form id="modality-selector">
        <div class="row table">
          <div class="col-6">
            <h7 style="margin-left: 10px;">Sales</h7>
            <table id="tableSales" class="table table-hover">
              <thead>
                <tr>
                  <th>Sale</th>
                  <th>Current Year</th>
                  <th>Last Year</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>
                    <input type="radio" name="modality-selector" id="rb-charneca" value="charneca" checked />
                    <label for="rb-charneca">CHARNECA</label>
                  </td>
                  <td>236.009,23</td>
                  <td>282.753,78</td>
                </tr>
                <tr>
                  <td>
                    <input type="radio" name="modality-selector" id="rb-joaoluis" value="joaoluis" />
                    <label for="rb-joaoluis">JOÃO LUIS</label>
                  </td>
                  <td>257.733,05</td>
                  <td>332.119,31</td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </form>
    </div>
  </div>
  </div>
</body>

关于javascript - 如何在悬停时显示每个元素的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59626751/

相关文章:

javascript - 在 d3.js 可视化中区分 mouseover 和 mouseout 事件

html - 使用 HTML 和 CSS 构建侧边栏区域的好方法

php - 用php保存urls截图

javascript - 如何正确缩放 d3 中的图表?

javascript - 遍历段落中的单词

html - 使用固定位置和相对位置制作 Web 布局时遇到问题

javascript - d3js v5 调整分组条形图大小

javascript - JS中Base64转图片文件

javascript - 在 JavaScript 中更改构造函数

javascript - 谷歌地图错误 : Uncaught InvalidValueError: setIcon: not a string; and no url property; and no path property