显示圆的鼠标悬停数据

我有一组数据,我正在分散绘制。当鼠标移动到一个圆圈上时,我希望它弹出数据(比如 x,y 值,也许更多)。下面是我试过的方法:

vis.selectAll("circle")
.data(datafiltered).enter().append("svg:circle")
.attr("cx", function(d) { return x(d.x);})
.attr("cy", function(d) {return y(d.y)})
.attr("fill", "red").attr("r", 15)
.on("mouseover", function() {
d3.select(this).enter().append("text")
.text(function(d) {return d.x;})
.attr("x", function(d) {return x(d.x);})
.attr("y", function (d) {return y(d.y);}); });

我想我需要了解更多关于输入什么数据的信息?

241564 次浏览

我猜你想要的是一个工具提示。最简单的方法是在每个圆周上添加一个 svg:title元素,因为浏览器会自动显示工具提示,而不需要 mousehandle。代码大概是这样的

vis.selectAll("circle")
.data(datafiltered).enter().append("svg:circle")
...
.append("svg:title")
.text(function(d) { return d.x; });

如果你想要更好的工具提示,你可以使用例如微醺。

这里描述了一种非常好的制作工具提示的方法: 简单的 D3工具提示示例

必须附加一个 div

var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");

然后你就可以用

.on("mouseover", function(){return tooltip.style("visibility", "visible");})
.on("mousemove", function(){return tooltip.style("top",
(d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});

d3.event.pageX/d3.event.pageY是当前的鼠标坐标。

如果要更改文本,可以使用 tooltip.text("my tooltip text");

工作范例

<script src="https://d3js.org/d3.v7.min.js"></script>
<body>
<div class="example_div"></div>
</body>
<script type="text/javascript">
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");


var sampleSVG = d3.select(".example_div")
.append("svg:svg")
.attr("class", "sample")
.attr("width", 300)
.attr("height", 300);


d3.select(".example_div svg")
.append("svg:circle")
.attr("stroke", "black")
.attr("fill", "aliceblue")
.attr("r", 50)
.attr("cx", 52)
.attr("cy", 52)
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
.on("mousemove", function(){return tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
</script>

我最近发现有一个很棒的图书馆可以用来做这件事。它使用简单,结果非常简洁: d3-tip。

你可以看到一个例子 给你:

enter image description here

基本上,你所要做的就是下载(Index.js) ,包括脚本:

<script src="index.js"></script>

然后按照 给你的说明进行操作(与示例链接相同)

但是对于您的代码,它应该是这样的:

定义方法:

var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Frequency:</strong> <span style='color:red'>" + d.frequency + "</span>";
})

创建 svg (正如您已经做的那样)

var svg = ...

调用方法:

svg.call(tip);

给你的对象加点提示:

vis.selectAll("circle")
.data(datafiltered).enter().append("svg:circle")
...
.on('mouseover', tip.show)
.on('mouseout', tip.hide)

不要忘记添加 CSS:

<style>
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}


/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}


/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>

您可以像这样传递在 mouseover 中使用的数据—— mouseover 事件使用一个函数,其中包含您之前使用的 entered 数据作为参数(索引作为第二个参数) ,因此您不需要再次使用 enter()

vis.selectAll("circle")
.data(datafiltered).enter().append("svg:circle")
.attr("cx", function(d) { return x(d.x);})
.attr("cy", function(d) {return y(d.y)})
.attr("fill", "red").attr("r", 15)
.on("mouseover", function(d,i) {
d3.select(this).append("text")
.text( d.x)
.attr("x", x(d.x))
.attr("y", y(d.y));
});

这个简洁的示例演示了如何在 d3中创建自定义工具提示的常用方法。

var w = 500;
var h = 150;


var dataset = [5, 10, 15, 20, 25];


// firstly we create div element that we can use as
// tooltip container, it have absolute position and
// visibility: hidden by default


var tooltip = d3.select("body")
.append("div")
.attr('class', 'tooltip');


var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);


// here we add some circles on the page


var circles = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle");


circles.attr("cx", function(d, i) {
return (i * 50) + 25;
})
.attr("cy", h / 2)
.attr("r", function(d) {
return d;
})
  

// we define "mouseover" handler, here we change tooltip
// visibility to "visible" and add appropriate test
  

.on("mouseover", function(d) {
return tooltip.style("visibility", "visible").text('radius = ' + d);
})
  

// we move tooltip during of "mousemove"
  

.on("mousemove", function() {
return tooltip.style("top", (event.pageY - 30) + "px")
.style("left", event.pageX + "px");
})
  

// we hide our tooltip on "mouseout"
  

