/postMessage-API

Bind custom interactivity to embedded Plotly graphs

Primary LanguageHTML

postMessage API

JavaScript API to embedded Plotly graphs

Image of fractal widget

JSFiddle here

Image of data widget

Codepen here

postMessage API examples

Mandelbrot explorer
Binds to zoom event to recalculate Mandelbrot set after zooming-in (click-drag to zoom)
http://codepen.io/alexcjohnson/full/KwQWYo

IMDB movie explorer
Interactively explore the IMBD movie database
http://codepen.io/theengineear/pen/ogEzZO

Changing colors
Change the fill color of an area plot
http://codepen.io/anon/pen/ZYrbwK

Rose plot explorer
Dynamically change the parameters of a rose plot
http://codepen.io/etpinard/pen/vEdmgg

Plotly-Shiny integration
Integration with the popular Shiny framework for R
https://github.com/chriddyp/plotly-shiny

Overview

Plotly's JavaScipt API to graph embeds is now an officially supported feature for advanced users.

The postMessage API can be used to build custom controls that change anything about an embedded Plotly graph - the graph type, colors, data, etc - after the graph is embedded in a webpage. You can even start with a blank Plotly graph, then insert data and style it throught the postMessage API.

The postMessage API is the basis for communication between Plotly graphs and IPython notebook widgets, as well as with RStudio's Shiny product. We're excited to see what else will be built with it.

If you want to play with the API, the first step is to create a Plotly graph or use someone else's, then embed it in Codepen or a webpage as an iframe. There are plenty of graphs with which you can experiment on the Plotly feed.

Plotly embeds typically have this HTML format:

<iframe width="640" height="480" frameborder="0" seamless="seamless" scrolling="no" src="https://plot.ly/~Dreamshot/411.embed?width=640&height=480" ></iframe>

Questions? Tweet us at @plotlygraphs or write us at support[at]plot[dot]ly. Share with us what you've built. Codepen is a great resource for messing with the API.

Plotly's JSON representation

Plotly encodes all graphs into a text-based JSON format from which the graph is drawn and accessed in languages other than JavaScript (Python, MATLAB, R, etc). This is the "DNA" of the graph.

For example, the JSON representation of the the graph with URL https://plot.ly/~PlotBot/80 is https://plot.ly/~PlotBot/80.json.

You will need to reference the JSON representation in order to pinpoint the aspects of the graph you want to modify through the postMessage API.

If you're curious about the JSON representations of more complicated Plotly graphs, try appending ".json" to the graph URL's on the Plotly feed.

Getting Started

Hello World contains boiler plate code for initalizing postMessage listeners and posting messages. Interact with the example.

Tasks

The postMessage API communicates with embedded graphs via a task identifier. Supported tasks are:

  • task: restyle - change attributes of the graph's data
  • task: relayout - change attributes of the graph size, margins, and axes
  • task: hover - force a hover tag to display over a specific x,y point on the graph
  • task: listen - specify a callback for a single event or group of events (zoom, hover, or click)
  • task: addTraces - shortcut for adding new traces to the graph
  • task: deleteTraces - shortcut for deleting traces from the graph
  • task: moveTraces - change the order of traces in a graph
  • task: getLayout - return the layout object as JSON for the graph
  • task: getAttributes - return a part of the graph's JSON representation by key
  • task: setAutosize - does the graph redraw when the window is resized?
  • task: ping - returns "pong" if the graph is loaded, just used to tell when the listener is active
  • task: redraw - forces a redraw of the graph
  • task: newPlot - idempotent plotting function, replaces existing Plotly plot with new one

# task: restyle

The restyle task changes attributes of the graph data - colors, type (ie bars versus points, scatter plot versus histogram), and the even values of the data. See Plotly's JSON representation above for how to pinpoint these attributes. The keys that can be changed through the restyle task are in the "data" array.

If you have several traces (several lines for a line chart, a stacked bar chart, etc), you can specify a trace index to target the data trace you want to restyle.

See Trace objects and Trace auxiliary objects for an exhaustive list of accepted key-value pairs that can be passed to the restyle task.

This example changes the y-values data of the second trace to 2 and the color of the points to red.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
        {
            'task': 'restyle',
            'update': {'marker.color': 'red', 'y': [[2, 3, 1]]},
            'indices': [1]
        },
        'https://plot.ly');

Here's the syntax to update the data in a graph with 2 traces, where X, X1, Y, and Y1 are numeric arrays.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
        {
            task:'restyle',
            update:{x:[X,X1],y:[Y,Y1]}
        }, 'https://plot.ly');   

