简体   繁体   中英

html file with jQuery does not work well if If I write the dashboardPage in Shiny as renderUI

I have a shiny app in which I embedded an iframe inside a shiny dashboard. I want the iframe to be able to expand in height and for that I have implemented a html file with a jquery script.

The test.html file looks like this and works well:

<!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>

The problem comes when I insert this html file inside the dashboard ui.

If I do it like this it works:

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)

But when I try to implement it as output$body with renderUI, the height expansion of the iframe does not work.

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)

They are basically the same code. Does anyone know why this happens?

First, you need to understand how HTML is rendered and how renderUI is working.

  1. The head tag in HTML is first loaded before the body part is loaded. In your first method, the head tag in your test.html is added/joined other head elements of your main app to be one big head tag when the app starts. So when you do the first method, Shiny actually split your HTML into to parts, head to head, body to body. When head is loaded (and done loading) before the body, resizable can be found because the head scripts are done with loading.
  2. When you use renderUI , all scripts in head and body are evaluated under the same level. There is no such thing to wait head is done running and do other things when you use renderUI . So when you call the $("#div1").resizable , it is not defined yet. It is still running. To me, this is more like a limitation of renderUI function. Shiny should have something to wait for the head tag in renderUI .

Here is a workaround: we can manually add a wait function of WaitUntil , and it waits until some conditions are fulfilled, and then we do something.

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)

A WaitUntil JS function (credit to wish king )is added to the main app. Note not inside your renderUI , because you may want to use it many times, so you just need to define it once.

for test.html , wrap the resizable inside 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM