Is it possible to move Leaflet controls outside of the map div in a Shiny environment?
From the following code:
library(shiny)
library(leaflet)
shinyApp(
ui <- fluidPage(
sidebarPanel("This is where I want to put the leaflet layers control", width = 3),
mainPanel(
leafletOutput("map", width = "100%"),
width = 9)
),
server <- function(session, input, output){
output$map <- renderLeaflet({
leaflet() %>%
addProviderTiles("Esri.WorldTerrain", group = "Layer1") %>%
addProviderTiles("Esri.WorldImagery", group = "Layer2") %>%
addLayersControl(position = "topleft",
baseGroups = c("Layer1", "Layer2"),
options = layersControlOptions(collapsed = F))
}) # END RENDERLEAFET
} # END SERVER
) # END SHINYAPP
I saw this question, but it was specific to leaflet.js, which differs significantly from Shiny. Is there any way to do this?
It is (partly?) possible with JS.
Using JS within Shiny
To use JS within shiny library(shinyjs)
is a nice shortcut. Use useShinyjs()
on the ui side and runjs()
on the server side.
Modify HTML
As you want to modify the DOM, you have to know that one has to wait until the DOM is completely build, before you can modify it. Use session$onFlushed(function() {}
to ensure you start modifying only when the DOM is completely build.
Repositioning the HTML elements
First, you will have to know how the elements, you want to reposition and the target where you want to insert it, are named. In order to do so run the app (in a browser) right click on the required element and choose "inspect". Next, the procedure would be to save the "element-to-reposition" in a variable, remove it from the leafletmap and add it outside the map.
The JS code could look like this:
var targetName = "div.leaflet-control-layers.leaflet-control-layers-expanded.leaflet-control"
var target = document.querySelector(targetName);
var dest = document.querySelector("div.col-sm-3");
document.querySelector(targetName).remove()
dest.appendChild(target)
Challenge
If you put that all together you will notice, the control is not within the grey box, but below it. Given the code above, the reason is that it was appended to div.col-sm-3
and not to form.well
(because it does not work for form.well
). I had the idea to add an empty container in the UI tags$div(id = "insertContainer", "")
and insert it in there, but that doesn´t work either. I dont know how important it is for you to get in within the grey box, would look better probably. Maybe someone could help out there.
Code
library(shiny)
library(leaflet)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
sidebarPanel(HTML("This is where I want to put the leaflet layers control"), width = 3, height = 5),
mainPanel(
leafletOutput("map", width = "100%"),
width = 9)
)
server <- function(session, input, output){
session$onFlushed(function() {
runjs('
var targetName = "div.leaflet-control-layers.leaflet-control-layers-expanded.leaflet-control"
var target = document.querySelector(targetName);
var dest = document.querySelector("div.col-sm-3");
document.querySelector(targetName).remove()
dest.appendChild(target)
')
})
output$map <- renderLeaflet({
leaflet() %>%
addProviderTiles("Esri.WorldTerrain", group = "Layer1") %>%
addProviderTiles("Esri.WorldImagery", group = "Layer2") %>%
addLayersControl(position = "topleft",
baseGroups = c("Layer1", "Layer2"),
options = layersControlOptions(collapsed = F))
}) # END RENDERLEAFET
} # END SERVER
runApp(shinyApp(ui, server), launch.browser = TRUE)