D3 圆弧图:圆形鼠标悬停不显示的工具提示

D3 Arc Diagram: Tooltip on Circle Mouseover Not Displaying

我被一个问题困住了,只是在寻找另一双眼睛,也许能发现我遗漏的东西。

我有一个正在构建的弧形图,当我将鼠标悬停在 circles/nodes 上时,我想要一个工具提示事件。

您可以看到示例 bl.ock here。我 认为 要让这个工作正常,每个圆圈都需要在它自己的 g 元素中,我已经做到了。我有一个处理工具提示创建的函数:

function tooltipText(d) {
 return "<h5>Information for " + d.token + "</h5>" +
   "<table>" +
   "<tr>" +
   "<td class='field'>Token: </td>" +
   "<td>" + d.token + "</td>" +
   "</tr>" +
   "<tr>" +
   "<td class='field'>Dialect: </td>" +
   "<td>" + d.dialect + "</td>" +
   "</tr>" +
   "<tr>" +
   "<td class='field'>IME: </td>" +
   "<td>" + d.input_method + "</td>" +
   "</tr>" +
   "<tr>" +
   "<td class='field'>Operating System: </td>" +
   "<td>" + d.operating_system + "</td>" +
   "</tr>" +
   "<tr>" +
   "<td class='field'>Trial: </td>" +
   "<td>" + d.trial + "</td>" +
   "</tr>" +
   "</table>";
}

使用 CSS,工具提示保持隐藏状态,直到调用鼠标悬停:

.hidden {
  display: none;
  visibility: hidden;
  pointer-events: none;
}

.tooltip {
  color: #222;
  background: #fff;
  padding: .5em;
  text-shadow: #f5f5f5 0 1px 0;
  border-radius: 10px;
  border-color: #a6a6a6;
  border-width: 1px;
  border-style: solid;
  box-shadow: 0px 0px 2px 0px #a6a6a6; 
  opacity: 0.9; 
  position: absolute;
  width: 225px;
  display: block;
}

.tooltip h5 {
  font-size: 1.05rem;
}

.tooltip p {
  font-size: 0.80rem;
}

鼠标悬停本身:

// Tooltip
var tooltip = d3.select("body").append("div")
  .classed("tooltip", true)
  .classed("hidden", true);

...

nodeEnter.selectAll(".node")
    .on("mousemove", function(d, i) {
      var mouse = d3.mouse(d3.select("body").node());
      tooltip
        .classed("hidden", false)
        .attr("class", "tooltip")
        .attr("style", "left:" + (mouse[0] + 20) + "px; top:" + (mouse[1] - 50) + "px")
        .html(tooltipText(d)); 
    })
    .on("mouseover", nodeOver);

任何指点将不胜感激。

在 JavaScript 代码的开头存在与 queue() 的使用相关的问题。

我建议使用标准 D3 函数来加载数据,例如 d3.json()。

我根据你的块制作了一个代码示例。与您的示例的唯一区别是数据在 JavaScript 函数内。工具提示有效!

var width = 960,
  height = 400,
  margin = 20,
  pad = margin / 2,
  padding = 100,
  radius = 6,
  yfixed = pad + radius;

// Legend variables
var legend_x = 0,
  legend_y = 5,
  legend_width = 175,
  legend_height = 620,
  legend_margin = 20
key_y = 40,
  key_x = 16,
  mapped_y = legend_y + legend_height - 90;

var color = d3.scale.category20();

// Tooltip
var tooltip = d3.select("body").append("div")
  .classed("tooltip", true)
  .classed("hidden", true);

var graph = getData();
arcDiagram(graph);