.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
});
.tooltip {
position: absolute;
z-index: 10;
visibility: hidden;
background-color: lightblue;
text-align: center;
padding: 4px;
border-radius: 4px;
font-weight: bold;
color: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>

你可以在自己动手之前想想你想要什么,我在这里提供4个例子。

从本质上讲,演示一,二,四基本上是在相同的精神,演示3是使用 书名的方法。


演示1,2,4: 为每个项目添加 短信(或 异物)标签

  • Demo1: 纯 javascript 编写。

  • Demo2: 与 demo1相同,使用 d3.js 代替

  • Demo4: 应用于柱状图的示例,并说明为什么我使用如此多的文本而不是仅仅使用一个。

    注:

演示3: 这是非常方便的,如果要求不高,这可能是最好的方法。(这和 拉尔斯 · 科特索夫回答道是一样的。)

例子

<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
text.tooltip {
display: none;
}


circle:hover + text.tooltip {
display: initial;
}


circle:hover + foreignobject {
display: initial;
color: #ffff00;
background-color: #015db7;
}


/* ↓ used for demo4Histogram only */
rect:hover + foreignobject {
display: initial;
}


rect:hover {
fill: red;
}
</style>
<body></body>
<script>
const w = 500
const h = 150
const dataset = [5, 10, 15, 20, 25]


function demo1PureJS() {
const svgFrag = document.createRange().createContextualFragment(`
<header>PureJS</header>
<svg width="400" height="150"><g></g></svg><br>
`)
const gElem = svgFrag.querySelector(`g`)
for (const idx in dataset) {
const r = dataset[idx]
const [cx, cy] = [idx * 50 + 25, h / 2];


gElem.insertAdjacentHTML("beforeend", `
<circle cx="${cx}" cy="${cy}" r="${r}" data-tooltip="(${cx}, ${cy})"></circle>
<text class="tooltip" x="${cx}" y="${cy}" fill="red">${r}</text>
`)
document.body.append(svgFrag)


}
}


function demo2D3js() {
const svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)


svg.node().insertAdjacentHTML("beforebegin", "<header>demo2D3js</header>")


svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", (d, i) => i * 50 + 25)
.attr("cy", h / 2)
.attr("r", d => d)
.text((d, idx, arr) => {
const circle = arr[idx]
const x = circle.getAttribute("cx")
const y = circle.getAttribute("cy")


const testCase = "foreignobject"
if (testCase === "foreignobject") { // 👈 focus here
circle.insertAdjacentHTML("afterend", `
<foreignobject x="${x}" y="${y}" width="${d.toString().length * 12}" height="26" display="none">
<div>${d}</div>
</foreignobject>
`)


} else {
circle.insertAdjacentHTML("afterend", `<text class="tooltip" x="${x}" y="${y}" fill="yellow">${d}</text>`)
}
return ""
})
}


function demo3SVGTitle() {
/*
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
<rect x="11" y="1" width="8" height="8">
<title>I'm a square</title>
</rect>
*/


const svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)


svg.node().insertAdjacentHTML("beforebegin", "<header>SVGTitle</header>")


svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", (d, i) => i * 50 + 25)
.attr("cy", h / 2)
.attr("r", d => d)
.append("svg:title") // 👈 focus here
.text(d => d)
}


async function demo4Histogram() {
const margin = {top: 50, right: 50, bottom: 50, left: 50},
width = 900 - margin.left - margin.right,
height = 900 - margin.top - margin.bottom


const svg = d3.select("body")
.append("svg")


svg.node().insertAdjacentHTML("beforebegin", "<header>Histogram</header>")


const mainG = svg.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`)




const dataSet = []
await d3.csv("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/1_OneNum.csv", (row) => {
dataSet.push(row)
})


// X: price
const scaleX = d3.scaleLinear()
.domain([0, 2000])
.range([0, width])


mainG.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(scaleX)
)


const histogram = d3.histogram()
.value(d => d.price)
.domain(scaleX.domain())
.thresholds(scaleX.ticks(50))


const bins = histogram(dataSet)


// Y: Count
const scaleY = d3.scaleLinear()
.domain([0, d3.max(bins, d => d.length)])
.range([height, 0])


mainG.append("g")
.call(d3.axisLeft(scaleY))


mainG.selectAll("rect")
.data(bins)
.enter()
.append("rect")
.attr("transform", d => `translate(${scaleX(d.x0)},${scaleY(d.length)})`)
.attr("x", 1)
.attr("width", d => d3.max([0, scaleX(d.x1) - scaleX(d.x0) - 1]))
.attr("height", d => height - scaleY(d.length))
.attr("fill", "#298e75")
.attr("fill-opacity", 0.4)
.text((d, idx, arr) => { // 👈 focus here
const rect = arr[idx]
const [x, y, width] = [rect.getAttribute("x"), rect.getAttribute("y") ?? 0, rect.getAttribute("width")];
if (width > 0) {
const msg = `${d.x0}~${d.x1}: ${d.length}`
rect.insertAdjacentHTML("afterend", `
<foreignobject x="${x}" y="${y}" width="${msg.length * 13}" height=26 display="none" class="tooltip"
transform="translate(${scaleX(d.x0)},${scaleY(d.length)})">
<div>${msg}</div>
</foreignobject>
`)
}
return ""
})


/**
You can certainly consider creating just one element and moving it around to achieve the display effect. [see https://stackoverflow.com/a/47002479/9935654]
On my side, I made a corresponding element individually, which seems to generate a lot of duplicate items, but it can be done as follows:
If you are interested in a specific marker, you can click on it, and it will display the message forever(cancel again to hidden)
* */
document.querySelectorAll(`foreignObject.tooltip`).forEach(div => { // 👈 focus here
div.addEventListener("click", () => {
div.setAttribute("display", div.getAttribute("display") === "none" ? "" : "none")
})
})
}


demo1PureJS()
demo2D3js()
demo3SVGTitle()
demo4Histogram()


</script>

Demo4: 因为每个元素都有一个标签,所以可以同时显示多个标签,这是单个元素无法做到的。

demo4Histogram


D3.js 版本: v7