在Shiny图表中添加超链接

7

我用不同的绘图方案制作了一个Shiny应用程序,用于在Shiny上从ggplot2绘制图形(我最喜欢的是plotly)。

我喜欢用户可以与图表交互的方式:使用plotly,用户可以缩放图表或单击点(例如散点图)并访问它们的值。

我想将散点图上的每个点链接到一个URL(而不显示它),并允许用户点击一个点,触发超链接的激活以打开一个新的网页。

如果我能用plotly做到这一点那就太棒了,但我也愿意尝试其他技术(如ggvis、dygraph等)。


我找到了这些代码行: library(ggvis) iris %>% ggvis(~Sepal.Width, ~Petal.Length, shape=~Species) %>% layer_points() %>% add_tooltip(html=function(df) paste0('<a href=', '"http://en.wikipedia.org/wiki/Iris_', df$Species, '">', df$Species, ' (wikipedia)</a>'), on="click") - jeandut
这似乎产生了我提到的结果,但有人对这个话题更了解吗?(可能是一个Plotly实现) - jeandut
1个回答

13
Shiny应用程序使用plotly的postMessage APIplotly.js,两者都公开了clickhoverzoom事件。这些事件尚未作为回调函数暴露给底层的Shiny服务器,但是您可以使用自定义JavaScript访问它们,并在Shiny中自己提供服务。
以下是一个使用单击事件的示例:

Adding click events to graphs in Shiny

ui.R

library(shiny)
library(plotly)

shinyUI(fluidPage(
  mainPanel(
    plotlyOutput("trendPlot"),
    tags$head(tags$script(src="clickhandler.js"))
  )
))

server.R

library(shiny)
library(plotly)

x = c(1, 2, 3)
y = c(4, 2, 4)
links = c("https://plot.ly/r/", 
          "https://plot.ly/r/shiny-tutorial", 
          "https://plot.ly/r/click-events")

df = data.frame(x, y, links)

shinyServer(function(input, output) {

  output$trendPlot <- renderPlotly({
    # Create a ggplot
    g = ggplot(data=df, aes(x = x, y = y)) + geom_point()
    # Serialize as Plotly's graph universal format
    p = plotly_build(g)
    # Add a new key, links, that JS will access on click events
    p$data[[1]]$links = links
    p

    # Alternatively, use Plotly's native syntax. More here: https://plot.ly/r
    # plot_ly(df, x=x,y=y,links=links)
  })
})

www/clickhandler.js

$(document).ready(function(){
// boiler plate postMessage plotly code (https://github.com/plotly/postMessage-API)
var plot = document.getElementById('trendPlot').contentWindow;

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

var clickResponse = function(e) {
     plot = document.getElementById('trendPlot').contentWindow;
    var message = e.data;
     console.log( 'New message from chart', message );
    if(message.pong) {
        // tell the embedded plot that you want to listen to click events
        clearInterval(pinger);
        plot.postMessage({
              task: 'listen', events: ['click']}, 'https://plot.ly');
          plot.postMessage({
            task: 'relayout',
            'update': {hovermode: 'closest'},
        },
        'https://plot.ly');
    }
    else if(message.type === 'click') {
        var curveNumber = message['points'][0]['curveNumber'],
            pointNumber = message['points'][0]['pointNumber'];

        var link;
        var traces = message.points[0].data;
        if(traces !== null && typeof traces === 'object') {
            link = traces.links[pointNumber];
        } else {
            link = traces[curveNumber].links[pointNumber];
        }

        console.log(link);

        var win = window.open(link, '_blank');
        win.focus();
    }
};

window.addEventListener("message", clickResponse, false);

});

以下是可能有帮助的其他资源:

当我尝试实现这个功能时,document.getElementById('trendPlot') 没有 contentWindow 属性 (Uncaught TypeError: Cannot read property 'postMessage' of undefined)。你有什么想法为什么会发生这种情况吗? - Hillary Sanders
@HillarySanders 给定的代码在一个<div> ... </div>元素内构建了图表。而这些元素没有contentWindow属性。看起来,只有当你的图表被包裹在一个iframe中时,这些点击事件才可用。但是到目前为止,我还没有看到任何让Shiny创建这样一个iframe的可能性... - K. Rohde
@ChrisP:非常好的答案!如果您有时间,可以看一下我的问题吗?https://dev59.com/yr_qa4cB1Zd3GeqPSvKD 谢谢! - stats_noob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接