While trying to create a funnel chart Mod in Spotfire using Plotly.js library, somehow plotly events are not working. Following the plotly documentation, we tried to add "plotly_selected", "plotly_hover" and "plotly_unhover" events which are triggered in the connecting area between the points rather than actual points.
We notice that the events are triggered when the mouse pointer is outside of the plotly chart elements. However, when we move the mouse pointer over the chart elements, the events are not triggered. We are unsure as to how to troubleshoot further. The code we have written works in a web browser, but not inside our application (Spotfire).
Below is Data preparation for chart:
for (const colorLeaf of colorLeafNodes) {
data.push({
type: "funnel",
name: colorLeaf.formattedPath(),
y: colorLeaf.rows().map((y) => y.categorical("Category").formattedValue()),
x: colorLeaf.rows().map((x) => x.continuous("Value Axis").value()),
textposition: "inside",
hoverinfo: "none",
textinfo: "value+percent initial",
textfont: {
family: styling.general.font.fontFamily,
size: styling.general.font.fontSize,
color: styling.general.font.color.bgBlack
},
marker: {
color: colorLeaf.rows().map((row) => row.color().hexCode)
}
});
}
Here is the Chart Div:
var chartDiv = document.getElementById("mod-container");
Plotly marking event
chartDiv
.on("plotly_selected", function (eventData) {
console.log("plotly_selected");
console.log(eventData.points);
let categories = [];
let colorValues = [];
if (eventData != null) {
if (eventData.points.length == 0) {
dataView.clearMarking();
return;
}
eventData.points.forEach((d) => {
console.log(d);
categories.push(d.y);
colorValues.push(d.data.name);
});
let rowsToMark = rows.filter(
(d) =>
categories.includes(d.categorical("Category").formattedValue()) &&
colorValues.includes(d.categorical("Color").formattedValue())
);
dataView.mark(rowsToMark);
}
})
After deeper troubleshooting, we found that CSS code was the troublemaker. After removing .points g path { pointer-events: all; } from CSS, plotly events were working smoothly with Mods.
Related
My chart looks like so::
and here is my code:
linePlot = Plot.plot({
marginLeft: 60, // space to the left of the chart
y: {
type: "log", // set the type
},
marks: [
Plot.line(data, {x: "timestamp", y: "views", z:"artist", title: d=>`${d.artist}`,})
]
})
I want to highlight or change color of each line when the mouse is over it.
The easiest thing to do would be to attach a pointerenter event to the lines. Since you're using Observable, to use D3 to handle that process. Here's what it looks like on Observable:
https://observablehq.com/d/2e1daf099a7aaaea
To be clear, you are using two libraries: D3 and Plot, both of which are automatically available on Observable. You can use them both in vanilla Javascript pretty easily, though:
// Manufacture some data
let pt_lists = d3.range(10).map(() => {
let cur = 0;
return d3.range(1000).map(function(x) {
let step = 2 * d3.randomInt(0, 2)() - 1;
cur = cur + step;
return [x, cur];
});
});
// Plot the data
let plot = Plot.plot({
marks: pt_lists.map((pts) => Plot.line(pts, {
strokeWidth: 2
}))
});
// Here's where the action is.
// We use d3 to select all the paths in the plot
d3.select(plot)
.selectAll("path")
// React when the pointer hovers over the path.
.on("pointerenter", function() {
d3.select(plot).selectAll("path").attr("opacity", 0.2);
d3.select(this).attr("opacity", 1);
});
// Reset the appearance when the pointer leaves the SVG
d3.select(plot).on("pointerleave", function() {
d3.select(plot).selectAll("path").attr("opacity", 1);
});
// Attach the plot to the container DIV
d3.select('#chart').append(() => plot)
<script src="https://cdn.jsdelivr.net/npm/d3#7"></script>
<script src="https://cdn.jsdelivr.net/npm/#observablehq/plot#0.6"></script>
<div id="chart" style="width: 600px; height: 400px"></div>
It might also be possible to do the interaction in css:
d3.select(chart)
.append("svg:style")
.text(`
path:hover {stroke-width: 2px;}
`)
I want plotlines to be rendered the last(right now area is overlaying them). If i use Zindex width increases and it doesn't look as neath, i also try line-width.
I did this approach for markers to be above plotlines but it doesnt work for plotlines above area. Am i doing something wrong?
componentDidRender() {
if(this.shadowRoot) {
var markers0 = this.shadowRoot.querySelector('.highcharts-markers.highcharts-series-0');
var plotLines0 = this.shadowRoot.querySelector('.highcharts-plot-lines-4');
var area0 = this.shadowRoot.querySelector('.highcharts-area-series');
/// plotlines below markers - works
if(plotLines0 && plotLines0.parentNode && markers0)
plotLines0.parentNode.insertBefore(markers0, plotLines0.nextSibling);
// markers below area - works
// markers0.parentNode.insertBefore(area0, markers0.nextSibling);
//area below plotlines- doesnt work
//if(area0 && area0.parentNode && plotLines0)
//area0.parentNode.insertBefore(plotLines0, area0.nextSibling);
if i run that code for area below plotlines nothing changes.
You can remove the second to last grid line by:
chart: {
events: {
load: function() {
var prevTick;
Highcharts.objectEach(this.yAxis[0].ticks, function(tick) {
if (tick.isLast) {
prevTick.gridLine.destroy();
}
prevTick = tick;
});
}
}
},
Live demo: http://jsfiddle.net/BlackLabel/obam5yxg/
Is there a way to override the reset zoom button click event in highcharts? When I click the reset zoom button, I want the chart to display the specific interval using setExtremes instead of zooming out the whole chart.
you can capture it in the selection property of your chart object. Just use the setExtremes()function with where you want to zoom, and a timeout of 0ms so that the graph's 'reset graph' function doesn't overwrite it.
chart: {
events: {
selection: function(event) {
if (!event.xAxis) {
setTimeout(function(){chart.xAxis[0].setExtremes(0, 5)}, 0);
}
}
...
}
working example: http://jsfiddle.net/rhavelka/M7cfm/64/
edit: but make sure that you want to zoom in on that specific interval and essentially hiding data from the user (unless you have a scrollbar on the graph like this example)
You can overwrite default Chart.prototype.showResetZoom and provide your own logic in zoomOut function:
(function(H) {
H.Chart.prototype.showResetZoom = function() {
var chart = this,
defaultOptions = H.defaultOptions,
fireEvent = H.fireEvent,
lang = defaultOptions.lang,
btnOptions = chart.options.chart.resetZoomButton,
theme = btnOptions.theme,
states = theme.states,
alignTo = btnOptions.relativeTo === 'chart' ? null : 'plotBox';
function zoomOut() {
//chart.zoomOut(); // default action
chart.xAxis[0].setExtremes(3, 7); // new action
}
fireEvent(this, 'beforeShowResetZoom', null, function() {
chart.resetZoomButton = chart.renderer.button(
lang.resetZoom,
null,
null,
zoomOut,
theme,
states && states.hover
)
.attr({
align: btnOptions.position.align,
title: lang.resetZoomTitle
})
.addClass('highcharts-reset-zoom')
.add()
.align(btnOptions.position, false, alignTo);
});
}
})(Highcharts);
Live demo: http://jsfiddle.net/BlackLabel/ay0t2e9f/
Docs reference: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts
For Flot, is there an event that fires after the user has completed panning or zooming using the mouse scroll wheel (after the range.xaxis.to/from and range.yaxis.to/from have settled)? I am trying to use the line below to update the selection on an overview plot after the user has panned or zoomed in the main plot, but am finding that either the update to the overview plot happens after panning or zooming(not both).
$("#plot").bind("mouseup scroll",updateOverviewSelection);
Edit: http://jsfiddle.net/apandit/nu2rr58h/9/
In the jsfiddle, I am unable to pan in the main plot and the cursor does not seem to change back to normal. The user can click and drag in the overview plot to make a selection, which leads to zooming in the main plot. I would also like to be able to allow the user to pan and zoom in the main plot and have the selection box in the overview plot updated; I am attempting to do this by binding the updateOverviewSelection method to the plot div for the scroll and mouseup events. Is there an event in Flot that fires every time the x- and y-axis limits are updated?
The solution to this issue is below. The issue was that setting the overview plot's selection(overview.setSelection(ranges);) was triggering the zoom method because it was bound to the plotselected event in the overview plot. At the end of the zoom method, the main plot was plotted, which was again calling the overview.setSelection(ranges); line in the updateOverviewSelection method. To prevent this ping-pong between the two methods/events, I added an updatingOverviewSelection flag.
http://jsfiddle.net/apandit/nu2rr58h/12/
var datasets = [[
[0,0],[1,1],[2,2],[3,3],[4,4],[5,5],[6,6],[7,7],[8,8],[9,9]
],
[
[0,0],[-1,-1],[-2,-2],[-3,-3],[-4,-4],[-5,-5],[-6,-6],[-7,-7],[-8,-8],[-9,-9]
]];
var plot = $.plot("#plot",datasets,{
pan: {
interactive: true
},
zoom: {
interactive: true,
mode: "x"
}
});
var overview = $.plot("#overview",datasets,{
selection: {
mode: "xy"
}
});
var updatingOverviewSelection = false;
$("#plot").bind("plotpan plotzoom",updateOverviewSelection);
$("#overview").bind("plotselected", zoom);
function zoom(event,ranges) {
if(updatingOverviewSelection) {
updatingOverviewSelection = false;
}
else {
var options = plot.getOptions();
options.xaxes[0].min = ranges.xaxis.from;
options.xaxes[0].max = ranges.xaxis.to;
options.yaxes[0].min = ranges.yaxis.from;
options.yaxes[0].max = ranges.yaxis.to;
plot = $.plot("#plot",datasets,options);
}
};
// get the window x-axis and y-axis ranges for the main plot
// and set the selection in the overview plot to those ranges
function updateOverviewSelection(event) {
var options = plot.getOptions();
var ranges = {
xaxis: {
from: options.xaxes[0].min,
to: options.xaxes[0].max
},
yaxis: {
from: options.yaxes[0].min,
to: options.yaxes[0].max
}
};
updatingOverviewSelection = true;
overview.setSelection(ranges);
};
Though I have successfully colored the bars of google chart individually but not able to keep them when we hover mouse over it. It is getting reset back to blue(which is default).
Here is the jsfiddle of what I have done jsfiddle.
I tried to control the hover behaviour with multiple ways like below.
This I am keeping outside (document.ready) but inside script tag.
1)
$('#chart_div').hover(
function() {
$('#chart_client').hide(); // chart_client is another google chart div.
}, function() { // just for testing I was doing hide/show of that.
$('#chart_client').show();
}
);
2)
$("#chart_div").on({
mouseenter: function () {
$('#chart_client').hide();
},
mouseleave:function () {
$('#chart_client').show();
}
},'rect');
3)
google.visualization.events.addListener('#chart_div', 'ready', function () {
$('#chart_div rect').mouseover(function (e) {
alert('hello');
});
});
I must be doing something wrong, could you please tell me what and where.
I solved it using below code. Earlier I was trying to create charts using dynamically adding rows into chart(please visit my jsfiddle) but with this below approach I am first preparing data(converting dynamic to static) and adding that static data in to chart's 'arrayToDataTable' method.
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawUserKeywordChart);
function drawUserKeywordChart() {
var val = 'Tax:47;Finance:95;Awards:126;Bank:137;Debt:145;';
var length = val.length;
var array = [];
//preparing data
while(length>0){
var sepAt = val.indexOf(";");
var value = parseInt(val.substring(val.indexOf(":")+1, sepAt));
array.push(val.substring(0, val.indexOf(":")));
array.push(value);
val = val.substring(val.indexOf(";")+1, length);
length = val.length;
}
var data = google.visualization.arrayToDataTable([
['Keyword', 'Occurences', { role: 'style' }],
[array[0], array[1], '#8AA3B3'],
[array[2], array[3], '#A9B089'],
[array[4], array[5], '#848C49'],
[array[6], array[7], '#44464A'],
[array[8], array[9], '#704610'],
]);
var options = {
title: 'Keyword Matches',
width: 660,
height: 450,
titleTextStyle:{bold:true,fontSize:20},
legend:{position:'none'}
};
var chart = new google.visualization.ColumnChart(document.getElementById('chart_keyword1'));
chart.draw(data, options);
}
Please advice if you find anything wrong here or you have better approach than this.