在闪亮的textOutput()更改后触发反应

时间:2019-10-10 13:56:07

标签: javascript r shiny reactive

我正在尝试使用JohnCoene/marker包来突出显示闪亮应用程序中的文本部分。我的意图是首先使用某些服务器逻辑生成文本并使用textOutput显示它。但是,我正在努力尝试在文本出现在网站上之后如何触发 marker 。将其放在相同的observeEvent()中是行不通的。

这是我的代表

# remotes::install_github("johncoene/marker")
library(shiny)
library(marker)

ui <- fluidPage(
  use_marker(),
  actionButton("click", "click"),
  textOutput("text_to_mark")
)
server <- function(input, output) {
   observeEvent(input$click, 
                {
                  output$text <- renderText("My house is yellow")
                })
  # observeEvent() below does not work. This is just for illustration
  observeEvent(input$text_to_mark,
               {
                 marker <- marker$new("#text_to_mark.shiny-text-output.shiny-bound-output")
                 marker$
                   unmark()$ # unmark all before we mark
                   mark("My house")
               })
}

# Run the application 
shinyApp(ui = ui, server = server)

reprex package(v0.3.0)于2019-10-10创建

为便于说明:我可以通过添加下面的代码中的第二个按钮来使标记起作用,但是我正在寻找一种解决方案,当文本出现时触发它。

# remotes::install_github("johncoene/marker")
library(shiny)
library(marker)

ui <- fluidPage(
  use_marker(),
  actionButton("click", "click"),
  textOutput("text_to_mark"),
  actionButton("mark", "Mark!")
)

server <- function(input, output) {
  observeEvent(input$click, 
               {
                 output$text_to_mark <- renderText("My house is yellow")
               })
  observeEvent(input$mark,
               {
                 marker <- marker$new("#text_to_mark.shiny-text-output.shiny-bound-output")
                 marker$
                   unmark()$ # unmark all before we mark
                   mark("My house")
               })
}

# Run the application 
shinyApp(ui = ui, server = server)

reprex package(v0.3.0)于2019-10-10创建

2 个答案:

答案 0 :(得分:1)

您可以使用JavaScript Is there a JavaScript / jQuery DOM change listener?监听DOM的更改。

发生更改时,您可以检查目标元素是否包含文本:

  hasText = document.getElementById("text_to_mark").innerHTML != ""

请注意,我假设您的元素的ID为“ text_to_mark”。

您可以使用

“发送到R”的结果
  Shiny.onInputChange("hasText", hasText);

在R端,您可以通过侦听input$hasText来知道元素是否具有文本。

因此您可以添加:

observeEvent(input$hasText,{
   ...
})

您可以使用tags$script(jsCode)或使用shinyjs将JavaScript添加到您的应用中。

可复制的示例:

library(shiny)
library(marker)

jsCode <- '
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations, observer) {
  console.log(mutations, observer);
  hasText = document.getElementById("text_to_mark").innerHTML != ""
  Shiny.onInputChange("hasText", hasText);
});

observer.observe(document, {
  subtree: true,
  attributes: true
});
'

ui <- fluidPage(
  use_marker(),
  tags$script(jsCode),
  actionButton("click", "click"),
  textOutput("text_to_mark"),
  actionButton("mark", "Mark!")
)

server <- function(input, output) {

  observeEvent(input$click, {
                 output$text_to_mark <- renderText("My house is yellow")
               })
  observeEvent(input$hasText,{
                 marker <- marker$new("#text_to_mark.shiny-text-output.shiny-bound-output")
                 marker$
                   unmark()$ # unmark all before we mark
                   mark("My house")
               })
}

# Run the application 
shinyApp(ui = ui, server = server)

请注意,这仅适用于文本的首次出现。如果您还想听文本的更改,可以将文本发送给R,然后在R侧检查文本是否已更新。不知道这里是否需要。

答案 1 :(得分:1)

侦听DOM更改是一种选择,但是您的方法已经显示出有一种纯粹的闪亮(非自定义JS)解决方案,只需多单击一次,因此问题是如何仅需单击一下即可实现。我建议使用invalidateLater并将其包装在if语句中,以防止它像看到的here那样一遍又一遍地运行。

技巧是在observe语句中运行标记调用。在此处包括invalidateLater并将其包装在if条件下,并带有一个计数器,该计数器对执行该语句的次数进行计数。用毫秒数和计数来计算,在我的情况下,它可以与if(isolate(val$cnt) < 1)invalidateLater(1000)一起正常工作。不要忘记将计数器包装在isolate中,否则它将陷入循环中。

还请注意,input$click不仅将文本写入reactValue,还将计数器val$cnt重置为0,以便您可以在新的invalidateLater上再次使用文本。如果您想使用observeEvent之类的内容来更新文本,相同的过程将为您提供帮助。只需确保将计数器重置为0,高亮显示在新文本部分即可。

# remotes::install_github("johncoene/marker")
library(shiny)
library(marker)

ui <- fluidPage(
  use_marker(),
  actionButton("click", "click"),
  textOutput("text_to_mark")
)

server <- function(input, output) {

  val <- reactiveValues(cnt = 0,
                        text = NULL)

  observeEvent(input$click, {
    val$text <- "My house is yellow"
    val$cnt <- 0

  })


  observe({
    if(isolate(val$cnt) < 1) {
      invalidateLater(1000)
    }

    marker <- marker$new("#text_to_mark.shiny-text-output.shiny-bound-output")
    marker$
      unmark()$ # unmark all before we mark
      mark("My house")

    val$cnt = isolate(val$cnt) + 1
    })

  output$text_to_mark <-renderText({
    val$text
  })
}

# Run the application 
shinyApp(ui = ui, server = server)