// Main
//-----------------------------------------------------
function arcDiagram(graph) {
  var radius = d3.scale.sqrt()
    .domain([0, 20])
    .range([0, 15]);

  var svg = d3.select("#chart").append("svg")
    .attr("id", "arc")
    .attr("width", width)
    .attr("height", height);

  // create plot within svg
  var plot = svg.append("g")
    .attr("id", "plot")
    .attr("transform", "translate(" + padding + ", " + padding + ")");

  // count the paths
  graph.links.forEach(function(d, i) {
    var pathCount = 0;
    for (var j = 0; j < i; j++) {
      var otherPath = graph.links[j];
      if (otherPath.source === d.source && otherPath.target === d.target) {
        pathCount++;
      }
    }
    // console.log(pathCount)
    d.pathCount = pathCount;
  });

  // fix graph links to map to objects
  graph.links.forEach(function(d, i) {
    d.source = isNaN(d.source) ? d.source : graph.nodes[d.source];
    d.target = isNaN(d.target) ? d.target : graph.nodes[d.target];
  });

  linearLayout(graph.nodes);
  drawLinks(graph.links);
  drawNodes(graph.nodes);
}

// layout nodes linearly
function linearLayout(nodes) {
  nodes.sort(function(a, b) {
    return a.uniq - b.uniq;
  });

  var xscale = d3.scale.linear()
    .domain([0, nodes.length - 1])
    .range([radius, width - margin - radius]);

  nodes.forEach(function(d, i) {
    d.x = xscale(i);
    d.y = yfixed;
  });
}

function drawNodes(nodes) {

  var gnodes = d3.select("#plot").selectAll("g.node")
    .data(nodes);

  var nodeEnter = gnodes.enter()
    .append('g')
    .attr("class", "gnode");

  nodeEnter.append("circle")
    .attr("class", "node")
    .attr("id", function(d, i) {
      return d.name;
    })
    .attr("cx", function(d, i) {
      return d.x;
    })
    .attr("cy", function(d, i) {
      return d.y;
    })
    .attr("r", 14)
    .style("stroke", function(d, i) {
      return color(d.type);
    });

  // Handling mouseover functions
  nodeEnter.selectAll(".node")
    .on("mousemove", function(d, i) {
      var mouse = d3.mouse(d3.select("body").node());
      tooltip
        .classed("hidden", false)
        .attr("class", "tooltip")
        .attr("style", "left:" + (mouse[0] + 20) + "px; top:" + (mouse[1] - 50) + "px")
        .html(tooltipText(d));
    })
    .on("mouseover", nodeOver);

  nodeEnter.append("text")
    .style("text-anchor", "middle")
    .attr("dx", function(d) {
      return d.x;
    })
    .attr("dy", function(d) {
      return d.y + 5;
    })
    .text(function(d) {
      return d.token;
    });

  d3.select("#trial2")
    .on("mouseover", trialOver);
}

function drawLinks(links) {
  var radians = d3.scale.linear()
    .range([Math.PI / 2, 3 * Math.PI / 2]);

  var arc = d3.svg.line.radial()
    .interpolate("basis")
    .tension(0)
    .angle(function(d) {
      return radians(d);
    });

  d3.select("#plot").selectAll(".link")
    .data(links)
    .enter().append("path")
    .attr("class", "link")
    .style("stroke-width", function(d) {
      return (2 + d.pathCount);
    })
    .attr("transform", function(d, i) {
      var xshift = d.source.x + (d.target.x - d.source.x) / 2;
      var yshift = yfixed;
      return "translate(" + xshift + ", " + yshift + ")";
    })
    .attr("d", function(d, i) {
      var xdist = Math.abs(d.source.x - d.target.x);
      arc.radius(xdist / 2);
      var points = d3.range(0, Math.ceil(xdist / 3));
      radians.domain([0, points.length - 1]);
      return arc(points);
    })
    .on("mouseover", edgeOver);
}

