Search code examples
rshinyr-leaflet

How to ensure popups are properly displayed regardless of initial tabPanel?


In implementing an advanced shiny popup solution from K. Rohde, I've run into a problem using a navbarPage and tabPanels. Per the linked solution, the following code is in the appropriate tabPanel:

tabPanel("Multiple Locations",
  uiOutput("script"),
  tags$div(id="garbage"),
  ...rest of UI...
)

If Multiple Locations is the only tabPanel, or if navbarPage(selected="Multiple Locations") is set, everything works wonderfully (I have implemented nearly identically to the example in the linked solution). But if navbarPage(selected="someOtherPanel") is set, and the user subsequently navigates to the Multiple Locations tabPanel, the popups show up empty.

I've tried moving the uiOutput("script") and tags$div(id="garbage") lines into the other tabPanel (the one that's active on startup), I've tried moving it right under the navbarPage (before any tabPanel elements), and I've tried duplicating it in those locations as well, to no avail. Moving it right under the navbarMenu() appears to insert a div into the menu itself. Here's the setup that works:

ui<-navbarPage(id="page", selected="Multiple Locations",
  tags$head(tags$link(href="myfavicon.ico", rel="icon"),
            includeCSS("www/style.css"),
            tags$script(type="text/javascript", src = "baranim.js")
            ),
  navbarMenu("Explorer",
    tabPanel("Single Location",
      ...UI elements...
    ),
    tabPanel("Multiple Locations",
        uiOutput("script"),
        tags$div(id="garbage"),
        ...UI elements...
    )
  )
)

Though the app is not yet complete, I don't imagine I'll have users starting on the Multiple Locations tabPanel. But clearly, I'd still like the popups to function.

Many thanks to K. Rohde for the original solution.


Solution

  • First off, I really appreciate that you liked my post. This is very encouraging.

    So I tried to reconstruct your situation and managed to reproduce the behavior you explained. My detective work showed, that you have another leaflet inside the Single Locations tab. Is that correct? The solution you linked was only designed for one single leaflet. It addresses the div of class leaflet-popup-pane, but if there are more than one, only the first one which is rendered, will be adressed. This explains the behavior with the tab choice in the beginning.

    I have modified the script such that all available leaflet-popup-panes are addressed:

    output$script <- renderUI({
      tags$script(HTML('
        var observer = new MutationObserver(function(mutations) {
          mutations.forEach(function(mutation) {
            if(mutation.addedNodes.length > 0){
              Shiny.bindAll(".leaflet-popup-content");
            };
            if(mutation.removedNodes.length > 0){
              var popupNode = mutation.removedNodes[0].childNodes[1].childNodes[0].childNodes[0];
    
              var garbageCan = document.getElementById("garbage");
              garbageCan.appendChild(popupNode);
    
              Shiny.unbindAll("#garbage");
              garbageCan.innerHTML = "";
            };
          });    
        });
    
        $(".leaflet-popup-pane").each(function() {
          observer.observe(this, {childList: true});
        });
      '))
    })
    

    This should do the trick.

    Some advice on placement: The garbage-div must only be placed once, to avoid duplicate Id problems. But it can be placed anywhere in the ui. If you have multiple leaflets with expanded popups, place one script output beneath each of those (or one beneath the last if there are several on the same page). Especially with tabPanels, this ensures that your leaflets have been fully rendered when the script is executed.

    Please comment, if this doesn't solve your problem or if there is something I missed.