d3 悬停文本到剪贴路径
d3 hover text to clippath
我正在尝试将悬停时的工具提示添加到剪辑路径[这是我第一次使用剪辑路径...这是否正确?]...我认为我真的很接近[应该跟随线的圆圈现在显示在 (0.0)] 但我似乎遗漏了一些东西。我想知道有没有人有时间看一下?我正在尝试改编来自 https://blockbuilder.org/bendoesdata/2c8b315d103bbaf98264efda92d313ab
的代码
var data3 = [
{ group: 1, ser1: "2020-01-01", ser2: 3 },
{ group: 1, ser1: "2020-01-02", ser2: 5 },
{ group: 1, ser1: "2020-01-03", ser2: 9 },
{ group: 1, ser1: "2020-01-04", ser2: 3 },
{ group: 1, ser1: "2020-01-05", ser2: 5 },
{ group: 1, ser1: "2020-01-06", ser2: 9 },
{ group: 2, ser1: "2020-01-07", ser2: 10 },
{ group: 2, ser1: "2020-01-08", ser2: 9 },
{ group: 2, ser1: "2020-01-09", ser2: 10 },
{ group: 2, ser1: "2020-01-10", ser2: 20 },
{ group: 2, ser1: "2020-01-11", ser2: 10 },
{ group: 2, ser1: "2020-01-12", ser2: 12 },
{ group: 3, ser1: "2020-01-13", ser2: 20 },
{ group: 3, ser1: "2020-01-14", ser2: 12 },
{ group: 3, ser1: "2020-01-15", ser2: 4 },
{ group: 3, ser1: "2020-01-16", ser2: 22 },
{ group: 3, ser1: "2020-01-17", ser2: 2 },
{ group: 3, ser1: "2020-01-18", ser2: 4 },
]
var line = d3.line()
.x(function (d) { return x(formatDate(d.ser1)); })
.y(function (d) { return y(d.ser2); })
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 50 },
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.append("defs")
.append("clipPath")
.attr("id", "chart-path")
.append("rect")
.attr("width", width - 100)
.attr("height", height)
var formatDate = d3.timeParse("%Y-%m-%d");
var bisectDate = d3.bisector(function (d) {
return d.str1;
}).left;
// Initialize a X axis:
var x = d3.scaleTime().range([0, width - 100]);
var xNum = d3.scaleLinear().range([0, width - 100])
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "myXaxis")
// Initialize an Y axis
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
.attr("class", "myYaxis")
// create the Y axis
y.domain([0, d3.max(data3, function (d) { return d.ser2 })]);
svg.selectAll(".myYaxis")
.transition()
.duration(1000)
.call(yAxis);
// Create a update selection: bind to the new data
var u = svg.selectAll(".lineTest")
.data([data3])
.enter()
.append("path")
.attr("class", "lineTest")
.attr("fill", "none")
.attr("stroke", "#0b6dbd")
.attr("stroke-width", 2.5)
.attr("clip-path", "url(#chart-path)")
// Create a function that takes a dataset as input and update the plot:
function update(data) {
// Create the X axis:
x.domain(d3.extent(data, function (d) { return formatDate(d.ser1) }));
svg.selectAll(".myXaxis")
.transition()
.duration(1000)
.call(xAxis);
u.transition()
.duration(1000)
.attr("d", line);
}
// At the beginning, I run the update function on the first dataset:
update(data3)
// this is where I try to implement a tooltip based on
// https://blockbuilder.org/bendoesdata/2c8b315d103bbaf98264efda92d313ab
function drawFocus() {
// Create focus object
let focus = svg.append('g')
.attr('class', 'focus')
// append circle on the line path
focus.append('circle')
.attr('r', 7.5)
// add background rectangle behind the text tooltip
focus.append('rect')
.attr('x', -30)
.attr('y', '-2em')
.attr('width', 70)
.attr('height', 20)
.style("fill", "white");
// add text annotation for tooltip
focus.append('text')
.attr('x', -30)
.attr('dy', '-1em')
.style("fill", "black")
.style("font-family", "SuisseIntl");
focus.append('div')
.attr('x', 10)
.attr('dy', '.35em')
.attr("class", "tooltip")
.style("opacity", 1)
// create an overlay rectangle to draw the above objects on top of
svg.append('rect')
.attr('class', 'overlay')
.attr('width', width - 100)
.attr('height', height)
.on('mouseover', () => focus.style('display', null))
.on('mouseout', () => focus.style('display', 'none'))
.on('mousemove', tipMove);
// make the overlay rectangle transparent,
// so it only serves the purpose of detecting mouse events
d3.select('.overlay')
.style('fill', 'none')
.style('pointer-events', 'all');
// select focus objects and set opacity
d3.selectAll('.focus')
.style('opacity', 0.9);
// select the circle and style it
d3.selectAll('.focus circle')
.style("fill", '#FDD511')
.style("opacity", 0)
// function that adds tooltip on hover
function tipMove() {
// below code finds the date by bisecting and
// stores the x and y coordinate as variables
let x0 = x.invert(d3.mouse(this)[0]);
let i = bisectDate(data3, x0, 1);
let d0 = data3[i - 1];
let d1 = data3[i];
let d = x0 - d0.str1 > d1.str1 - x0 ? d1 : d0;
// place the focus objects on the same path as the line
focus.attr('transform', `translate(${x(d.date)}, ${y(d.value)})`);
// position the x line
focus.select('line.x')
.attr('x1', 0)
.attr('x2', x(formatDate(d.str1)))
.attr('y1', 0)
.attr('y2', 0);
// position the y line
focus.select('line.y')
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', 0)
.attr('y2', height - y(d.str2));
// position the text
focus.select('text').text(d.str2).transition() // slowly fade in the tooltip
.duration(100)
.style("opacity", 1);
// show the circle on the path
focus.selectAll('.focus circle')
.style("opacity", 1)
};
}
drawFocus();
.tooltip {
position: absolute;
text-align: left;
font-family: "Open Sans Condensed";
font-size: 12px;
width: 80px;
height: 52px;
padding: 8px;
background: white;
pointer-events: none;
background-color: white;
line-height: 0.05em;
padding: 20px;
/* border: solid;
border-width: 1px; */
}
#my_dataviz {
padding: 20px;
}
<script src="https://d3js.org/d3.v5.js"></script>
<div id="my_dataviz"></div>
您混淆了您的命名。 ser1
你给 str1
打了很多次电话。这是一个明显的迹象,表明您的原始命名 ser1
和 ser2
不够合乎逻辑!如果您现在不能遵循它,一年后您重新访问代码时如何?
在使用数据之前解析你的数据,而不是在你使用它的时候。如果您将日期作为字符串存储在原始对象中,请在 对其进行处理之前对其进行解析。否则,当您想要比较、绘图、格式化或只是 使用 时,您会 运行 遇到问题。
就是这样,我所做的大部分更改只是在需要时将 formatDate
替换为开头的 parseDate
(这些是不同的东西),并将 str1/2
替换为 ser1/2
需要的地方。
var data3 = [
{ group: 1, ser1: "2020-01-01", ser2: 3 },
{ group: 1, ser1: "2020-01-02", ser2: 5 },
{ group: 1, ser1: "2020-01-03", ser2: 9 },
{ group: 1, ser1: "2020-01-04", ser2: 3 },
{ group: 1, ser1: "2020-01-05", ser2: 5 },
{ group: 1, ser1: "2020-01-06", ser2: 9 },
{ group: 2, ser1: "2020-01-07", ser2: 10 },
{ group: 2, ser1: "2020-01-08", ser2: 9 },
{ group: 2, ser1: "2020-01-09", ser2: 10 },
{ group: 2, ser1: "2020-01-10", ser2: 20 },
{ group: 2, ser1: "2020-01-11", ser2: 10 },
{ group: 2, ser1: "2020-01-12", ser2: 12 },
{ group: 3, ser1: "2020-01-13", ser2: 20 },
{ group: 3, ser1: "2020-01-14", ser2: 12 },
{ group: 3, ser1: "2020-01-15", ser2: 4 },
{ group: 3, ser1: "2020-01-16", ser2: 22 },
{ group: 3, ser1: "2020-01-17", ser2: 2 },
{ group: 3, ser1: "2020-01-18", ser2: 4 },
];
var parseDate = d3.timeParse("%Y-%m-%d");
// Parse the date ASAP
data3 = data3.map(function(d) {
return {
group: d.group,
ser1: parseDate(d.ser1),
ser2: d.ser2
};
});
var line = d3.line()
.x(function (d) { return x(d.ser1); })
.y(function (d) { return y(d.ser2); })
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 50 },
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.append("defs")
.append("clipPath")
.attr("id", "chart-path")
.append("rect")
.attr("width", width - 100)
.attr("height", height)
var formatDate = d3.timeParse("%Y-%m-%d");
var bisectDate = d3.bisector(function (d) {
return d.ser1;
}).left;
// Initialize a X axis:
var x = d3.scaleTime()
.range([0, width - 100])
.domain(d3.extent(data3, function (d) { return d.ser1; }));
var xNum = d3.scaleLinear().range([0, width - 100])
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "myXaxis")
// Initialize an Y axis
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
.attr("class", "myYaxis")
// create the Y axis
y.domain([0, d3.max(data3, function (d) { return d.ser2 })]);
svg.selectAll(".myYaxis")
.transition()
.duration(1000)
.call(yAxis);
// Create a update selection: bind to the new data
var u = svg.selectAll(".lineTest")
.data([data3])
.enter()
.append("path")
.attr("class", "lineTest")
.attr("fill", "none")
.attr("stroke", "#0b6dbd")
.attr("stroke-width", 2.5)
.attr("clip-path", "url(#chart-path)")
svg.selectAll(".myXaxis")
.call(xAxis);
u.attr("d", line);
// this is where I try to implement a tooltip based on
// https://blockbuilder.org/bendoesdata/2c8b315d103bbaf98264efda92d313ab
function drawFocus() {
// Create focus object
let focus = svg.append('g')
.attr('class', 'focus')
// append circle on the line path
focus.append('circle')
.attr('r', 7.5)
// add background rectangle behind the text tooltip
focus.append('rect')
.attr('x', -30)
.attr('y', '-2em')
.attr('width', 70)
.attr('height', 20)
.style("fill", "white");
// add text annotation for tooltip
focus.append('text')
.attr('x', -30)
.attr('dy', '-1em')
.style("fill", "black")
.style("font-family", "SuisseIntl");
focus.append('div')
.attr('x', 10)
.attr('dy', '.35em')
.attr("class", "tooltip")
.style("opacity", 1)
// create an overlay rectangle to draw the above objects on top of
svg.append('rect')
.attr('class', 'overlay')
.attr('width', width - 100)
.attr('height', height)
.on('mouseover', () => focus.style('display', null))
.on('mouseout', () => focus.style('display', 'none'))
.on('mousemove', tipMove);
// make the overlay rectangle transparent,
// so it only serves the purpose of detecting mouse events
d3.select('.overlay')
.style('fill', 'none')
.style('pointer-events', 'all');
// select focus objects and set opacity
d3.selectAll('.focus')
.style('opacity', 0.9);
// select the circle and style it
d3.selectAll('.focus circle')
.style("fill", '#FDD511')
.style("opacity", 0)
// function that adds tooltip on hover
function tipMove() {
// below code finds the date by bisecting and
// stores the x and y coordinate as variables
let x0 = x.invert(d3.mouse(this)[0]);
let i = bisectDate(data3, x0, 1);
let d0 = data3[i - 1];
let d1 = data3[i];
let d = x0 - d0.ser1 > d1.ser1 - x0 ? d1 : d0;
// place the focus objects on the same path as the line
focus.attr('transform', `translate(${x(d.ser1)}, ${y(d.ser2)})`);
// position the x line
focus.select('line.x')
.attr('x1', 0)
.attr('x2', x(d.ser1))
.attr('y1', 0)
.attr('y2', 0);
// position the y line
focus.select('line.y')
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', 0)
.attr('y2', height - y(d.ser2));
// position the text
focus.select('text').text(d.ser2)
.transition() // slowly fade in the tooltip
.duration(100)
.style("opacity", 1);
// show the circle on the path
focus.selectAll('.focus circle')
.style("opacity", 1)
};
}
drawFocus();
.tooltip {
position: absolute;
text-align: left;
font-family: "Open Sans Condensed";
font-size: 12px;
width: 80px;
height: 52px;
padding: 8px;
background: white;
pointer-events: none;
background-color: white;
line-height: 0.05em;
padding: 20px;
/* border: solid;
border-width: 1px; */
}
#my_dataviz {
padding: 20px;
}
<script src="https://d3js.org/d3.v5.js"></script>
<div id="my_dataviz"></div>
我正在尝试将悬停时的工具提示添加到剪辑路径[这是我第一次使用剪辑路径...这是否正确?]...我认为我真的很接近[应该跟随线的圆圈现在显示在 (0.0)] 但我似乎遗漏了一些东西。我想知道有没有人有时间看一下?我正在尝试改编来自 https://blockbuilder.org/bendoesdata/2c8b315d103bbaf98264efda92d313ab
的代码var data3 = [
{ group: 1, ser1: "2020-01-01", ser2: 3 },
{ group: 1, ser1: "2020-01-02", ser2: 5 },
{ group: 1, ser1: "2020-01-03", ser2: 9 },
{ group: 1, ser1: "2020-01-04", ser2: 3 },
{ group: 1, ser1: "2020-01-05", ser2: 5 },
{ group: 1, ser1: "2020-01-06", ser2: 9 },
{ group: 2, ser1: "2020-01-07", ser2: 10 },
{ group: 2, ser1: "2020-01-08", ser2: 9 },
{ group: 2, ser1: "2020-01-09", ser2: 10 },
{ group: 2, ser1: "2020-01-10", ser2: 20 },
{ group: 2, ser1: "2020-01-11", ser2: 10 },
{ group: 2, ser1: "2020-01-12", ser2: 12 },
{ group: 3, ser1: "2020-01-13", ser2: 20 },
{ group: 3, ser1: "2020-01-14", ser2: 12 },
{ group: 3, ser1: "2020-01-15", ser2: 4 },
{ group: 3, ser1: "2020-01-16", ser2: 22 },
{ group: 3, ser1: "2020-01-17", ser2: 2 },
{ group: 3, ser1: "2020-01-18", ser2: 4 },
]
var line = d3.line()
.x(function (d) { return x(formatDate(d.ser1)); })
.y(function (d) { return y(d.ser2); })
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 50 },
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.append("defs")
.append("clipPath")
.attr("id", "chart-path")
.append("rect")
.attr("width", width - 100)
.attr("height", height)
var formatDate = d3.timeParse("%Y-%m-%d");
var bisectDate = d3.bisector(function (d) {
return d.str1;
}).left;
// Initialize a X axis:
var x = d3.scaleTime().range([0, width - 100]);
var xNum = d3.scaleLinear().range([0, width - 100])
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "myXaxis")
// Initialize an Y axis
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
.attr("class", "myYaxis")
// create the Y axis
y.domain([0, d3.max(data3, function (d) { return d.ser2 })]);
svg.selectAll(".myYaxis")
.transition()
.duration(1000)
.call(yAxis);
// Create a update selection: bind to the new data
var u = svg.selectAll(".lineTest")
.data([data3])
.enter()
.append("path")
.attr("class", "lineTest")
.attr("fill", "none")
.attr("stroke", "#0b6dbd")
.attr("stroke-width", 2.5)
.attr("clip-path", "url(#chart-path)")
// Create a function that takes a dataset as input and update the plot:
function update(data) {
// Create the X axis:
x.domain(d3.extent(data, function (d) { return formatDate(d.ser1) }));
svg.selectAll(".myXaxis")
.transition()
.duration(1000)
.call(xAxis);
u.transition()
.duration(1000)
.attr("d", line);
}
// At the beginning, I run the update function on the first dataset:
update(data3)
// this is where I try to implement a tooltip based on
// https://blockbuilder.org/bendoesdata/2c8b315d103bbaf98264efda92d313ab
function drawFocus() {
// Create focus object
let focus = svg.append('g')
.attr('class', 'focus')
// append circle on the line path
focus.append('circle')
.attr('r', 7.5)
// add background rectangle behind the text tooltip
focus.append('rect')
.attr('x', -30)
.attr('y', '-2em')
.attr('width', 70)
.attr('height', 20)
.style("fill", "white");
// add text annotation for tooltip
focus.append('text')
.attr('x', -30)
.attr('dy', '-1em')
.style("fill", "black")
.style("font-family", "SuisseIntl");
focus.append('div')
.attr('x', 10)
.attr('dy', '.35em')
.attr("class", "tooltip")
.style("opacity", 1)
// create an overlay rectangle to draw the above objects on top of
svg.append('rect')
.attr('class', 'overlay')
.attr('width', width - 100)
.attr('height', height)
.on('mouseover', () => focus.style('display', null))
.on('mouseout', () => focus.style('display', 'none'))
.on('mousemove', tipMove);
// make the overlay rectangle transparent,
// so it only serves the purpose of detecting mouse events
d3.select('.overlay')
.style('fill', 'none')
.style('pointer-events', 'all');
// select focus objects and set opacity
d3.selectAll('.focus')
.style('opacity', 0.9);
// select the circle and style it
d3.selectAll('.focus circle')
.style("fill", '#FDD511')
.style("opacity", 0)
// function that adds tooltip on hover
function tipMove() {
// below code finds the date by bisecting and
// stores the x and y coordinate as variables
let x0 = x.invert(d3.mouse(this)[0]);
let i = bisectDate(data3, x0, 1);
let d0 = data3[i - 1];
let d1 = data3[i];
let d = x0 - d0.str1 > d1.str1 - x0 ? d1 : d0;
// place the focus objects on the same path as the line
focus.attr('transform', `translate(${x(d.date)}, ${y(d.value)})`);
// position the x line
focus.select('line.x')
.attr('x1', 0)
.attr('x2', x(formatDate(d.str1)))
.attr('y1', 0)
.attr('y2', 0);
// position the y line
focus.select('line.y')
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', 0)
.attr('y2', height - y(d.str2));
// position the text
focus.select('text').text(d.str2).transition() // slowly fade in the tooltip
.duration(100)
.style("opacity", 1);
// show the circle on the path
focus.selectAll('.focus circle')
.style("opacity", 1)
};
}
drawFocus();
.tooltip {
position: absolute;
text-align: left;
font-family: "Open Sans Condensed";
font-size: 12px;
width: 80px;
height: 52px;
padding: 8px;
background: white;
pointer-events: none;
background-color: white;
line-height: 0.05em;
padding: 20px;
/* border: solid;
border-width: 1px; */
}
#my_dataviz {
padding: 20px;
}
<script src="https://d3js.org/d3.v5.js"></script>
<div id="my_dataviz"></div>
您混淆了您的命名。
ser1
你给str1
打了很多次电话。这是一个明显的迹象,表明您的原始命名ser1
和ser2
不够合乎逻辑!如果您现在不能遵循它,一年后您重新访问代码时如何?在使用数据之前解析你的数据,而不是在你使用它的时候。如果您将日期作为字符串存储在原始对象中,请在 对其进行处理之前对其进行解析。否则,当您想要比较、绘图、格式化或只是 使用 时,您会 运行 遇到问题。
就是这样,我所做的大部分更改只是在需要时将 formatDate
替换为开头的 parseDate
(这些是不同的东西),并将 str1/2
替换为 ser1/2
需要的地方。
var data3 = [
{ group: 1, ser1: "2020-01-01", ser2: 3 },
{ group: 1, ser1: "2020-01-02", ser2: 5 },
{ group: 1, ser1: "2020-01-03", ser2: 9 },
{ group: 1, ser1: "2020-01-04", ser2: 3 },
{ group: 1, ser1: "2020-01-05", ser2: 5 },
{ group: 1, ser1: "2020-01-06", ser2: 9 },
{ group: 2, ser1: "2020-01-07", ser2: 10 },
{ group: 2, ser1: "2020-01-08", ser2: 9 },
{ group: 2, ser1: "2020-01-09", ser2: 10 },
{ group: 2, ser1: "2020-01-10", ser2: 20 },
{ group: 2, ser1: "2020-01-11", ser2: 10 },
{ group: 2, ser1: "2020-01-12", ser2: 12 },
{ group: 3, ser1: "2020-01-13", ser2: 20 },
{ group: 3, ser1: "2020-01-14", ser2: 12 },
{ group: 3, ser1: "2020-01-15", ser2: 4 },
{ group: 3, ser1: "2020-01-16", ser2: 22 },
{ group: 3, ser1: "2020-01-17", ser2: 2 },
{ group: 3, ser1: "2020-01-18", ser2: 4 },
];
var parseDate = d3.timeParse("%Y-%m-%d");
// Parse the date ASAP
data3 = data3.map(function(d) {
return {
group: d.group,
ser1: parseDate(d.ser1),
ser2: d.ser2
};
});
var line = d3.line()
.x(function (d) { return x(d.ser1); })
.y(function (d) { return y(d.ser2); })
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 50 },
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.append("defs")
.append("clipPath")
.attr("id", "chart-path")
.append("rect")
.attr("width", width - 100)
.attr("height", height)
var formatDate = d3.timeParse("%Y-%m-%d");
var bisectDate = d3.bisector(function (d) {
return d.ser1;
}).left;
// Initialize a X axis:
var x = d3.scaleTime()
.range([0, width - 100])
.domain(d3.extent(data3, function (d) { return d.ser1; }));
var xNum = d3.scaleLinear().range([0, width - 100])
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "myXaxis")
// Initialize an Y axis
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
.attr("class", "myYaxis")
// create the Y axis
y.domain([0, d3.max(data3, function (d) { return d.ser2 })]);
svg.selectAll(".myYaxis")
.transition()
.duration(1000)
.call(yAxis);
// Create a update selection: bind to the new data
var u = svg.selectAll(".lineTest")
.data([data3])
.enter()
.append("path")
.attr("class", "lineTest")
.attr("fill", "none")
.attr("stroke", "#0b6dbd")
.attr("stroke-width", 2.5)
.attr("clip-path", "url(#chart-path)")
svg.selectAll(".myXaxis")
.call(xAxis);
u.attr("d", line);
// this is where I try to implement a tooltip based on
// https://blockbuilder.org/bendoesdata/2c8b315d103bbaf98264efda92d313ab
function drawFocus() {
// Create focus object
let focus = svg.append('g')
.attr('class', 'focus')
// append circle on the line path
focus.append('circle')
.attr('r', 7.5)
// add background rectangle behind the text tooltip
focus.append('rect')
.attr('x', -30)
.attr('y', '-2em')
.attr('width', 70)
.attr('height', 20)
.style("fill", "white");
// add text annotation for tooltip
focus.append('text')
.attr('x', -30)
.attr('dy', '-1em')
.style("fill", "black")
.style("font-family", "SuisseIntl");
focus.append('div')
.attr('x', 10)
.attr('dy', '.35em')
.attr("class", "tooltip")
.style("opacity", 1)
// create an overlay rectangle to draw the above objects on top of
svg.append('rect')
.attr('class', 'overlay')
.attr('width', width - 100)
.attr('height', height)
.on('mouseover', () => focus.style('display', null))
.on('mouseout', () => focus.style('display', 'none'))
.on('mousemove', tipMove);
// make the overlay rectangle transparent,
// so it only serves the purpose of detecting mouse events
d3.select('.overlay')
.style('fill', 'none')
.style('pointer-events', 'all');
// select focus objects and set opacity
d3.selectAll('.focus')
.style('opacity', 0.9);
// select the circle and style it
d3.selectAll('.focus circle')
.style("fill", '#FDD511')
.style("opacity", 0)
// function that adds tooltip on hover
function tipMove() {
// below code finds the date by bisecting and
// stores the x and y coordinate as variables
let x0 = x.invert(d3.mouse(this)[0]);
let i = bisectDate(data3, x0, 1);
let d0 = data3[i - 1];
let d1 = data3[i];
let d = x0 - d0.ser1 > d1.ser1 - x0 ? d1 : d0;
// place the focus objects on the same path as the line
focus.attr('transform', `translate(${x(d.ser1)}, ${y(d.ser2)})`);
// position the x line
focus.select('line.x')
.attr('x1', 0)
.attr('x2', x(d.ser1))
.attr('y1', 0)
.attr('y2', 0);
// position the y line
focus.select('line.y')
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', 0)
.attr('y2', height - y(d.ser2));
// position the text
focus.select('text').text(d.ser2)
.transition() // slowly fade in the tooltip
.duration(100)
.style("opacity", 1);
// show the circle on the path
focus.selectAll('.focus circle')
.style("opacity", 1)
};
}
drawFocus();
.tooltip {
position: absolute;
text-align: left;
font-family: "Open Sans Condensed";
font-size: 12px;
width: 80px;
height: 52px;
padding: 8px;
background: white;
pointer-events: none;
background-color: white;
line-height: 0.05em;
padding: 20px;
/* border: solid;
border-width: 1px; */
}
#my_dataviz {
padding: 20px;
}
<script src="https://d3js.org/d3.v5.js"></script>
<div id="my_dataviz"></div>