// Draw legend
//-----------------------------------------------------
function drawLegend(d) {
  var legend = svg.append("g")
    .attr("class", "legend");
  var key = legend.append("g")

  // Initial
  key.append("circle")
    .attr("id", "legend_initial")
    .attr("cx", legend_x + key_x)
    .attr("cy", legend_y + key_y + 5)
    .attr("r", 5)
    .style("fill", "blue");

  key.append("text")
    .attr("class", "legendText")
    .attr("id", "legend_initial_label")
    .attr("x", legend_x + key_x + 10)
    .attr("y", legend_y + 10 + key_y)
    .text("Initial");

  // Selection
  key.append("circle")
    .attr("id", "legend_selection")
    .attr("cx", function() {
      return legend_x + key_x
    })
    .attr("cy", function() {
      return legend_y + legend_margin + key_y + 5
    })
    .attr("r", 5)
    .style("fill", "lightblue");

  key.append("text")
    .attr("class", "legendText")
    .attr("id", "legend_selection_label")
    .attr("x", legend_x + key_x + 10)
    .attr("y", legend_y + legend_margin + 10 + key_y)
    .text("Selection");

  // Final
  key.append("circle")
    .attr("id", "legend_final")
    .attr("cx", legend_x + key_x)
    .attr("cy", legend_y + 2 * legend_margin + key_y + 5)
    .attr("r", 5)
    .style("fill", "orange");

  key.append("text")
    .attr("class", "legendText")
    .attr("id", "legend_final_label")
    .attr("x", legend_x + key_x + 10)
    .attr("y", legend_y + 2 * legend_margin + 10 + key_y)
    .text("Final");

  // Delete
  key.append("circle")
    .attr("id", "legend_delete")
    .attr("cx", legend_x + key_x)
    .attr("cy", legend_y + 3 * legend_margin + key_y + 5)
    .attr("r", 5)
    .style("fill", "gold");

  key.append("text")
    .attr("class", "legendText")
    .attr("id", "legend_delete_label")
    .attr("x", legend_x + key_x + 10)
    .attr("y", legend_y + 3 * legend_margin + 10 + key_y)
    .text("Delete");
}

function tooltipText(d) {
  return "<h5>Information for " + d.token + "</h5>" +
    "<table>" +
    "<tr>" +
    "<td class='field'>Token: </td>" +
    "<td>" + d.token + "</td>" +
    "</tr>" +
    "<tr>" +
    "<td class='field'>Dialect: </td>" +
    "<td>" + d.dialect + "</td>" +
    "</tr>" +
    "<tr>" +
    "<td class='field'>IME: </td>" +
    "<td>" + d.input_method + "</td>" +
    "</tr>" +
    "<tr>" +
    "<td class='field'>Operating System: </td>" +
    "<td>" + d.operating_system + "</td>" +
    "</tr>" +
    "<tr>" +
    "<td class='field'>Trial: </td>" +
    "<td>" + d.trial + "</td>" +
    "</tr>" +
    "</table>";
}

function nodeOver(d, i) {
  d3.selectAll("path").style("stroke", function(p) {
    return p.source == d || p.target == d ? "#17becf" : "#888888"
  })
}

function edgeOver(d) {
  d3.selectAll("path").style("stroke", function(p) {
    return p == d ? "#17becf" : "#888888"
  })
}

function trialOver(d) {
  var active,
    changedOpacity;

  d3.select("#arcToken"); // function)
}

function tokenOver(d, i) {
  d3.selectAll(this).style("stroke", function(d) {
    return p.token == d ? "#17becf" : "#888888"
  })
}


