With a little bit of JQuery, the JavaScript function Shiny.setInputValue
lets you take any HTML element and make it a Shiny input
which I’ll display here using a div called all_circles
which contains three circles. Think of all_circles
as a widget like selectInput
, the circles themselves would then be the same as setting choices
.
selectInput("inputID", "Name to Display", choices = c("Red Circle", "Yellow Circle", "Blue Circle"))
Because we can envision use cases to be beyond clicking on simple shapes, I wanted to use SVGs
, which was my first learning curve.
<svg class="circ" height="100" width="100" xmlns="http://www.w3.org/2000/svg">
<circle id ='circ_red' cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
Note that the svg class is circ
and the circle has the unique id circ_red
. Repeating this code two more times but changing the fill to blue
and yellow
and the circle ids to circ_blue
and circ_yellow
yeilds three circles, all of the same class but with their own unique ids:
Shiny can take on a lot of HTML tags, but I found to properly render SVGs I needed to add a uiOutput
to the UI portion of the app
And put the SVG code in a reactive within the server:
$svgout <- renderUI({
outputHTML(
"<svg class='circ' height='100' width='100' xmlns='http://www.w3.org/2000/svg'>
<circle id ='circ_red' cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
<svg id ='circ_blue' class='circ' height='100' width='100' xmlns='http://www.w3.org/2000/svg'>
<circle id ='circ_blue' cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='blue' />
</svg>
<svg id ='circ_yellow'class='circ' height='100' width='100' xmlns='http://www.w3.org/2000/svg'>
<circle id ='circ_yellow' cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='yellow'/>
</svg>"
) })
Note that I wrapped the SVG within a div called all_circles
- this is the div that will act as our selectInput
.
JavaScript Time!
In the next little chunk of code we use JQuery to go into the div of class all_circles
and find the id of the circ
class that was clicked on. We can then take that id and assign it as the value of the shiny input all_circles
.
$script(HTML(
tags"$('#all_circles').on('click', '.circ', (ev) => {
Shiny.setInputValue('all_circles', ev.target.id)
})
"
))
And there you have it! The #
denotes that we’re looking for an id and the .
a class name. We’re using a callback function to get the id of the clicked svg, which you can learn more about in some of my prior blog posts. By adding verbatimTextOutput("debug")
and in the server input$debug <- renderText(input$all_circles)
you’ll see that the input changes from red to yellow to blue on click!
My revelation to set custom Shiny inputs was an incredibly powerful one that I needed to share, as it enables assigning whatever you want as an input with only a little bit of JQuery to find the elements you want to set as your choices
. For instance, I used This opensource library of a human body made fully with CSS and SVGs to create a div
called human_body
, then made the “choices” each of the body part SVGs. I hope this opens up new ways of exploring and interacting with data as it did for me!!