nnnick chart.js - Custom Tooltip on Line Chart - chart.js

We are using nnnick chart.js (open source chart) in our application for reporting purpose.There is a requirement to show the Custom tool-tip in the line chart.
As of now , Normal chart tooltip is showing based on the X-axis and Y axis dataset values. But Here we want to show the Dynamic additional data in the Tooltip.
For Example ,
Let us take a Student Enrollment .
here
X Axis Value - Enrollment Month (Jan,Feb,Mar....etc)
Y Axis Value - Number of Enrollments (10,20,30...ect)
After the Line chart is plotted , Now it is displaying (Jan ,10) in the tooltip.
We have to show the Number of Male & Female student details in the tool tip On mouse over the data point Jan 10 (i.e) (Jan,10, Male:5 , Female 5 ).
If you see the above screen shot , Green color is toop-tip is the normal one which is a built-in option. Red Color highlighted tool-tip is the one we are expecting.
Please provide any suggestion on this .

So you can achieve this using the custom tool tip function in the newer (not sure when it was included) version of chart js. You can have it display anything you want in place of a normal tooltip so in this case i have added a tooltip and a tooltip-overview.
The really annoying thing is though in this function you are not told which index you are currently showing a tooltip for. Two ways to solve this, override the showToolTip function so it actually passes this information or do a quick little hack to extract the label from the tooltip text and get the index from the labels array (hacky but quicker so i went for this one in the example)
So here is a quick example based upon the samples in chartjs samples folder. This is just a quick example so you would prob need to play around with the positioning and stuff until its what you need.
Chart.defaults.global.pointHitDetectionRadius = 1;
Chart.defaults.global.customTooltips = function(tooltip) {
// Tooltip Element
var tooltipEl = $('#chartjs-tooltip');
var tooltipOverviewEl = $('#chartjs-tooltip-overview');
// Hide if no tooltip
if (!tooltip) {
tooltipEl.css({
opacity: 0
});
tooltipOverviewEl.css({
opacity: 0
});
return;
}
//really annoyingly we don;t get told which index this comes from so going to have
//to extract the label from the text :( and then find the index based on that
//other option here is to override the the whole showTooltip in chartjs and have the index passed
var label = tooltip.text.substr(0, tooltip.text.indexOf(':'));
var labelIndex = labels.indexOf(label);
var maleEnrolmentNumber = maleEnrolments[labelIndex];
var femaleEnrolmentNumber = FemaleEnrolments[labelIndex];
// Set caret Position
tooltipEl.removeClass('above below');
tooltipEl.addClass(tooltip.yAlign);
// Set Text
tooltipEl.html(tooltip.text);
//quick an ddirty could use an actualy template here
var tooltipOverviewElHtml = "<div> Overall : " + (maleEnrolmentNumber + femaleEnrolmentNumber) + "</div>";
tooltipOverviewElHtml += "<div> Male : " + (maleEnrolmentNumber) + "</div>";
tooltipOverviewElHtml += "<div> Female : " + (femaleEnrolmentNumber) + "</div>";
tooltipOverviewEl.html(tooltipOverviewElHtml);
// Find Y Location on page
var top;
if (tooltip.yAlign == 'above') {
top = tooltip.y - tooltip.caretHeight - tooltip.caretPadding;
} else {
top = tooltip.y + tooltip.caretHeight + tooltip.caretPadding;
}
// Display, position, and set styles for font
tooltipEl.css({
opacity: 1,
left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
top: tooltip.chart.canvas.offsetTop + top + 'px',
fontFamily: tooltip.fontFamily,
fontSize: tooltip.fontSize,
fontStyle: tooltip.fontStyle,
});
tooltipOverviewEl.css({
opacity: 1,
fontFamily: tooltip.fontFamily,
fontSize: tooltip.fontSize,
fontStyle: tooltip.fontStyle,
});
};
var maleEnrolments = [5, 20, 15, 20, 20, 30, 50]; // Integer array for male each value is corresponding to each month
var FemaleEnrolments = [5, 0, 15, 20, 30, 30, 20]; // Integer array for Female each value is corresponding to each month
var labels = ["Jan", "February", "March", "April", "May", "June", "July"]; //Enrollment by Month
var lineChartData = {
labels: labels,
datasets: [{
label: "Student Details",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [10, 20, 30, 40, 50, 60, 70], //enrollement Details overall (Male + Female)
}]
};
var ctx2 = document.getElementById("chart2").getContext("2d");
window.myLine = new Chart(ctx2).Line(lineChartData, {
responsive: true
});
#canvas-holder1 {
width: 300px;
margin: 20px auto;
}
#canvas-holder2 {
width: 50%;
margin: 20px 25%;
position:relative;
}
#chartjs-tooltip-overview {
opacity: 0;
position: absolute;
background: rgba(0, 0, 0, .7);
color: white;
padding: 3px;
border-radius: 3px;
-webkit-transition: all .1s ease;
transition: all .1s ease;
pointer-events: none;
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
left:200px;
top:0px
}
#chartjs-tooltip {
opacity: 1;
position: absolute;
background: rgba(0, 0, 0, .7);
color: white;
padding: 3px;
border-radius: 3px;
-webkit-transition: all .1s ease;
transition: all .1s ease;
pointer-events: none;
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.chartjs-tooltip-key {
display:inline-block;
width:10px;
height:10px;
}
<script src="https://raw.githack.com/nnnick/Chart.js/master/Chart.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="canvas-holder2">
<div id="chartjs-tooltip-overview"></div>
<div id="chartjs-tooltip"></div>
<canvas id="chart2" width="600" height="600" />
</div>

