Here is a shiny app:
library(shiny)
library(leaflet)
nc = sf::st_read(system.file("shape/nc.shp", package="sf"))
ui <- function(request){
tagList(
selectInput("color", "color", c("blue", "red", "green")),
leafletOutput("map")
)
}
server <- function(
input,
output,
session
){
output$map <- renderLeaflet({
leaflet(nc) %>%
addPolygons(color = input$color)
})
}
shinyApp(ui, server)
I want to bind an event on click on each polygon, but I want it to happen from the client-side only, I don't want it to go through R. For example, I would like to send an alert when the user click on the polygon.
I've got some hacky code that does that, but I would love to have a clean way to do it.
What I'm looking for is a way to define from R something that would look like addPolygon(onClick = "alert('hello there')")
.
To be clear I don't want this to go through the server, I want everything to happen in the browser.
It works with the following JS code (in ext/script.js)
$(document).ready(function() {
Shiny.addCustomMessageHandler('bindleaflet', function(arg) {
$("#" + arg).find("path").remove();
wait_for_path(arg);
})
});
var wait_for_path = function(id) {
if ($("#" + id).find("path").length !== 0) {
$("#" + id).find(".leaflet-interactive").on("click", function(x) {
alert("hey")
})
} else {
setTimeout(function() {
wait_for_path(id);
}, 500);
}
}
Then in R
library(shiny)
library(leaflet)
nc = sf::st_read(system.file("shape/nc.shp", package="sf"))
ui <- function(request){
tagList(
tags$script(src = "ext/script.js"),
selectInput("color", "color", c("blue", "red", "green")),
leafletOutput("map")
)
}
server <- function(
input,
output,
session
){
addResourcePath("ext", "ext")
output$map <- renderLeaflet({
session$sendCustomMessage("bindleaflet", "map")
leaflet(nc) %>%
addPolygons(color = input$color)
})
}
shinyApp(ui, server)
But that seems overly complicated for something you would define as such when building the leaflet in pure JS:
onEachFeature: function(feature, layer) {
layer.on({
click: (function(ev) { alert('hey') } )
Is there a way to natively do it when building the app in R?
I've built a reprex of my current code here: https://github.com/ColinFay/leaflet-shiny-click-event
As mentioned in the comments we can use htmlwidgets::onRender
to pass custom JS code.
With the help of the eachLayer method we can add an on-click function to each polygon layer (also see this related answer):
library(shiny)
library(leaflet)
nc = sf::st_read(system.file("shape/nc.shp", package="sf"))
ui <- function(request){
tagList(
selectInput("color", "color", c("blue", "red", "green")),
leafletOutput("map")
)
}
server <- function(
input,
output,
session
){
output$map <- renderLeaflet({
leaflet(nc) %>%
addPolygons(color = input$color) %>%
htmlwidgets::onRender("
function(el, x) {
var map = this;
map.eachLayer(function(layer) {
if(layer instanceof L.Polygon && !(layer instanceof L.Rectangle) ){
layer.on('click', function(e){
alert('hey - you clicked on layer._leaflet_id: ' + layer._leaflet_id);
})
.addTo(map)
}
});
}
")
})
}
shinyApp(ui, server)