18 Introduction

The same plotly.js events that we leveraged in shiny through event_data() in Section 17.2 can also be handled using JavaScript (JS) instead of R, which offers numerous advantages:

  1. Your web browser natively understands JS, so writing event handlers in JS instead of R offers the potential of having a purely client-side webpage instead of client-server app, making the end result easier to share, host, and maintain.35 That being said, the effort required to rewrite a ‘heavy-duty’ client-server app as a ‘light-weight’ client-side webpage isn’t always worth the investment; so before doing so, you should have a clear vision for the interactivity you desire, be fairly confident that vision won’t change in the future, and have a use-case that doesn’t require sophisicated statistical computations to be run dynamically in response to user events (i.e., it’s not practical or feasible to pre-compute).36
  2. There are certain things you can do in JS that you can’t necessarily do in R, such as accessing the web browser’s window API to open hyperlinks in response to plotly.js click events (e.g., Figure 21.1).
  3. JS event handlers can be noticably faster than running comparable code on an external R process, especially with a slow internet connection.

For those new to JS, you may find it helpful to compare code examples from this part of the book to code examples from Section 17.3.1.37 That’s because, the plotlyProxy() interface which powers that section is just an R interface to plotly.js’ JavaScript functions, and is primarily for updating graphs within an event handler. Therefore, if you understand how the examples in that section work, you can translate a good amount of that knowledge to a JS context as well. However, when handling these events in JS instead of R, you’ll want to be familiar with JavaScript Object Notation (JSON), which is introduced in Chapter 19. That chapter also offers a minimal JS programming foundation for manipulating JSON, then Chapter 20 quickly covers how to attach JS event handlers to various plotly.js events, which is really all that’s required to loosely understand the bulk of the examples in Chapters 21 and 22.

An important thing to know about when doing any sort of web development is how to open and navigate web browsers developer tools. Through the developer tools, you can access a JS console to run and test out JS code, inpsect and debug the JS/CSS/HTML code behind a website, query components of the Document Object Model (DOM), inspect network traffic, and much more. In our use case of writing plotly.js event handlers, the JS console will come in handy especially to see what information a plotly.js event is firing (think of it as the analog of printing output to the R console in a shiny app), before writing the actual event handler. To open the console of a web browser (including RStudio), you can likely do: right-click -> “Inspect Element” -> “Console” tab (or similar).

One way to write a custom event handler for a plotly graph is to leverage the onRender() function from the htmlwidgets package. This R function accepts a JS function as a string and calls that function when the widget is done rendering in the browser. The JS function needs (at least) one argument, el, which is the Document Object Model (DOM) element containing the plotly graph. It’s worth noting that htmlwidgets::onRender() serves a more general purpose than registering plotly.js event handlers, so you could use it to a bunch other things, such as adding conditional logic based on information stored in el. Figure 18.1 shows how you could use onRender() to log (and inspect) the DOM element to your browser’s JS console. To demonstrate some useful DOM element’s properties, Figure 18.1 uses Firefox to inspect the element as a global variable, but as Figure 20.1 shows, Chrome currently offers better tools for code debugging the JS function provided to onRender() (e.g., setting breakpoints in virtual memory).

library(htmlwidgets)
plot_ly(z = ~volcano) %>%
  onRender("function(el) { console.log(el); }")

FIGURE 18.1: Using htmlwidgets::onRender() to inspect the relevant DOM instance containing the plotly graph and information related to it’s current display. The _fullData and _fullLayout attributes bound to the element are ‘internal’ (meaning relying on this information in production code is discouraged), but do provide a useful description of the chart’s current state, especially if you need access to computations done by plotly.js (e.g., axis tick placement). For the interactive, see https://plotly-r.com/interactives/console-log.html

If you’re completely new to JS and JSON, Section 19 provides a foundation for understanding the subsequent sections, but those already familiar can skip ahead to Section 20, which shows how to log plotly.js event data to the JS console via htmlwidgets::onRender().


  1. Comparatively speaking, client-server apps require way more runtime software dependencies. In the case of shiny apps, RStudio provides accessible resources for hosting shiny apps https://shiny.rstudio.com/articles/#deployment, but using these services to host apps that encounters lots of traffic will either cost money and/or time for setting up the proper computational infrastructure.

  2. Compared to JS, R has way more facilities for statistical computing.

  3. In fact, converting some examples from that section from shiny to JavaScript/HTML would be a good exercise!