问题
I am trying to add some annotations to a Google Candlestick chart. I noticed someone had already asked this same question (Adding annotations to Google Candlestick chart). The user Aperçu replied with a detailed solution to extend the chart and add annotations since the chart doesn't have any such feature built in. However, when I try this solution I get an error "TypeError: document.querySelectorAll(...)[0] is undefined"
Here is my code:
chartPoints = [
['Budget', 0, 0, 9999, 9999, 'foo1'],
['Sales', 0, 0, 123, 123, 'foo2'],
['Backlog', 123, 123, 456, 456, 'foo3'],
['Hard Forecast', 456, 456, 789, 789, 'foo4'],
['Sales to Budget', 789, 789, 1000, 1000, 'foo5']
];
var data = google.visualization.arrayToDataTable(chartPoints, true);
data.setColumnProperty(5, 'role', 'annotation');
var options = {
legend: 'none',
bar: { groupWidth: '40%', width: '100%' },
candlestick: {
fallingColor: { strokeWidth: 0, fill: '#a52714' },
risingColor: { strokeWidth: 0, fill: '#0f9d58' }
}
};
var chart = new google.visualization.CandlestickChart(document.getElementById('chart_div'));
chart.draw(data, options);
// attempt to use Aperçu's solution
const bars = document.querySelectorAll('#chart_div svg > g:nth-child(5) > g')[0].lastChild.children // this triggers a TypeError
for (var i = 0 ; i < bars.length ; i++) {
const bar = bars[i]
const { top, left, width } = bar.getBoundingClientRect()
const hint = document.createElement('div')
hint.style.top = top + 'px'
hint.style.left = left + width + 5 + 'px'
hint.classList.add('hint')
hint.innerText = rawData.filter(t => t[1])[i][0]
document.getElementById('chart_div').append(hint)
}
I want the chart to show the last piece of data next to the bars (i.e. "foo1", "foo2", etc)
回答1:
each candle or bar will be represented by a <rect>
element
we can use the rise and fall colors to separate the bars from other <rect>
elements in the chart
there will be the same number of bars as rows in the data table
once we find the first bar, we can use rowIndex
of zero to pull values from the data
we need to find the value of the rise / fall, to know where to place the annotation
then use chart methods to find the location for the annotation
getChartLayoutInterface()
- Returns an object containing information about the onscreen placement of the chart and its elements.
getYLocation(position, optional_axis_index)
- Returns the screen y-coordinate of position relative to the chart's container.
see following working snippet
two annotations are added
one for the difference in rise and fall
and the other for the value in the column with annotation role
google.charts.load('current', {
callback: drawChart,
packages: ['corechart']
});
function drawChart() {
var chartPoints = [
['Budget', 0, 0, 9999, 9999, 'foo1'],
['Sales', 0, 0, 123, 123, 'foo2'],
['Backlog', 123, 123, 456, 456, 'foo3'],
['Hard Forecast', 456, 456, 789, 789, 'foo4'],
['Sales to Budget', 789, 789, 1000, 1000, 'foo5']
];
var data = google.visualization.arrayToDataTable(chartPoints, true);
data.setColumnProperty(5, 'role', 'annotation');
var options = {
legend: 'none',
bar: { groupWidth: '40%', width: '100%' },
candlestick: {
fallingColor: { strokeWidth: 0, fill: '#a52714' },
risingColor: { strokeWidth: 0, fill: '#0f9d58' }
}
};
var container = document.getElementById('chart_div');
var chart = new google.visualization.CandlestickChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
var annotation;
var bars;
var chartLayout;
var formatNumber;
var positionY;
var positionX;
var rowBalance;
var rowBottom;
var rowIndex;
var rowTop;
var rowValue;
var rowWidth;
chartLayout = chart.getChartLayoutInterface();
rowIndex = 0;
formatNumber = new google.visualization.NumberFormat({
pattern: '#,##0'
});
bars = container.getElementsByTagName('rect');
for (var i = 0; i < bars.length; i++) {
switch (bars[i].getAttribute('fill')) {
case '#a52714':
case '#0f9d58':
rowWidth = parseFloat(bars[i].getAttribute('width'));
if (rowWidth > 2) {
rowBottom = data.getValue(rowIndex, 1);
rowTop = data.getValue(rowIndex, 3);
rowValue = rowTop - rowBottom;
rowBalance = Math.max(rowBottom, rowTop);
positionY = chartLayout.getYLocation(rowBalance) - 6;
positionX = parseFloat(bars[i].getAttribute('x'));
// row value
annotation = container.getElementsByTagName('svg')[0].appendChild(container.getElementsByTagName('text')[0].cloneNode(true));
annotation.textContent = formatNumber.formatValue(rowValue);
annotation.setAttribute('x', (positionX + (rowWidth / 2)));
annotation.setAttribute('y', positionY);
annotation.setAttribute('font-weight', 'bold');
// annotation column
annotation = container.getElementsByTagName('svg')[0].appendChild(container.getElementsByTagName('text')[0].cloneNode(true));
annotation.textContent = data.getValue(rowIndex, 5);
annotation.setAttribute('x', (positionX + (rowWidth / 2)));
annotation.setAttribute('y', positionY - 18);
annotation.setAttribute('font-weight', 'bold');
rowIndex++;
}
break;
}
}
});
chart.draw(data, options);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
来源:https://stackoverflow.com/questions/46064803/adding-annotations-to-google-candlestick-chart-posted-solution-triggers-typeerr