
本教程详细介绍了如何在r shiny应用中,通过点击按钮在新标签页或新窗口中打开指定url。针对常见的`window.location`和`window.open`在自定义消息处理器中可能遇到的限制,本文提供了一个健壮的解决方案,利用javascript动态创建并模拟点击一个带有`target=”_blank”`属性的html ``元素,从而确保链接能够可靠地在新标签页中打开。
在开发Shiny应用时,用户经常会遇到需要点击某个按钮,然后在新标签页或新窗口中打开一个外部链接的需求。直接使用window.location会在当前窗口重定向,而window.open在某些上下文中可能无法按预期工作或被浏览器阻止。本文将提供一个稳定且兼容性良好的方法来实现这一功能。
问题背景与常见尝试
当用户在Shiny应用中点击一个按钮时,我们通常会通过R的session$sendCustomMessage函数向前端JavaScript发送指令。一个常见的初步尝试是使用JavaScript的window.location来重定向:
// 初始尝试:在JavaScript中直接使用 window.location Shiny.addCustomMessageHandler('mymessage', function(message) { window.location = message; });
然而,这种方法会将当前Shiny应用重定向到新的URL,而不是在新标签页中打开。为了在新标签页中打开,开发者可能会尝试window.open:
// 另一个尝试:在JavaScript中使用 window.open Shiny.addCustomMessageHandler('mymessage', function(message) { window.open(message, '_blank'); // 理论上应该在新标签页打开 });
虽然window.open理论上可以实现新标签页打开,但在某些浏览器环境或被调用方式下,它可能被浏览器的弹窗拦截器阻止,导致无法正常工作。
解决方案:利用动态创建的html <a> 元素
一个更可靠且兼容性更强的解决方案是,在JavaScript中动态创建一个隐藏的HTML <a>(锚点)元素,设置其href属性为目标URL,并设置target=”_blank”属性以指示在新标签页中打开,然后通过JavaScript模拟点击这个元素。
以下是完整的Shiny应用代码示例:
library(shiny) # 定义自定义JavaScript消息处理器 myJS <- "Shiny.addCustomMessageHandler('mymessage', function(message) { // 1. 创建一个新的a(锚点)HTML元素 var a = document.createElement('a'); // 2. 将其设置为不可见,因为它只是用于触发点击事件 a.style.display = 'none'; // 3. 设置链接的目标URL,即从R传递过来的message a.href = message; // 4. 关键步骤:设置target属性为'_blank',指示在新标签页/窗口打开 a.target = '_blank'; // 5. 将a元素临时添加到文档的body中。 // 这在某些浏览器中是必要的,以确保a.click()能够成功触发。 document.body.appendChild(a); // 6. 模拟点击这个a元素 a.click(); // 7. 点击完成后,移除这个临时的a元素,保持dom整洁 a.remove(); });" ui <- fluidPage( # 在UI的head部分引入自定义JavaScript tags$head(tags$script(myjs)), # 创建一个动作按钮 actionButton("button", "点击我打开新链接") ) server <- function(input, output, Session) { # 监听按钮的点击事件 observeEvent(input$button,{ # 定义要打开的URL url <- "https://www.r-project.org/" # 通过自定义消息处理器将URL发送到前端JavaScript session$sendCustomMessage("mymessage", url) }) } # 运行Shiny应用 shinyApp(ui, server)
代码详解
-
R部分 (server函数):
- observeEvent(input$button, {…}): 这是一个标准的Shiny观察者,用于监听名为button的actionButton的点击事件。
- url <- “https://www.r-project.org/”: 定义了当按钮被点击时要打开的目标URL。你可以根据需要替换为任何有效的URL。
- session$sendCustomMessage(“mymessage”, url): 这是R与JavaScript通信的关键。它会向客户端发送一个名为mymessage的自定义消息,并将url作为消息内容传递给JavaScript。
-
JavaScript部分 (myjs变量中的代码):
- Shiny.addCustomMessageHandler(‘mymessage’, function(message) { … });: 这段JavaScript代码注册了一个自定义消息处理器。当R端通过session$sendCustomMessage(“mymessage”, …)发送消息时,这个函数就会被执行,并且R传递的url会作为message参数传入。
- var a = document.createElement(‘a’);: 在文档对象模型(DOM)中动态创建一个新的<a>元素。
- a.style.display = ‘none’;: 将新创建的<a>元素设置为不可见。由于我们只是利用它来触发一个事件,而不需要它实际显示在页面上,所以隐藏它是良好的实践。
- a.href = message;: 将从R端接收到的URL(即message)赋值给<a>元素的href属性,指定了链接的目标。
- a.target = ‘_blank’;: 这是实现新标签页打开的关键。_blank值告诉浏览器在新窗口或新标签页中打开链接。
- document.body.appendChild(a);: 将这个隐藏的<a>元素临时添加到<body>元素的末尾。虽然在某些浏览器中不添加也能工作,但为了确保a.click()方法在所有浏览器中都能可靠地触发链接,将其添加到DOM中是一个更健壮的做法。
- a.click();: 通过JavaScript模拟对这个<a>元素的点击操作。这会触发浏览器打开a.href指定的新链接。
- a.remove();: 在链接被触发后,立即从DOM中移除这个临时的<a>元素,保持页面的整洁和性能。
注意事项
- 浏览器弹窗拦截器: 尽管这种方法比直接使用window.open更不容易被拦截,但如果用户的浏览器设置了非常严格的弹窗拦截策略,仍然有可能阻止新标签页的打开。在这种情况下,用户可能需要在浏览器中允许您的Shiny应用的弹窗。
- 用户体验: 确保用户清楚点击按钮会打开新标签页。可以在按钮文本中加入提示,例如“点击打开新链接 (新标签页)”。
- 安全性: 当从R端传递URL时,请确保URL是受信任的来源。如果URL是用户输入或来自不可信的数据源,应进行适当的验证和清理,以防止潜在的跨站脚本攻击(xss)。
总结
通过在Shiny应用中结合R的session$sendCustomMessage和JavaScript动态创建并模拟点击带有target=”_blank”属性的<a>元素,我们可以实现一个稳定且用户体验良好的在新标签页中打开外部链接的功能。这种方法避免了window.location的当前页重定向问题,并增强了window.open在某些场景下的兼容性和可靠性,是Shiny应用中处理外部链接跳转的推荐实践。


