Drag and Change Column Value
A column chart you can edit by hand: press anywhere on the plot and drag up or down, and the column under the pointer follows. The chart becomes an input.
About this chart
When people set the values
Dragging columns is a quick way to collect a rough set of numbers: a budget split by month, a forecast, a guess in a survey. People see the shape of what they enter as they go, and the new values are in the chart’s data, ready for your code to save.
Good for
- What-if planning and rough budgets
- Surveys and estimation games
- Letting people sketch a forecast
Think twice when
- Exact figures: offer input boxes too
- Dozens of columns: hard to hit the one you mean
- Data that must not change, like reported results
For developers
Get the code.
Built with amCharts 5, a JavaScript library. The JavaScript includes your theme and renderer (canvas or SVG); the JSON also follows the changes you make with the controls.
For projects that use npm:
How it reads:
npm install @amcharts/amcharts5. See Getting started.
The same chart as a JSON config. Chart Maker reads and writes this format, the library's JSON parser turns it into a chart, and the <am5-chart> element puts it on a page with no code.How it reads:
"type" is the class to create and "settings" what you would pass to its new() in code; "properties" fill in its parts, lists such as series and axes or templates such as columns.template; "refs" holds objects used in more than one place ("#series-0" stands for the one named series-0); a string starting with "@" takes a value from another object, such as "@series.get('fill')".
A complete page. Save it as an .html file and open it in a browser.
Paste this into an AI coding assistant to build the chart from scratch. More in amCharts & AI.
// Create root element
// https://docs.amcharts.com/v5/getting-started/#root-element
var root = am5.Root.new("chartdiv");
// a theme of our own: its rules apply to every axis label
const myTheme = am5.Theme.new(root);
myTheme.rule("AxisLabel", ["minor"]).setAll({
dy: 1 // the minor labels sit 1px lower
});
myTheme.rule("AxisLabel").setAll({
fontSize: "0.9em" // all axis labels a little smaller
});
// Set themes
// https://docs.amcharts.com/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root),
myTheme,
am5themes_Responsive.new(root)
]);
// Create chart
// https://docs.amcharts.com/v5/charts/xy-chart/
var chart = root.container.children.push(am5xy.XYChart.new(root, {
wheelX: "panX", // a horizontal wheel or trackpad swipe pans...
wheelY: "zoomX", // ...and the vertical wheel zooms in on the dates
paddingLeft: 0 // the value labels sit at the chart's left edge
}));
// Add cursor
// https://docs.amcharts.com/v5/charts/xy-chart/cursor/
var cursor = chart.set("cursor", am5xy.XYCursor.new(root, {
// dragging over the plot doesn't zoom, so it is free to change the column values
behavior: "none"
}));
cursor.lineY.set("visible", false); // no horizontal cursor line
// Generate random data: one value from 30 to 90 per day
var date = new Date();
date.setHours(0, 0, 0, 0);
// the next day's data point
function generateData() {
var value = Math.round(Math.random() * 60 + 30);
am5.time.add(date, "day", 1);
return {
date: date.getTime(),
value: value
};
}
// count days of data in a row
function generateDatas(count) {
var data = [];
for (var i = 0; i < count; ++i) {
data.push(generateData());
}
return data;
}
// Create axes
// https://docs.amcharts.com/v5/charts/xy-chart/axes/
var xAxis = chart.xAxes.push(am5xy.DateAxis.new(root, {
maxDeviation: 0.2, // panning can go up to 20% past the first and last day
baseInterval: { // one column per day
timeUnit: "day",
count: 1
},
renderer: am5xy.AxisRendererX.new(root, {
minorGridEnabled: true, // faint grid lines between the main ones...
minorLabelsEnabled: true // ...with small labels for the days in between
}),
tooltip: am5.Tooltip.new(root, {}) // shows the hovered date on the axis
}));
// the minor labels show just the day of the month
xAxis.set("minorDateFormats", {
"day":"dd",
"month":"MM"
});
var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
min: 0, // columns start at zero, so a column dragged down can reach it
extraMax: 0.1, // a little room above the highest column, to drag it higher
renderer: am5xy.AxisRendererY.new(root, {
minorGridEnabled: true, // faint grid lines between the value grid lines
// dragging along the axis labels zooms the value scale
pan: "zoom"
})
}));
// Add series
// https://docs.amcharts.com/v5/charts/xy-chart/series/
var series = chart.series.push(am5xy.ColumnSeries.new(root, {
name: "Series",
xAxis: xAxis,
yAxis: yAxis,
valueYField: "value",
valueXField: "date",
tooltip: am5.Tooltip.new(root, {
labelText: "{valueY.formatNumber('#.#')}" // the value, with at most one decimal
}),
// Each column takes its own color from the series palette
colorByDataItem: true
}));
// rounded top corners, no outline
series.columns.template.setAll({ cornerRadiusTL: 5, cornerRadiusTR: 5, strokeOpacity: 0 });
// manipulating with mouse code
var isDown = false;
// register down
chart.plotContainer.events.on("pointerdown", function () {
isDown = true;
})
// register up
chart.plotContainer.events.on("globalpointerup", function () {
isDown = false;
})
// while the button is down, the hovered column takes the value under the pointer
chart.plotContainer.events.on("globalpointermove", function (e) {
// if pointer is down
if (isDown) {
// get tooltip data item
var tooltipDataItem = series.get("tooltipDataItem");
if (tooltipDataItem) {
if (e.originalEvent) {
// the pointer's height in the plot area, as a relative position on the value axis
var position = yAxis.coordinateToPosition(chart.plotContainer.toLocal(e.point).y);
// the value under the pointer, rounded to one decimal
var value = Math.round(yAxis.positionToValue(position) * 10) / 10;
// set both the value and the working value
tooltipDataItem.set("valueY", value);
tooltipDataItem.set("valueYWorking", value);
}
}
}
})
// Set data
var data = generateDatas(20);
series.data.setAll(data);
// Make stuff animate on load
// https://docs.amcharts.com/v5/concepts/animations/
series.appear(1000);
chart.appear(1000, 100);
{
"type": "XYChart",
"settings": {
"wheelX": "panX",
"wheelY": "zoomX",
"paddingLeft": 0,
"cursor": {
"type": "XYCursor",
"settings": {
"behavior": "none"
},
"properties": {
"lineY": {
"settings": {
"visible": false
}
}
}
}
},
"properties": {
"series": [
"#series-0"
],
"xAxes": [
"#axis-0"
],
"yAxes": [
"#axis-1"
]
},
"refs": [
{
"data-0": [
{
"date": 1791406800000,
"value": 54
},
{
"date": 1791493200000,
"value": 56
},
{
"date": 1791579600000,
"value": 32
},
{
"date": 1791666000000,
"value": 60
},
{
"date": 1791752400000,
"value": 41
},
{
"date": 1791838800000,
"value": 90
},
{
"date": 1791925200000,
"value": 41
},
{
"date": 1792011600000,
"value": 73
},
{
"date": 1792098000000,
"value": 33
},
{
"date": 1792184400000,
"value": 86
},
{
"date": 1792270800000,
"value": 76
},
{
"date": 1792357200000,
"value": 66
},
{
"date": 1792443600000,
"value": 51
},
{
"date": 1792530000000,
"value": 68
},
{
"date": 1792616400000,
"value": 34
},
{
"date": 1792702800000,
"value": 41
},
{
"date": 1792789200000,
"value": 87
},
{
"date": 1792875600000,
"value": 38
},
{
"date": 1792965600000,
"value": 39
},
{
"date": 1793052000000,
"value": 49
}
]
},
{
"axis-0": {
"type": "DateAxis",
"settings": {
"maxDeviation": 0.2,
"baseInterval": {
"timeUnit": "day",
"count": 1
},
"renderer": {
"type": "AxisRendererX",
"settings": {
"minorGridEnabled": true,
"minorLabelsEnabled": true
}
},
"tooltip": {
"type": "Tooltip"
},
"minorDateFormats": {
"day": "dd",
"month": "MM"
}
}
}
},
{
"axis-1": {
"type": "ValueAxis",
"settings": {
"min": 0,
"extraMax": 0.1,
"renderer": {
"type": "AxisRendererY",
"settings": {
"minorGridEnabled": true,
"pan": "zoom"
}
}
}
}
},
{
"series-0": {
"type": "ColumnSeries",
"settings": {
"name": "Series",
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "value",
"valueXField": "date",
"tooltip": {
"type": "Tooltip",
"settings": {
"labelText": "{valueY.formatNumber('#.#')}"
}
},
"colorByDataItem": true
},
"properties": {
"data": "#data-0",
"columns": {
"properties": {
"template": {
"settings": {
"cornerRadiusTL": 5,
"cornerRadiusTR": 5,
"strokeOpacity": 0
}
}
}
},
"bullets": [
{
"type": "Bullet",
"settings": {
"locationY": 1,
"sprite": {
"type": "Label",
"settings": {
"text": "{valueY.formatNumber('#.#')}",
"populateText": true,
"centerX": {
"type": "Percent",
"value": 50
},
"centerY": {
"type": "Percent",
"value": 100
},
"dy": -6,
"fontSize": 12
}
}
}
}
]
}
}
}
]
}



