Skip to content

hover events for labels/legends #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tlrdstd opened this issue Dec 3, 2015 · 15 comments
Closed

hover events for labels/legends #65

tlrdstd opened this issue Dec 3, 2015 · 15 comments
Labels
feature something new

Comments

@tlrdstd
Copy link
Contributor

tlrdstd commented Dec 3, 2015

I would like to be able to know when the user hovers or clicks on my label or legend items. Specifically, I would like to be able to highlight a given pie slice or bar trace when a particular label or legend item is hovered over. d3pie does this, and an example can be seen here: http://d3pie.org/#generator-start

As a starting point, it would be useful to have this click handler fire an event after it calls Plotly.restyle or Plotly.relayout:

traceToggle.on('click', function() {

A hover handler for the same traces would help a lot as well.

@etpinard
Copy link
Contributor

etpinard commented Dec 3, 2015

I really like this idea.

Custom legend item click handlers could be set in the plot config e.g.:

Plotly.plot('graph', [], {}, {
   onLegendItemClick: function(data) {
      /* do something wilh data */
   }
}

The current handler would simply be the default.

@alexcjohnson
Copy link
Collaborator

The current handler would simply be the default.

or, as in some other cases like plotly_beforeplot, it will still execute the default handler unless you return false from the custom handler

@Abildtoft
Copy link

Thanks for an awesome library. Can I ask what the status is with regards to this?

@abalter
Copy link

abalter commented Oct 9, 2016

Here is a hacky way to do it. I did "inspect element" on a legend item and found that it has a class legendtoggle. Using this,

$('.legendtoggle').on('click', function(data)
{
    alert("legend clicked");
})

It doesn't say which trace item was clicked. However, if you are truly dedicated, you can climb back up to the legend group tag, then back down to the text element and grab the data-unformatted value.

@joeymink
Copy link

joeymink commented Jan 4, 2017

We would like to give a user the ability to select series colors after viewing a plot. Ideally they would interact with the legend to do so. Another vote to resolve this issue :)

@mauritzvdworm
Copy link

Another vote here.

Another use for this would be if you have a plot with a collection of line graphs corresponding to different groups. The user could then hover over the legend to highlight the particular plot similar to the d3pie example above.

@aliavni
Copy link

aliavni commented Mar 13, 2017

+1

2 similar comments
@cutalion
Copy link

+1

@acailly
Copy link

acailly commented May 16, 2017

+1

@sardbaba
Copy link

sardbaba commented Dec 11, 2017

It seems that there isn't a native event handler for the axis labels but we can workaround this limitation using vanilla Javascript and D3js. Here is what I did under Angular for a bar chart (probably the selector needs to be adapted for other chart types):

import * as d3 from 'd3';

export class AngularCustomComponent {

    // contructor and other methods here..

    plotlyClickEvents() {
        /**
         * Distinguish between single and double click
         * @see http://bl.ocks.org/ropeladder/83915942ac42f17c087a82001418f2ee
         */
        function clickcancel() {
            let dispatcher = d3.dispatch('click', 'dblclick');
            function cc(selection) {
                let down, tolerance = 5, last, wait = null, args;

                // euclidean distance
                function dist(a, b) {
                    return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
                }

                selection.on('mousedown', function() {
                    down = d3.mouse(document.body);
                    last = +new Date();
                    args = arguments;
                });

                selection.on('mouseup', function() {
                    if (dist(down, d3.mouse(document.body)) > tolerance) {
                        return;
                    }
                    else {
                        if (wait) {
                            window.clearTimeout(wait);
                            wait = null;
                            dispatcher.apply("dblclick", this, args);
                        }
                        else {
                            wait = window.setTimeout((function() {
                                return function() {
                                    dispatcher.apply("click", this, args);
                                    wait = null;
                                };
                            })(), 300);
                        }
                    }
                });
            };

            // Copies a variable number of methods from source to target.
            let d3rebind = function(target, source, method) {
                let i = 1, n = arguments.length;
                while (++i < n) target[method] = d3_rebind(target, source, source[method]);
                return target;
            };

            // Method is assumed to be a standard D3 getter-setter:
            // If passed with no arguments, gets the value.
            // If passed with arguments, sets the value and returns the target.
            function d3_rebind(target, source, method) {
                return function() {
                    let value = method.apply(source, arguments);
                    return value === source ? target : value;
                };
            }

            return d3rebind(cc, dispatcher, 'on');
        }

        return clickcancel();
    }

    onBarChartXAxisClick() {
        let self = this;
        let item = null;

        d3.select("#your-parent-element-identifier").
            select('.cartesianlayer')
                .select('.xaxislayer-above')
                    .selectAll('.xtick > *').each(function(e) {
            // @see https://hstefanski.wordpress.com/2015/10/25/responding-to-d3-events-in-typescript/
            // but `d3.select(d3.event.currentTarget)` does not work in this case.
            // To workaround see https://stackoverflow.com/a/40314241

            item = d3.select(this);

            // @NOTE: the element of the x-axis is a <text> and does not have the
            // attribute "pointer-events". Without this attribute is not possibile
            // to listen for mouse events, and for that we have to add it.
            item.attr('pointer-events', 'all');

            const cc = self.plotlyClickEvents();
            item.call(cc);

            cc.on('click', (d, index) => {
                // Do something
            });
        });
    }

}

Hope this helps

@bruggsy
Copy link

bruggsy commented Dec 12, 2017

For anybody looking to add hover events to the the Plotly legend, here's are some simple events I added to the traceToggle item that highlights the hovered legend item using opacity:

`

traceToggle.on('mouseenter', function() {
var legendItem = g.data()[0][0],
    fullData = gd._fullData,
    trace = legendItem.trace,
    legendgroup = trace.legendgroup,
    tracei,
    allTraces = [],
    i,
    traceOpacity = [];

if(trace.visible === true){	    
        for(i = 0; i < fullData.length; i++) {
	allTraces.push(i);
	traceOpacity.push(
	    Registry.traceIs(fullData[i], 'notLegendIsolatable') ? 1.0: 0.1
	);
        }

    if(legendgroup === ''){
	traceOpacity[trace.index] = 1.0;
    } else{
	for(i = 0; i < fullData.length; i++) {
	    tracei = fullData[i];
	    if(tracei.legendgroup === legendgroup) {
		traceOpacity[allTraces.indexOf(i)] = 1.0;
	    }
	}
    }

    var update = {
	opacity: traceOpacity
    };
    Plotly.restyle(gd,update,allTraces);
}
});
traceToggle.on('mouseleave',function() {

var legendItem = g.data()[0][0],
    fullData = gd._fullData,
    trace = legendItem.trace,
    allTraces = [],
    i;
if(trace.visible === true){	    

    var update = {
	opacity: 1.0
    };
    for(i = 0; i < fullData.length; i++){
	allTraces.push(i);
    }

    Plotly.restyle(gd,update,allTraces);
}

});

`

@etpinard
Copy link
Contributor

etpinard commented May 1, 2018

The click part of this feature request has been merged in #2581

@markzolotoy
Copy link

Lots of information here. What is a status on making legend items clickable?

@chriddyp chriddyp changed the title click/hover events for labels/legends hover events for labels/legends Apr 11, 2019
@jackparmer
Copy link
Contributor

This issue has been tagged with NEEDS SPON$OR

A community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.

Sponsorship range: $25k-$30k

What Sponsorship includes:

  • Completion of this feature to the Sponsor's satisfaction, in a manner coherent with the rest of the Plotly.js library and API
  • Tests for this feature
  • Long-term support (continued support of this feature in the latest version of Plotly.js)
  • Documentation at plotly.com/javascript
  • Possibility of integrating this feature with Plotly Graphing Libraries (Python, R, F#, Julia, MATLAB, etc)
  • Possibility of integrating this feature with Dash
  • Feature announcement on community.plotly.com with shout out to Sponsor (or can remain anonymous)
  • Gratification of advancing the world's most downloaded, interactive scientific graphing libraries (>50M downloads across supported languages)

Please include the link to this issue when contacting us to discuss.

@gvwilson
Copy link
Contributor

gvwilson commented Jun 5, 2024

Hi - this issue has been sitting for a while, so as part of our effort to tidy up our public repositories I'm going to close it. If it's still a concern, we'd be grateful if you could open a new issue (with a short reproducible example if appropriate) so that we can add it to our stack. Cheers - @gvwilson

@gvwilson gvwilson closed this as completed Jun 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature something new
Projects
None yet
Development

No branches or pull requests