<a href="#relayout"name="relayout"># task: relayout

The relayout task changes the size, margins, and axes of a graph. See Plotly's JSON representation above for how to pinpoint these attributes. The keys that can be changed through the relayout task are in the "layout" object.

See Layout objects and Axes objects for an exhaustive list of accepted key-value pairs that can be passed to the restyle task.

This example changes the width of the graph to 500px and makes sure the legend is displayed.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
        {
            'task': 'relayout',
            'update': {'width': '500', 'showlegend': True},
        },
        'https://plot.ly');

# task: hover

Force a hover tag to display over a specific x,y point on the graph.

This example forces a hover tag to display at point (1,2) on the graph.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
        {
            'task': 'hover',
            'selection': {xval: 1, yval: 2},
            // 'subplot': 'xy' optional: for graphs with subplots, specify which subplot
        },
        'https://plot.ly');

# task: listen

Add a listener to a single event or group of events (click, hover, or zoom).

This example listens for a click, hover, or zoom then prints the event object in the browser console.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
    {
        task: 'listen',
        events: ['zoom','click','hover']
    }, 'https://plot.ly');

window.addEventListener('message', function(e) {
    var message = e.data;
    alert(message.type);
    console.log(message); // prints object for zoom, click, or hover event
});

# task: addTraces

Overwrites or adds data traces to a graph.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
    {
        task: 'addTraces',
        traces: [{y: [2,1,2]}, {y: [2,4,5]}],
        newIndices: [0, -1]
    }, 'https://plot.ly');

# task: deleteTraces

Removes traces from a graph by the trace index.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow to delete traces at indices 0 and 1
plot.postMessage(
    {
        task: 'deleteTraces',
        indices: [0, 1]
    }, 'https://plot.ly');

// send a message to the contentWindow to delete the last trace
plot.postMessage(
    {
        task: 'deleteTraces',
        indices: [-1]
    }, 'https://plot.ly');

# task: moveTraces

Change the order of traces in a graph.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
    {
        task: 'moveTraces',
        currentIndices: [0, 1],
        newIndices: [1, 0]
    }, 'https://plot.ly');

# task: getLayout

Get the full layout object of the graph (see Plotly's JSON representation above).

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
    {
        task: 'getLayout',
    }, 'https://plot.ly');

window.addEventListener('message', function(e) {
    var message = e.data;
    console.log(message);
});

# task: getAttributes

Return a specific attribute from the top-level layout or data objects (see Plotly's JSON representation above).

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
    {
        task: 'getAttributes',
        attributes: ['layout.title'] }, 'https://plot.ly' );

window.addEventListener('message', function(e) {
    var message = e.data;
    console.log(message);
});

Here's the syntax to get x,y data arrays in a graph with 2 traces.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

var X, Y, X1, Y1;

// get current x, y data
plot.postMessage({
    task: 'getAttributes',
    attributes: [
        'data[0].x','data[0].y',
        'data[1].x','data[1].y'
    ] },
    'https://plot.ly/');

window.addEventListener('message', function(e) {
    var message = e.data;
    X = message.response['data[0].x'];
    Y = message.response['data[0].y'];
    X1 = message.response['data[1].x'];
    Y1 = message.response['data[1].y']; 
});

# task: setAutosize

If set to true, iframe will resize if placed in a container smaller than the original iframe dimensions.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage(
        {task: 'setAutosize', 'value': true}, 'https://plot.ly');

# task: ping

The ping task is just used to tell when the listener is active.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

var pinger = setInterval(function(){
    plot.postMessage({task: 'ping'}, 'https://plot.ly')
}, 100);

window.addEventListener('message', function(e) {
    var message = e.data;
    if(message.pong) {
        console.log('Initial pong, frame is ready to receive');
        clearInterval(pinger);
        // Do some stuff, iframe is now ready to receive tasks
    }
});

# task: redraw

Force a redraw of the graph contents.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow
plot.postMessage({task: 'redraw'}, 'https://plot.ly');

# task: newPlot

Idempotent plot command to created new plotly Plot with data and layout attributes.

// Grab the embed's contentWindow by the iframe id
var plot = document.getElementById('plot').contentWindow;

// send a message to the contentWindow to replace the existing plot
plot.postMessage({
    task: 'newPlot',
    data: [{y: [2, 5, 1, 2], type: 'bar'}, {y: [2, 5, 1, 2], type: 'scatter'}],
    layout: {title: 'new plot'}
}, 'https://plot.ly');