简体   繁体   English

R ggiraph 在没有 Shiny 的情况下动态设置工具提示文本

[英]R ggiraph dynamically setting tooltip text without Shiny

What : Dynamically set the contents of ggiraph tooltips in rmarkdown knitted to html on page load.内容:在页面加载时将rmarkdown中的 ggiraph 工具提示内容动态设置为 html。

Why : Using embedded png's tooltips can be made to be graphics which is valuable for certain biological structures where text is insufficient.原因:使用嵌入式 png 的工具提示可以制作成图形,这对于文本不足的某些生物结构很有价值。 Here is a minimal example of what I'm currently doing:这是我目前正在做的一个最小示例:

encodedImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAUCAIAAAAcIrrpAAAACXBIWXMAAASdAAAEnQF8NGuhAAADGElEQVR4nI2Uy08TURTG+VuMJmCCoIKIxq2ujEQT9y40yKsiILrSGITIzoVxZUwDFQlKQiC0ttMpMwXsi9JSSkvftJ0WWkuhBVtoAb+ZKeNQy+PkZjJz5t7fPfec892yg1K2u7u7tbW1vr4ejUYDgYDL5fJ4PHhhGCYej+NXyVWwsqLvnZ0dULAsFAphvdfrBcvhcNjt9oWFhfn5eZPJpNfr8b62tra/v38sLpPJxGIxTML+q6urIAaDQZ/P53a7nU4ncFar1Ww2G41GnU43MzND0/T09DR2LYHb3t4OcxaJRAQiPq2u8KI36vDHXKGEJ5xkX/yMPxCYm5sjSVKlUikUCmx5BIdcrHCGcIBAXCBig3w+X95E1HVR4nG9i7rWSd3opls+mX+ZrJOTkxMTE8hsAYc1vkPz+/2AIsBcLsf/vtKuOfdEWXJcaFTdeTPLMJHx8fGxsTFknMVtbGwgOx7OkHhEhw2E4E/AYZQ3E8Na9iijo6M2m43FIZfLnKGC4KZSKXFqBVxFs/rRB/PDfuP9PgPOe75RxfsbevWYRlEUksjigHBwhvIBKg5NjKvvpgVnNJm9+nyK9996pYUHJxsZGWFxSPySyI7DIf3h35lg/A+en4mVKgnJ++/26DANJyMIgsVtbm4ucmbjLJFIlMRVtpIoa23HFJ6VbaRQjfc/3JimVCoRCovb29vDm5Uzi8WCU0MYZyxF/Qs6nclDPENDQ+l0utB30AOig4DQ9OhPcNF0J+NqO6l77/RMIgO1DA4OouePqCKbzUKGEJDBYIAkISOUBVUWcOXNaqSp5rAC1c80PTITOm5gYAASKhbZASd+hA0W9KjVajUaDTJyqZXg19d1aHC7qHVL1ZKC52KT/Mt3dTKZFOe6+EaBHiA1EKFHuVxe2fKTX3y5TS6TyaRSaXv/14qnCt55+/Vs0fJiHG+4eZA+XDDVEjW/sqZdjcZEzyOchl5dodxtJGGNn477v7LiNrYHU8gd77/5khZfeqfgqiQlcLAHfQZBtlIyeFbc448WSBWjZ3hZ7HeG07wf4+23f7/+An2Gx2N7QYsiAAAAAElFTkSuQmCC"

g = ggplot()+
  geom_point_interactive(aes(x=1,y=1,tooltip=sprintf('<img src=\"%s\" />',encodedImage)))
girafe(code=print(g))

The downside to this is that for every plot using the tooltip graphic the encoded image is repeated, resulting in a file size that is too large to store many of.这样做的缺点是,对于使用工具提示图形的每个 plot,编码的图像都会重复,导致文件太大而无法存储许多文件。

How : To mitigate the increase in file size with the increase in use of tooltips I'm looking to assign the embedded image text into a json object, then dynamically update all tooltips to be the embedded image using javascript.如何:为了减轻文件大小随着工具提示使用的增加而增加的问题,我希望将嵌入图像文本分配到 json object,然后使用 ZDE9B9ED78D7E2E19DCEEFFEE780E2 动态更新所有工具提示以成为嵌入图像。

Things I've Done : I'm able to get the embedded image stored in a json by simply including script tags and outputing json text inside of those tags.我已经完成的事情:我可以通过简单地包含脚本标签并在这些标签中输出 json 文本来获取存储在 json 中的嵌入图像。 For testing heres the hard-coded example with a simple replace text:为了测试这里的硬编码示例,带有一个简单的替换文本:

<script type="application/json" id="lookuptable">
{"ID1":"ReplaceText"}
</script>