Related

How to change color of the sliders in r shiny? [duplicate]

I tried to make different color for a few sliderInput bar in R shiny. It requires css etc. I looked online and can only find how to make one sliderInput. How can I make create several different color to different bars?
Here are my testing code. It will show all bar in same style:
ui <- fluidPage(
tags$style(type = "text/css", "
.irs-bar {width: 100%; height: 25px; background: black; border-top: 1px solid black; border-bottom: 1px solid black;}
.irs-bar-edge {background: black; border: 1px solid black; height: 25px; border-radius: 0px; width: 20px;}
.irs-line {border: 1px solid black; height: 25px; border-radius: 0px;}
.irs-grid-text {font-family: 'arial'; color: white; bottom: 17px; z-index: 1;}
.irs-grid-pol {display: none;}
.irs-max {font-family: 'arial'; color: black;}
.irs-min {font-family: 'arial'; color: black;}
.irs-single {color:black; background:#6666ff;}
.irs-slider {width: 30px; height: 30px; top: 22px;}
.irs-bar1 {width: 50%; height: 25px; background: red; border-top: 1px solid black; border-bottom: 1px solid black;}
.irs-bar-edge1 {background: black; border: 1px solid red; height: 25px; border-radius: 0px; width: 20px;}
.irs-line1 {border: 1px solid red; height: 25px; border-radius: 0px;}
.irs-grid-text1 {font-family: 'arial'; color: white; bottom: 17px; z-index: 1;}
.irs-grid-pol1 {display: none;}
.irs-max1 {font-family: 'arial'; color: red;}
.irs-min1 {font-family: 'arial'; color: red;}
.irs-single1 {color:black; background:#6666ff;}
.irs-slider1 {width: 30px; height: 30px; top: 22px;}
"),
uiOutput("testSlider")
)
server <- function(input, output, session){
output$testSlider <- renderUI({
fluidRow(
column(width=3,
box(
title = "Preferences", width = NULL, status = "primary",
sliderInput(inputId="test", label=NULL, min=1, max=10, value=5, step = 1, width='100%'),
sliderInput(inputId="test2", label=NULL, min=1, max=10, value=5, step = 1, width='50%')
)
))
})
}
shinyApp(ui = ui, server=server)
Below is a sample code of how you can modify style of the sliders. You can add your own logic to it.
rm(list = ls())
library(shiny)
ui <- fluidPage(
# All your styles will go here
tags$style(HTML(".js-irs-0 .irs-single, .js-irs-0 .irs-bar-edge, .js-irs-0 .irs-bar {background: purple}")),
tags$style(HTML(".js-irs-1 .irs-single, .js-irs-1 .irs-bar-edge, .js-irs-1 .irs-bar {background: red}")),
tags$style(HTML(".js-irs-2 .irs-single, .js-irs-2 .irs-bar-edge, .js-irs-2 .irs-bar {background: green}")),
sliderInput("slider1", "Slider 1",min = 0.1, max = 1, value = 0.4, step = 0.05),
sliderInput("slider2", "Slider 2",min = 0.1, max = 1, value = 0.4, step = 0.05),
sliderInput("slider3", "Slider 3",min = 100, max = 20000, value = 5000, step= 200)
)
server <- function(input, output, session){}
shinyApp(ui = ui, server=server)
The previous answer unfortunately only changed the bar colour for me and not the number tags above. This did the trick for the rest of them. (Replace the hex colour codes with what ever colour you like).
/* changes the colour of the bars */
tags$head(tags$style(HTML('.js-irs-0 .irs-single, .js-irs-0 .irs-bar-edge, .js-irs-0 .irs-bar {
background: #000069;
border-top: 1px solid #000039 ;
border-bottom: 1px solid #000039 ;}
/* changes the colour of the number tags */
.irs-from, .irs-to, .irs-single { background: #000069 }'
))
)
The above solutions will not work when sliders are generated dynamically and reused, since it relies on the counting of the class of the container (".js-irs-0"). When reinitializing the slider, the count will increase. Compare sliders 1 vs 2 and 3 in the example below.
If CSS would allow parent-selectors, one could use the id of the input to select the elements needed (id does not change). Since this is not posssible, another solution is needed. Fortunately the label has a for=id-attribute, which can be used to select the following sibblings - the span-elements containing bars etc. I have also highlighted the label of slider 2 for better understanding. See also this overview of CSS-selectors.
library(shiny)
library(shinyjs)
`%||%` <- function(a, b) {
if (!is.null(a)) a else b
}
NUM_PAGES <- 3
ui<- fluidPage(
useShinyjs(),
tags$head(tags$style(HTML('.js-irs-1 .irs-single, .js-irs-1 .irs-bar-edge, .js-irs-1 .irs-bar {
background: purple;
}
'
))
),
tags$head(tags$style(HTML(' [for=sl2]+span>.irs>.irs-single, [for=sl2]+span>.irs-bar-edge, [for=sl2]+span>.irs-bar {
background: green;}
[for=sl2]{color:red;}
[for=sl3]+span>.irs>.irs-single, [for=sl3]+span>.irs-bar-edge, [for=sl3]+span>.irs-bar {
background: grey;}
'
))
),
uiOutput("ui"),
br(),
actionButton("prevBtn", "< Previous"),
actionButton("nextBtn", "Next >")
)
server<- function(input, output, session) {
rv <- reactiveValues(page = 1)
uilist <- reactive(list(
sliderInput("sl1","Dies ist slider 1", 1,101,input$sl1%||%11),
sliderInput("sl2","Dies ist slider 2", 2,102,input$sl2%||%22),
sliderInput("sl3","Dies ist slider 3", 3,103,input$sl3%||%33)
))
observeEvent(rv$page,{
toggleState(id = "nextBtn", condition = rv$page < NUM_PAGES+1)
if(rv$page <= NUM_PAGES){
#Einzelne Slider
toggleState(id = "prevBtn", condition = rv$page > 1)
output$ui<- renderUI(uilist()[[rv$page]])
}else{
#Am Ende Gesamtliste
output$ui<- renderUI(uilist())
}
})
navPage <- function(direction) {
rv$page <- rv$page + direction
}
observeEvent(input$prevBtn, navPage(-1))
observeEvent(input$nextBtn, navPage(1))
}
shinyApp(ui, server)

Customizing layout of raido buttons in Shiny

I have a set of several radio button and would like the ability to customize the layout more detailed, I know you can choose vertical or horizontal layout but I would like to combine the two. What I currently have is shown in the below picture, is it possible to keep the first 5 buttons in the same vertical column, then make the next set of 5 buttons in a vertical column to the right of the first 5, and then the last 2 in a third column to the right of those 5?
Current code:
radioButtons("b_Bet_Sizes", "",
choiceNames = list("17.5%", "35%", "62.5%", "75%", "90%", "125%", "150%", "175%","300%","400%","geo2","geo3"),
choiceValues = c(0.175,0.35, 0.625, 0.75, 0.9,1.25,1.5,1.75,3,4,"geo2","geo3")
),
You can play with CSS to meet your needs. Try this
css <- "
.shiny-options-group {
height: 100px;
width: 600px;
-webkit-column-count: 3; /* Chrome, Safari, Opera */
-moz-column-count: 3; /* Firefox */
row-count: 5;
-webkit-column-fill: auto;
-moz-column-fill: auto;
column-fill: auto;
margin-top: 0px;
}
.control-label {
padding-bottom: 10px;
}
div.radio {
margin-top: 0px;
margin-bottom: 0px;
padding-bottom: 5px;
}
"
radioLab <-list(tags$div(align = 'left',
class = 'multicol',
radioButtons("b_Bet_Sizes", "",
choiceNames = list("17.5%", "35%", "62.5%", "75%", "90%", "125%", "150%", "175%","300%","400%","geo2","geo3"),
choiceValues = c(0.175,0.35, 0.625, 0.75, 0.9,1.25,1.5,1.75,3,4,"geo2","geo3")
), style = "font-size:75%"))
ui <- shinyUI(
navbarPage("TITLE",
tabPanel("TABULATE",
tags$head(tags$style(HTML(css))),
fluidRow(
column(width = 6, radioLab, align = "center"),
column(6)
)
)))
server <- shinyServer(function(input, output) {
})
shinyApp(ui,server)

Chart.js: Thousand Separator and Tittle ind the Middle of the Doughnut

I have searched a lot and included a thousand separator in tooltips. But I would like to make it work everywhere there is text. Where I live we use "." to separate thousands and "," for decimal.
I didn't find a simple way to put the title in the middle of the doughnut.
This is what I have:
Chart.defaults.global.defaultFontColor = '#7792b1';
var ctx = document.getElementById('myChart').getContext('2d');
var dataset = [{
label: 'Volume de Operações',
data: [254000.87, 355000.57],
backgroundColor: ['#4bb8df', '#6290df']
}]
var chart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['CALL', 'PUT'],
datasets: dataset
},
options: {
rotation: 1 * Math.PI,
circumference: 1 * Math.PI,
legend: {
display: false
},
cutoutPercentage: 60,
plugins: {
labels: [{
render: 'label',
arc: true,
fontStyle: 'bold',
position: 'outside'
}, {
render: 'percentage',
fontColor: '#ffffff',
precision: 1
}],
},
title: {
display: true,
fontSize: 15,
text: [
dataset.reduce((t, d) => t + d.data.reduce((a, b) => a + b), 0),
'Volume Total'
],
position: 'bottom'
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var dataLabel = data.labels[tooltipItem.index];
var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].toLocaleString();
if (Chart.helpers.isArray(dataLabel)) {
dataLabel = dataLabel.slice();
dataLabel[0] += value;
} else {
dataLabel += value;
}
return dataLabel;
}
}
}
}
});
<canvas id="myChart" style="max-width: 450px"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<script src="https://cdn.jsdelivr.net/gh/emn178/chartjs-plugin-labels/src/chartjs-plugin-labels.js"></script>
In short:
Global thousand separator: (.)
Title in the middle of doughnut
Tooltip without label: Only value
To not show a title in your tooltip you will have to only return the value in your custom label calback. So your callback will become this:
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].toLocaleString();
}
There is no build in way to get the title in the middle of the circle, you will have to write a custom plugin for that.
To replace the thousand serperator in the percentage label, you will have to write a custom render. So instead render: 'percentage'. You will get something like this:
// custom render
{
render: function (args) {
// args will be something like:
// { label: 'Label', value: 123, percentage: 50, index: 0, dataset: {...} }
return '$' + args.value;
}
}
You will have to make the logic so the value gets transformed to a percentage still
EDIT custom tooltip so you dont see the color in front.
Chart.defaults.global.defaultFontColor = '#7792b1';
var ctx = document.getElementById('myChart').getContext('2d');
var dataset = [{
label: 'Volume de Operações',
data: [254000.87, 355000.57],
backgroundColor: ['#4bb8df', '#6290df']
}]
var chart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['CALL', 'PUT'],
datasets: dataset
},
options: {
rotation: 1 * Math.PI,
circumference: 1 * Math.PI,
legend: {
display: false
},
cutoutPercentage: 60,
plugins: {
labels: [{
render: 'label',
arc: true,
fontStyle: 'bold',
position: 'outside'
}, {
render: 'percentage',
fontColor: '#ffffff',
precision: 1
}],
},
title: {
display: true,
fontSize: 15,
text: [
dataset.reduce((t, d) => t + d.data.reduce((a, b) => a + b), 0),
'Volume Total'
],
position: 'bottom'
},
tooltips: {
enabled: false,
custom: function(tooltipModel) {
// Tooltip Element
var tooltipEl = document.getElementById('chartjs-tooltip');
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = '<table></table>';
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
// Set caret Position
tooltipEl.classList.remove('above', 'below', 'no-transform');
if (tooltipModel.yAlign) {
tooltipEl.classList.add(tooltipModel.yAlign);
} else {
tooltipEl.classList.add('no-transform');
}
function getBody(bodyItem) {
return bodyItem.lines[0].split(': ')[1].replace('.', ',');
}
// Set Text
if (tooltipModel.body) {
var titleLines = tooltipModel.title || [];
var bodyLines = tooltipModel.body.map(getBody);
var innerHtml = '<thead>';
innerHtml += '</thead><tbody>';
bodyLines.forEach(function(body, i) {
var colors = tooltipModel.labelColors[i];
var style = 'background:' + colors.backgroundColor;
style += '; border-color:' + colors.borderColor;
style += '; border-width: 2px';
var span = '<span style="' + style + '"></span>';
innerHtml += '<tr><td>' + span + body + '</td></tr>';
});
innerHtml += '</tbody>';
var tableRoot = tooltipEl.querySelector('table');
tableRoot.innerHTML = innerHtml;
}
// `this` will be the overall tooltip
var position = this._chart.canvas.getBoundingClientRect();
// Display, position, and set styles for font
tooltipEl.style.opacity = 1;
tooltipEl.style.position = 'absolute';
tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX + 'px';
tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + 'px';
tooltipEl.style.fontFamily = tooltipModel._bodyFontFamily;
tooltipEl.style.fontSize = tooltipModel.bodyFontSize + 'px';
tooltipEl.style.fontStyle = tooltipModel._bodyFontStyle;
tooltipEl.style.padding = tooltipModel.yPadding + 'px ' + tooltipModel.xPadding + 'px';
tooltipEl.style.pointerEvents = 'none';
}
/*
callbacks: {
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].toLocaleString();
},
}*/
}
}
});
#chartjs-tooltip {
opacity: 1;
position: absolute;
background: rgba(0, 0, 0, .7);
color: white;
border-radius: 3px;
-webkit-transition: all .1s ease;
transition: all .1s ease;
pointer-events: none;
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
<canvas id="myChart" style="max-width: 450px"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<script src="https://cdn.jsdelivr.net/gh/emn178/chartjs-plugin-labels/src/chartjs-plugin-labels.js"></script>
You might want to add some dots as thousand seperators in the tooltip but thats up to you. Best place to do it is in the getBody method.