function getData() {
  return {
    "nodes": [{
      "token": "x",
      "type": "initial",
      "uniq": "1",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "ia",
      "type": "final",
      "uniq": "2",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "1",
      "type": "selection",
      "uniq": "3",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "m",
      "type": "initial",
      "uniq": "4",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "a",
      "type": "final",
      "uniq": "5",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "1",
      "type": "selection",
      "uniq": "6",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "y",
      "type": "initial",
      "uniq": "7",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "in",
      "type": "final",
      "uniq": "8",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "l",
      "type": "initial",
      "uniq": "9",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "iao",
      "type": "final",
      "uniq": "10",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "_",
      "type": "selection",
      "uniq": "11",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "*",
      "type": "productive-delete",
      "uniq": "12",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "j",
      "type": "initial",
      "uniq": "13",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "un",
      "type": "final",
      "uniq": "14",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "z",
      "type": "initial",
      "uniq": "15",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "1",
      "type": "selection",
      "uniq": "16",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "*",
      "type": "productive-delete",
      "uniq": "17",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "j",
      "type": "initial",
      "uniq": "18",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "iu",
      "type": "final",
      "uniq": "19",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }, {
      "token": "1",
      "type": "selection",
      "uniq": "20",
      "age_group": "18-30",
      "dialect": "cantonese",
      "gender": "female",
      "operating_system": "mac",
      "input_method": "apple",
      "trial": "2",
      "chinese": "",
      "english": ""
    }],
    "links": [{
      "source": 0,
      "target": 1
    }, {
      "source": 0,
      "target": 1
    }, {
      "source": 0,
      "target": 1
    }, {
      "source": 0,
      "target": 1
    }, {
      "source": 0,
      "target": 1
    }, {
      "source": 0,
      "target": 1
    }, {
      "source": 1,
      "target": 3
    }, {
      "source": 1,
      "target": 3
    }, {
      "source": 1,
      "target": 3
    }, {
      "source": 1,
      "target": 3
    }, {
      "source": 1,
      "target": 3
    }, {
      "source": 1,
      "target": 2
    }, {
      "source": 2,
      "target": 3
    }, {
      "source": 3,
      "target": 4
    }, {
      "source": 3,
      "target": 4
    }, {
      "source": 3,
      "target": 4
    }, {
      "source": 3,
      "target": 4
    }, {
      "source": 3,
      "target": 4
    }, {
      "source": 3,
      "target": 4
    }, {
      "source": 4,
      "target": 5
    }, {
      "source": 4,
      "target": 6
    }, {
      "source": 4,
      "target": 6
    }, {
      "source": 4,
      "target": 5
    }, {
      "source": 4,
      "target": 5
    }, {
      "source": 4,
      "target": 5
    }, {
      "source": 5,
      "target": 6
    }, {
      "source": 5,
      "target": 6
    }, {
      "source": 5,
      "target": 6
    }, {
      "source": 6,
      "target": 7
    }, {
      "source": 6,
      "target": 7
    }, {
      "source": 6,
      "target": 7
    }, {
      "source": 6,
      "target": 7
    }, {
      "source": 6,
      "target": 7
    }, {
      "source": 6,
      "target": 7
    }, {
      "source": 7,
      "target": 12
    }, {
      "source": 7,
      "target": 12
    }, {
      "source": 7,
      "target": 12
    }, {
      "source": 7,
      "target": 12
    }, {
      "source": 7,
      "target": 12
    }, {
      "source": 7,
      "target": 8
    }, {
      "source": 8,
      "target": 9
    }, {
      "source": 9,
      "target": 10
    }, {
      "source": 10,
      "target": 11
    }, {
      "source": 11,
      "target": 12
    }, {
      "source": 12,
      "target": 13
    }, {
      "source": 12,
      "target": 13
    }, {
      "source": 12,
      "target": 13
    }, {
      "source": 12,
      "target": 13
    }, {
      "source": 12,
      "target": 13
    }, {
      "source": 12,
      "target": 13
    }, {
      "source": 13,
      "target": 14
    }, {
      "source": 14,
      "target": 15
    }, {
      "source": 15,
      "target": 16
    }, {
      "source": 16,
      "target": 17
    }, {
      "source": 13,
      "target": 17
    }, {
      "source": 13,
      "target": 17
    }, {
      "source": 13,
      "target": 17
    }, {
      "source": 13,
      "target": 17
    }, {
      "source": 13,
      "target": 17
    }, {
      "source": 17,
      "target": 18
    }, {
      "source": 17,
      "target": 18
    }, {
      "source": 17,
      "target": 18
    }, {
      "source": 17,
      "target": 18
    }, {
      "source": 17,
      "target": 18
    }, {
      "source": 17,
      "target": 18
    }, {
      "source": 18,
      "target": 19
    }, {
      "source": 18,
      "target": 19
    }, {
      "source": 18,
      "target": 19
    }, {
      "source": 18,
      "target": 19
    }, {
      "source": 18,
      "target": 19
    }]
  };

}
@import url(http://fonts.googleapis.com/css?family=Lato:300,400);
 body {
  font-family: 'Lato', sans-serif;
  font-weight: 300;
  background: #fff;
}
b {
  font-weight: 900;
}
#chart {
  position: relative;
}
.outline {
  fill: none;
  stroke: #888888;
  stroke-width: 1px;
}
.hidden {
  display: none;
  visibility: hidden;
  pointer-events: none;
}
.tooltip {
  color: #222;
  background: #fff;
  padding: .5em;
  text-shadow: #f5f5f5 0 1px 0;
  border-radius: 10px;
  border-color: #a6a6a6;
  border-width: 1px;
  border-style: solid;
  box-shadow: 0px 0px 2px 0px #a6a6a6;
  opacity: 0.9;
  position: absolute;
  width: 225px;
  display: block;
}
.tooltip h5 {
  font-size: 1.05rem;
}
.tooltip p {
  font-size: 0.80rem;
}
table {
  border: none;
  margin: 0;
  padding: 0;
  border-spacing: 0;
  width: 100%;
}
td {
  text-align: right;
  padding: 2px 0!important;
}
tr {
  margin: 0;
  background-color: white;
}
.node {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 2.5px;
  font: 10px sans-serif;
}
.node text {
  color: #333;
}
.link {
  fill: none;
  stroke: #888888;
  stroke-weight: 1px;
  stroke-opacity: 0.5;
}
.legend {
  font-size: 12px;
}
/*rect {
  stroke-width: 2;
}*/