What I can't do is replace the text of the tooltips.我不能做的是替换工具提示的文本。 Essentially I was planning on setting the tooltip to and ID of some sort and using that to match the embedded image to the corresponding point.本质上,我计划将工具提示设置为某种类型的 ID,并使用它来将嵌入的图像与相应的点相匹配。 The tooltip text is stored in a json by ggiraph in something like:工具提示文本由 ggiraph 存储在 json 中,如下所示:

<script type="application/json" data-for="htmlwidget-b8ceca7828d4dd46f692"> {x:{"html":.....}}</script>

In this json all html components (<,",>,etc) are "escaped," I believe this "htmlwidget" data is passed to what is essentially a mini-html page embedded in the html page, the "htmlwidget".在这个 json 中,所有 html 组件(<、、>等)都被“转义”了,我相信这个“htmlwidget”数据被传递给嵌入在 ZFC35FDC70D5FC69D269883A82E2 中的基本上是一个 mini-html 页面的页面。

I've tried wrapping my temporary tooltips in <div> tags, but since they are tied up in a JSON they aren't seen in the DOM.我尝试将我的临时工具提示包装在 <div> 标签中,但由于它们被绑定在 JSON 中,因此在 DOM 中看不到它们。

I've tried just naively replacing all instances if ID in any script tags:如果任何脚本标签中的 ID 存在,我已经尝试过天真地替换所有实例:

<script type="application/javascript">
  console.log(document.getElementById('lookuptable').innerHTML)
  var lookuptable = JSON.parse(document.getElementById('lookuptable').innerHTML);
  
  var scriptTags = document.getElementsByTagName("script");
  for (s=0; s < scriptTags.length; s++){
    var item = scriptTags[s];
    for(var k in lookuptable){
      console.log(item.innerHTML);
      item.innerHTML.replace(k,lookuptable[k])
    }
  }
</script>

However it seems that by the time that this script runs the json no longer has the tooltip text in it (though it's in the html source).然而,当这个脚本运行时,json 似乎不再有工具提示文本(尽管它在 html 源中)。

This is where I currently am stuck.这是我目前卡住的地方。 I'll update or answer this question if I make any more progress.如果我取得更多进展,我会更新或回答这个问题。

Also I'm well aware this would be trivial with Shiny, unfortunately that is not an option as the html page needs to be entirely standalone as the real rmarkdown I'm building is meant to be a standalone report.我也很清楚这对于 Shiny 来说是微不足道的,不幸的是,这不是一个选项,因为 html 页面需要完全独立,因为我正在构建的真正 rmarkdown 是一个独立的报告。

Finally here is a complete, reproducible example.最后,这是一个完整的、可重复的示例。 The final knitted project should result in the tooltip being "ReplaceText" (fix R code blocks by removing \):最终的针织项目应导致工具提示为“ReplaceText”(通过删除 \ 来修复 R 代码块):

---
title: "Demo"
author: "Zachary Klamer"
date: "12/9/2021"
output: html_document
---

\```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(ggplot2)
library(ggiraph)
library(rjson)
\```

\```{r,echo=FALSE}
lookup = list()
lookup$FullID1 = "ReplaceText"
jsonData = toJSON(lookup)
\```
<script type="application/json" id="lookuptable">
`r jsonData`
</script>



\```{r,echo=FALSE}
g = ggplot()+
  geom_point_interactive(aes(x=1,y=1,tooltip='FullID1'))
girafe(code=print(g))
\```

<script type="application/javascript">
  console.log(document.getElementById('lookuptable').innerHTML)
  var lookuptable = JSON.parse(document.getElementById('lookuptable').innerHTML);

  var scriptTags = document.getElementsByTagName("script");
  for (s=0; s < scriptTags.length; s++){
    var item = scriptTags[s];
    for(var k in lookuptable){
      console.log(item.innerHTML);
      item.innerHTML.replace(k,lookuptable[k])
    }
  }
</script>

There may be several different ways to achieve this goal, in particular I suspect that using the htmlwidgets "onRender" function may be able to achieve this more cleanly but I never got that to work.可能有几种不同的方法可以实现这个目标,特别是我怀疑使用 htmlwidgets "onRender" function 可能能够更干净地实现这个目标,但我从来没有让它工作。

What I've found is that any editing of the innerHTML of the htmlwidget or htmlwidget data breaks the mouseover text completely, because it breaks the event listener which powers the mouseover text.我发现,对 htmlwidget 或 htmlwidget 数据的 innerHTML 的任何编辑都会完全破坏鼠标悬停文本,因为它会破坏为鼠标悬停文本提供动力的事件侦听器。

Instead I've found I can edit the resulting svg of the htmlwidget by wrapping my ending script in a $(window).load(function(){... }) call.相反,我发现我可以通过将结束脚本包装在 $(window).load(function(){... }) 调用中来编辑 htmlwidget 生成的 svg。 If I find all svg elements (in this case circles.) and edit the title property of those svg objects I can preserve the event listeners and change the title contents (to be an image!).如果我找到所有 svg 元素(在本例中为圆圈。)并编辑这些 svg 对象的标题属性,我可以保留事件侦听器并更改标题内容(成为图像!)。

See complete example below which has 1000 1kb images as tooltips but gives no increase in file size:请参阅下面的完整示例,其中有 1000 个 1kb 图像作为工具提示,但文件大小没有增加:

---
title: "Demo"
author: "Zachary Klamer"
date: "12/9/2021"
output: html_document
---

\```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(ggplot2)
library(ggiraph)
library(rjson)
library(htmlwidgets)
\```

\```{r,echo=FALSE}
lookup = list()

lookup$FullID1 = '<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAUCAIAAAAcIrrpAAAACXBIWXMAAASdAAAEnQF8NGuhAAADGElEQVR4nI2Uy08TURTG+VuMJmCCoIKIxq2ujEQT9y40yKsiILrSGITIzoVxZUwDFQlKQiC0ttMpMwXsi9JSSkvftJ0WWkuhBVtoAb+ZKeNQy+PkZjJz5t7fPfec892yg1K2u7u7tbW1vr4ejUYDgYDL5fJ4PHhhGCYej+NXyVWwsqLvnZ0dULAsFAphvdfrBcvhcNjt9oWFhfn5eZPJpNfr8b62tra/v38sLpPJxGIxTML+q6urIAaDQZ/P53a7nU4ncFar1Ww2G41GnU43MzND0/T09DR2LYHb3t4OcxaJRAQiPq2u8KI36vDHXKGEJ5xkX/yMPxCYm5sjSVKlUikUCmx5BIdcrHCGcIBAXCBig3w+X95E1HVR4nG9i7rWSd3opls+mX+ZrJOTkxMTE8hsAYc1vkPz+/2AIsBcLsf/vtKuOfdEWXJcaFTdeTPLMJHx8fGxsTFknMVtbGwgOx7OkHhEhw2E4E/AYZQ3E8Na9iijo6M2m43FIZfLnKGC4KZSKXFqBVxFs/rRB/PDfuP9PgPOe75RxfsbevWYRlEUksjigHBwhvIBKg5NjKvvpgVnNJm9+nyK9996pYUHJxsZGWFxSPySyI7DIf3h35lg/A+en4mVKgnJ++/26DANJyMIgsVtbm4ucmbjLJFIlMRVtpIoa23HFJ6VbaRQjfc/3JimVCoRCovb29vDm5Uzi8WCU0MYZyxF/Qs6nclDPENDQ+l0utB30AOig4DQ9OhPcNF0J+NqO6l77/RMIgO1DA4OouePqCKbzUKGEJDBYIAkISOUBVUWcOXNaqSp5rAC1c80PTITOm5gYAASKhbZASd+hA0W9KjVajUaDTJyqZXg19d1aHC7qHVL1ZKC52KT/Mt3dTKZFOe6+EaBHiA1EKFHuVxe2fKTX3y5TS6TyaRSaXv/14qnCt55+/Vs0fJiHG+4eZA+XDDVEjW/sqZdjcZEzyOchl5dodxtJGGNn477v7LiNrYHU8gd77/5khZfeqfgqiQlcLAHfQZBtlIyeFbc448WSBWjZ3hZ7HeG07wf4+23f7/+An2Gx2N7QYsiAAAAAElFTkSuQmCC\" />'
jsonData = toJSON(lookup)
\```
<script type="application/json" id="lookuptable">
`r jsonData`
</script>


\```{r,echo=FALSE}
g = ggplot()+
  geom_point_interactive(aes(x=1:1000,y=1:1000,tooltip='FullID1'))
 girafe(code=print(g))
\```

<script type="application/javascript">
  $(window).load(function(){
    var lookuptable = JSON.parse(document.getElementById('lookuptable').innerHTML);
    var keys = Object.keys(lookuptable);
    var circleTags = document.getElementsByTagName("circle");
    for (s=0; s < circleTags.length; s++){
      var item = circleTags[s];
      var itemTitle = item.attributes.title.nodeValue;
      console.log(itemTitle);
      if (keys.includes(itemTitle)){
        item.attributes.title.nodeValue=lookuptable[itemTitle];
      }
      console.log(item.attributes.title.nodeValue);
    }
  });
</script>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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