How to add image to chart.js tooltip?

i'm using Chart.js to build a line graph by specific directions from a designer, and I want my tooltip to include a small icon.
is it possible ?
You can override the customTooltips function.
var myLineChart = new Chart(ctx).Line(data, {
customTooltips: function (tooltip) {
var tooltipEl = $('#chartjs-tooltip');
if (!tooltip) {
tooltipEl.css({
opacity: 0
});
return;
}
tooltipEl.removeClass('above below');
tooltipEl.addClass(tooltip.yAlign);
// split out the label and value and make your own tooltip here
var parts = tooltip.text.split(":");
var innerHtml = '<img src="pathTomyImage/myImage.png"> <span>' + parts[0].trim() + '</span> : <span><b>' + parts[1].trim() + '</b></span>';
tooltipEl.html(innerHtml);
tooltipEl.css({
opacity: 1,
left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
top: tooltip.chart.canvas.offsetTop + tooltip.y + 'px',
fontFamily: tooltip.fontFamily,
fontSize: tooltip.fontSize,
fontStyle: tooltip.fontStyle,
});
}
});
Replace pathTomyImage/myImage.png with your image URL (you could also pick this from a lookup using parts[0] - which is the x axis label, or easier still give your images a name depending on the axis label. eg. January.png, February.png)
Make sure you add the following markup as well
<div id="chartjs-tooltip"></div>
Fiddle - http://jsfiddle.net/02xrgy10/

