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
函数上,您对 mouseover
和 mouseout
事件进行了注释,如果您删除注释,则会引发错误,这是因为您正在尝试添加事件过渡或更好的听众,您应该在设置过渡之前输入这些行。像这样:
...
.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 它。
tipMouseover
和 tipMouseout
函数也是如此,你声明了它们两次,尽量不要创建重复代码,所以如果你创建一次,并保留对它们的引用。
最后,您还重复了追加圆圈的代码,您可以使用通用函数对其进行改进。我根据建议创建了 this jsfiddle,希望对您有所帮助。
在此 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
函数上,您对 mouseover
和 mouseout
事件进行了注释,如果您删除注释,则会引发错误,这是因为您正在尝试添加事件过渡或更好的听众,您应该在设置过渡之前输入这些行。像这样:
...
.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 它。
tipMouseover
和 tipMouseout
函数也是如此,你声明了它们两次,尽量不要创建重复代码,所以如果你创建一次,并保留对它们的引用。
最后,您还重复了追加圆圈的代码,您可以使用通用函数对其进行改进。我根据建议创建了 this jsfiddle,希望对您有所帮助。