.highlight {
  stroke: red;
  stroke-weight: 4px;
  stroke-opacity: 1.0;
}
.row {
  padding-top: 50px;
}
.col-md-3 {
  background: rgba(250, 255, 255, 0.5);
  border-left: 1px solid #333;
  height: 100%;
}
#english {
  padding: 1em;
  background: rgba(250, 255, 255, 1);
  border-bottom: 1px solid #ccc;
}
#chinese {
  padding: 1em;
  background: rgba(250, 255, 255, 1);
  border-bottom: 1px solid #ccc;
}
#source {
  color: brown;
}
/* Filters */

#accordion {
  position: fixed;
  width: 20%;
  right: 1;
}
.panel-body {
  background: rgba(250, 255, 255, 0.5);
}
.panel-default > .panel-heading {
  /*background: #fff;*/
}
.panel-heading {
  padding: 0;
  border-top-left-radius: 0px;
  border-top-right-radius: 0px;
}
.panel-group .panel {
  border-radius: 0;
}
.panel-group {
  margin-bottom: 0;
}
.panel-title a {
  color: #333;
  text-align: left;
  width: 100%;
  display: block;
  padding: 10px 15px;
  font-size: 14px;
  outline: none;
}
.panel-title a:hover,
.panel-title a:focus,
.panel-title a:active {
  text-decoration: none;
  outline: none;
}
.panel-options {
  padding: 5px;
}
.legend circle {
  fill: none;
  stroke: #ccc;
}
.legend text {
  fill: #777;
  font: 10px sans-serif;
  text-anchor: middle;
}
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title>Chinese Input</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <link rel="stylesheet" href="style.css">
  <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
  <script src="http://d3js.org/queue.v1.min.js"></script>
  <script src="visualization.js"></script>


</head>

<body>

  <div id="chart"></div>

</body>

</html>