Drawing visual Lines in Google Charts

I'm writing a Google Chart. It has stacked columns. On top of that I want to draw 2 lines, which indicate min and max allowed value.
The only solution I came up with, was modifying the first example of ComboCharts. My result looks like this:
Which isn't sufficient. The graph is variable, so if there's only 1 Quartal shown, the line will solely be a dot. My Questions are:
Is there a way to draw the line further, so it hits the left and right boundary of the Graph?
Can I draw markup lines into the graph, without pretending it's another datapoint?
You can fiddle with a ComboChart here if you want.
You can't get the lines to go edge-to-edge with a discrete (string-based) x-axis. If you switch to a continuous (number, date, datetime, timeofday) axis, then you can add one row before your real data and one row after that contain the goal lines (and nulls for the other data series):
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('number', 'Quarter');
data.addColumn('number', 'Value 1');
data.addColumn('number', 'Value 2');
data.addColumn('number', 'Value 3');
data.addColumn('number', 'Goal 1');
data.addColumn('number', 'Goal 2');
data.addRows([
[0, null, null, null, 10, 14],
[1, 5, 4, 7, null, null],
[2, 6, 9, 6, null, null],
[3, 2, 6, 4, null, null],
[4, 3, 6, 4, null, null],
[5, null, null, null, 10, 14]
]);
var chart = new google.visualization.ComboChart(document.querySelector('#chart_div'));
chart.draw(data, {
height: 400,
width: 600,
isStacked: true,
legend: {
position: 'top'
},
seriesType: 'bars',
interpolateNulls: true,
series: {
3: {
type: 'line'
},
4: {
type: 'line'
}
},
hAxis: {
format: 'Q#',
ticks: [1, 2, 3, 4],
viewWindow: {
min: 0.5,
max: 4.5
}
},
chartArea: {
left: '10%',
width: '80%'
}
});
}
google.load('visualization', '1', {packages:['corechart'], callback: drawChart});
See working example: http://jsfiddle.net/asgallant/W67qU/
Here is some explanation of what is going on (edit on Nov 24, 2022 by Jorr.it):
At the top and bottom of the DataTable there are extra rows added with the goals only. With the hAxis.viewWindow option the two new goal dots are just cut off the chart, but resulting in a full line over the whole width of the chart. Finally option "interpolateNulls" needs to be set to connect the two invisible dots "over" the null values in the bar rows.
Maybe a bit late but I faced the same issue. I was trying to set max and min lines into a line chart with a lot of data points in the serie and I wanted to avoid adding new series with a lot of repeated points so I used overlays ( https://developers.google.com/chart/interactive/docs/overlays#javascript2 ).
Here are an example, It's just a draft in which I'm working now but maybe can help:
<html>
<head>
<script
type="text/javascript"
src="https://www.gstatic.com/charts/loader.js"
></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<style>
#container {
position: relative;
width: 900px;
height: 500px;
}
.min-bar {
height: 1px;
background-color: red;
position: absolute;
left: 0;
right: 0;
}
</style>
<script type="text/javascript">
$(function() {
$.get(
"https://firebasestorage.googleapis.com/v0/b/manasav-pricetracker.appspot.com/o/products%2F-L6O-CtBKZAc2NTCFq7Z.data?alt=media&token=60e06bb6-59b7-41a9-8fd0-f82f4ddc75f2",
function(data) {
google.charts.load("current", { packages: ["corechart"] });
google.charts.setOnLoadCallback(drawChart);
var downloadedData = JSON.parse("[" + data);
function drawChart() {
var dataTable = [["Time", "New"]];
let min = Number.MAX_VALUE;
let rowMin;
for (var i in downloadedData) {
var d = downloadedData[i];
if (d.new < min) {
rowMin = i;
min = d.new;
}
dataTable.push([new Date(d.date), d.new]);
}
var data = google.visualization.arrayToDataTable(dataTable);
var options = {
title: "Price evolution",
legend: { position: "bottom" },
trendlines: { 0: {} }
};
var chart = new google.visualization.LineChart(
document.getElementById("curve_chart")
);
function placeMarker(dataTable) {
var cli = this.getChartLayoutInterface();
var chartArea = cli.getChartAreaBoundingBox();
document.querySelector(".min-bar").style.top =
Math.floor(cli.getYLocation(min)) + "px";
document.querySelector(".min-bar").style.left =
Math.floor(cli.getXLocation(dataTable.getValue(0,0))) - 25 + "px";
document.querySelector(".min-bar").style.right =
(document.querySelector("#container").offsetWidth - Math.floor(cli.getXLocation(dataTable.getValue(dataTable.getNumberOfRows()-1,0)))) - 25 + "px";
// document.querySelector(".min-bar").style.top =
// Math.floor(cli.getXLocation(dataTable.getValue(rowMin, 1))) +
// "px";
}
google.visualization.events.addListener(
chart,
"ready",
placeMarker.bind(chart, data)
);
chart.draw(data, options);
}
}
);
});
</script>
</head>
<body>
<div id="container">
<div id="curve_chart" style="width: 900px; height: 500px"></div>
<div class="min-bar"></div>
</div>
</body>
</html>
Jsfiddle demo => https://jsfiddle.net/jRubia/8z7ao1nh/