我有一个 shiny 应用程序,其中我在 shiny 仪表板中嵌入了一个 iframe。我希望 iframe 能够扩展高度,为此我实现了一个带有 jquery 脚本的 html 文件。
test.html 文件看起来像这样并且运行良好:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script type="text/javascript" src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css">
<style>
#div1 {
position: relative;
width: 100%;
height: 200px;
}
</style>
</head>
<body>
<div id="div1"> <iframe id="ifr" src="https://pubmed.juliasanchezmartinez98.workers.dev/" onLoad="doOnLoad()" style="width:100%; height:100%;"></iframe>
<script>
$("#div1").resizable({
start: function (event, ui) {
ui.element.append($("<div/>", {
id: "iframe-barrier",
css: {
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
"z-index": 10
}
}));
},
stop: function (event, ui) {
$("#iframe-barrier", ui.element).remove();
},
resize: function (event, ui) {
$("iframe", ui.element).width(ui.size.width).height(ui.size.height);
}
});
$('#div1').resize(function () {
});
</script>
</body>
</html>
当我在仪表板 ui 中插入此 html 文件时,问题就出现了。
如果我这样做,它会起作用:
library(shiny)
library(shinyjs)
library(shinydashboard)
num_engine ="test.html"
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
fluidRow(
h1("Evaluating a Search Engine"),
box(width = 12,
useShinyjs(),
includeHTML(num_engine)),
box(width=12,textAreaInput("txt", "Enter the answer below:",height = "100px", width = "100%"),
actionButton("submit_answer", "Submit", class = "btn-primary")),
),
))
server <- function(input, output) {
}
runApp(list(ui = ui, server = server), launch.browser = TRUE)
但是当我尝试使用 renderUI 将其实现为 output$body 时,iframe 的高度扩展不起作用。
library(shiny)
library(shinyjs)
library(shinydashboard)
header <- dashboardHeader( title = "Welcome", uiOutput("logoutbtn"))
sidebar <- dashboardSidebar(uiOutput("sidebarpanel"))
body <- dashboardBody(shinyjs::useShinyjs(), uiOutput("body"))
ui <- dashboardPage(header, sidebar, body, skin = "blue")
server <- function(input, output) {
num_engine ="test.html"
output$body <- renderUI({
fluidRow(
h1("Evaluating a Search Engine"),
box(width = 12,
useShinyjs(),
includeHTML(num_engine)),
box(width=12,textAreaInput("txt", "Enter the answer below:",height = "100px", width = "100%"),
actionButton("submit_answer", "Submit", class = "btn-primary")),
)
})
}
runApp(list(ui = ui, server = server), launch.browser = TRUE)
它们基本上是相同的代码。有谁知道为什么会这样?
回答1
首先,您需要了解 HTML 是如何呈现的以及 renderUI
是如何工作的。
- HTML 中的
head
标记在加载正文部分之前首先加载。在您的第一种方法中,当应用程序启动时,您的test.html
中的head
标记被添加/加入主应用程序的其他头部元素,成为一个大的head
标记。因此,当您执行第一种方法时, Shiny 实际上将您的 HTML 拆分为多个部分,头对头,身体对身体。当 head 在 body 之前加载(并完成加载)时,可以找到resizable
,因为 head 脚本已完成加载。 - 使用
renderUI
时,head
和body
中的所有脚本都在同一级别下进行评估。当您使用renderUI
时,无需等待head
完成运行并执行其他操作。因此,当您调用$("#div1").resizable
时,它尚未定义。它仍在运行。对我来说,这更像是renderUI
功能的限制。 Shiny 应该有一些东西等待renderUI
中的head
标记。
这里有一个解决方法:我们可以手动添加一个WaitUntil
的等待函数,它会一直等待,直到某些条件满足,然后我们做一些事情。
library(shiny)
library(shinyjs)
library(shinydashboard)
header <- dashboardHeader( title = "Welcome", uiOutput("logoutbtn"))
sidebar <- dashboardSidebar(uiOutput("sidebarpanel"))
body <- dashboardBody(
tags$script(HTML(
"
var WaitUntil = (function(){
var waitUntil=function(condfunc, callback, interval, trys){
var getGuid = getGuid||function() {
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
};
var timer = {}, counter={};
var waiter = function(condfunc, callback, interval, trys, guid){
guid = guid || getGuid();
interval = interval || 100;
trys = trys || 300;
counter[guid] = counter[guid] ? counter[guid]++ : 1;
if(counter[guid]>trys){
if(timer[guid]) clearTimeout(timer[guid]);
//callback('fail');
return;
}
timer[guid] = setTimeout(function(){
if(condfunc()){
if(timer[guid]) clearTimeout(timer[guid]);
callback('ok');
} else {
if(timer[guid]) clearTimeout(timer[guid]);
waiter(condfunc, callback, interval, trys, guid);
}
}, interval);
}
waiter(condfunc, callback, interval, trys);
}
return waitUntil;
})();
"
)),
shinyjs::useShinyjs(), uiOutput("body")
)
ui <- dashboardPage(header, sidebar, body, skin = "blue")
server <- function(input, output) {
num_engine ="test.html"
output$body <- renderUI({
fluidRow(
h1("Evaluating a Search Engine"),
box(width = 12,
useShinyjs(),
includeHTML(num_engine)),
box(width=12,textAreaInput("txt", "Enter the answer below:",height = "100px", width = "100%"),
actionButton("submit_answer", "Submit", class = "btn-primary")),
)
})
}
runApp(list(ui = ui, server = server), launch.browser = TRUE)
WaitUntil
JS 函数(归功于 https://greasyfork.org/en/scripts/412875-waituntil/code)被添加到主应用程序中。注意不要在你的 renderUI
里面,因为你可能想多次使用它,所以你只需要定义一次。
对于 test.html
,将 resizable
包裹在 WaitUntil
内
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script type="text/javascript" src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css">
<style>
#div1 {
position: relative;
width: 100%;
height: 200px;
}
</style>
</head>
<body>
<div id="div1"> <iframe id="ifr" src="https://pubmed.juliasanchezmartinez98.workers.dev/" onLoad="doOnLoad()" style="width:100%; height:100%;"></iframe>
<script>
WaitUntil(function(){return typeof $("#div1").resizable !=="undefined"}, function(){
$("#div1").resizable({
start: function (event, ui) {
ui.element.append($("<div/>", {
id: "iframe-barrier",
css: {
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
"z-index": 10
}
}));
},
stop: function (event, ui) {
$("#iframe-barrier", ui.element).remove();
},
resize: function (event, ui) {
$("iframe", ui.element).width(ui.size.width).height(ui.size.height);
}
});
$('#div1').resize(function () {
});
});
</script>
</body>
</html>