首页 > 解决方案 > 将鼠标悬停在堆积条形图中的线段上时将工具提示与鼠标移动对齐 [Qlik Sense Extension]

问题描述

我正在使用 Qlik Sense Cloud,并且我已经使用 d3.js v4 框架成功创建了堆积条形图。图表如下所示:

在此处输入图像描述

如您所见,工具提示未正确放置在 USA 的蓝色部分(错误放置在 Japan-Nordic 上方的空白区域)。如果您想自己测试代码,我建议使用此处的zip 文件夹并将其上传到您工作的 Qlik Sense 版本。我还包含了一个 qvf 文件。我确定我的问题在于 Qlik Sense 窗口,因为在下面附加的 jsfiddle 窗口中测试相同的代码对我有用。但它不能按预期在 Qlik Sense 上运行。

我还在 Qlik Sense 社区中上传了一个问题,但我还没有答案。

具体来说,我使用以下代码来放置我的工具提示:

```javascript
.on("mousemove", function(d) {
var xPosition = d3.event.pageX;
var yPosition = d3.event.pageY;

toolTip.style("left", xPosition + 10 + "px");
toolTip.style("top", yPosition - 25 + "px");
toolTip.style("position", "absolute");
toolTip.style("display", "inline-block");
tooltip.html(...)
}
```

data=[{dValues: "Germany", m1Values: 3323936.920000017, m2Values: 1469934.4999999946},
{dValues: "Japan", m1Values: 11847615.030000022, m2Values: 4860290.499999991},
{dValues: "Nordic", m1Values: 10382965.910000034, m2Values: 4289934.389999997},
{dValues: "Spain", m1Values: 3449601.7199999965, m2Values: 1594701.149999997},
{dValues: "UK", m1Values: 28157182.22999989, m2Values: 12590207.999999987},
{dValues: "USA", m1Values: 47691372.99999974, m2Values: 18448120.5719999}]

var quarters = data.map(function(d) { return d.dValues; })
//console.log('Quarters: ', quarters);

var layers = d3.stack().keys(["m1Values", "m2Values", "m3Values"]).order(d3.stackOrderNone).offset(d3.stackOffsetNone)(data);
//console.log('Layers: ', layers);

var max = d3.max(layers[layers.length-1], function(d) { return d[1]; });

// helper Function to round the displayed numbers
var roundNumber = function roundNumber(num, noPrecision){
//check if the string passed is number or contains formatting like 13%
if (/^[0-9.]+$/.test(num)) {
num = !noPrecision ? parseFloat(num).toFixed(2) : Math.round(num);
if (num >= 1000 && num < 1000000) {
num = !noPrecision ? parseFloat(num / 1000).toFixed(2) : Math.round(num / 1000);
if (/\.00$/.test(num)) {
num = num.replace(/\.00$/, ''); // Remove .00
}
num += 'K'; // Add the abbreviation
} else if (num >= 1000000 && num < 1000000000) {
num = !noPrecision ? parseFloat(num / 1000000).toFixed(2) : Math.round(num / 1000000);
if (/\.00$/.test(num)) {
num = num.replace(/\.00$/, ''); // Remove .00
}
num += 'M'; // Add the abbreviation
} else if (num >= 1000000000) {
num = !noPrecision ? parseFloat(num / 1000000000).toFixed(2) : Math.round(num / 1000000000);
if (/\.00$/.test(num)) {
num = num.replace(/\.00$/, ''); // Remove .00
}
num += 'T'; // Add the abbreviation
}
}
return num;
};

// Step 1: set the dimensions and margins of the graph
var margin = { top: 20, right: 20, bottom: 50, left: 60 }; //adjust left margin to show how y-axis +20 in bottom and left for X,Y axis titles
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

// Step 2: set the ranges for x, y axis
var x = d3.scaleBand()
.range([0, width])
.padding(0.3) //padding will help 

var y = d3.scaleLinear()
.range([height, 0]);

color = null;
color = d3.scaleOrdinal().range(["#98abc5", "#8a89a6", "#7b6888"]);

//Step 4: Set color domains
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "dValues"; }));

//Step 5: Create the dataset as needed for the stacked barchart
data.forEach(function(d) {
var y0 = 0;
d.values = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.values[d.values.length - 1].y1;
});

//step 6: Set rest domains
y.domain([0, d3.max(data, function(d) { return d.total; })]);
x.domain(quarters);

//-----------------------------------------------------------------------------
// Build the svg chart
var svg = d3.select("body").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 + ")");

// Create Parent Group layering
svg.append("g").attr("id", "grid");

//Add x axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));

// Add the y Axis
svg.append("g")
.attr("transform", "translate(0,0)")
.call(d3.axisLeft(y).tickFormat(d3.format(".2s")));

//toolTip
var toolTip = d3.select("body").append("div")
.attr("class", "toolTip")
.html("toolTip")
.style("display", "none");

var project_stackedbar = svg.selectAll(".project_stackedbar")
.data(data.reverse())
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + (x(d.dValues))  + ",0)"; });

var bar_environment = project_stackedbar.selectAll("rect")
.data(function(d) { return d.values; })
.enter();
bar_environment.append("rect")
.attr("class", "rect-svg")
.attr("x",  d => x(d.dValues))
.attr("y", function(d) {
return y(d.y1);
})
.attr("width", x.bandwidth() - 1)
.attr("height", function(d){
return y(d.y0) - y(d.y1);
})
.style("fill", function(d) {
return color(d.name);
});

bar_environment.append("text")
.attr("class", "text-svg")
.attr("y", function(d) {
return y(d.y1)+(y(d.y0) - y(d.y1))/2;
})
.attr("x", function(d){
return x.bandwidth()/2;
})
.attr("text-anchor","middle")
.attr("font-size","12px")
.attr("font-family","Helvetica")
.style("fill", "#000000")
.text(function(d) {
return roundNumber(d.y1 - d.y0);
});

project_stackedbar.on("mouseover", function() { toolTip.style("display", null); })
.on("mouseout", function() { toolTip.style("display", "none"); })
.on("mousemove", function(d) {
// var xPosition = d3.mouse(this)[0];
// var yPosition = d3.mouse(this)[1];
var xPosition = d3.event.pageX;
var yPosition = d3.event.pageY;
// toolTip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
toolTip.style("left", xPosition + 10 + "px");
toolTip.style("top", yPosition - 25 + "px");
toolTip.style("position", "absolute");
toolTip.style("display", "inline-block");
var elements = document.querySelectorAll(':hover');
l = elements.length
l = l-1
element = elements[l].__data__
value = element.y1 - element.y0
if(element.name === "m1Values"){
toolTip.html("Region" + ": " + (d.dValues) +
"<br>"+ "Sum(Sales)" + ": " + roundNumber(value));
} else if(element.name ==="m2Values"){
toolTip.html("Region" + ": " + (d.dValues) +
"<br>"+ "Sum(Margin)" + ": " + roundNumber(value));
}else{
toolTip.html("Region" + ": " + (d.dValues) +
"<br>"+ "Sum(GrossSales)" + ": " + roundNumber(value));
}
});

var totalLabels = svg.append('g').attr('class', 'totals');
totalLabels.selectAll('.total')
.data(data)
.enter().append('text')
.attr('class', 'total')
.attr("x", function(d) { return x(d.dValues) + x.bandwidth()/2; })
.attr("y", function(d) {
// Retrieve the horizontal coordinates based on total (stored as d.value)
// Add pixel offset so labels don't stick to end of stacked bars
return y(d.total) - 5;  })
.attr("text-anchor","middle")
.attr("font-size","12px")
.attr("font-family","Helvetica")
.style("fill", "#000000")
.text(function(d) {
// Inject total as text content (stored as d.value)
return roundNumber(d.total);
});

var legend = svg.append("g")
.attr("font-family", "Helvetica")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(["Sum(Sales)", "Sum(Margin)", "Sum(GrossSales)"])
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", color);

legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });

console.log("Stacked BarChart completed");
/* .bar {
fill: steelblue; Not needed anymore after adding the color picker
} */

div.toolTip{
position: absolute; /*we need the tooltip to follow the mouse on the screen */
background-color: white;
opacity: 0.9;
border: 1px solid #c9c9c9;
padding: 10px;
}
.gridlines line {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.gridlines path {
stroke-width: 0;
}
body {
font-family: 'Open Sans', sans-serif;
}
<script src="https://d3js.org/d3.v4.min.js"></script>https://stackoverflow.com/questions/ask#

标签: javascriptjqueryd3.jsqliksense

解决方案


推荐阅读