Search code examples
rr-markdownflexdashboard

Change second tabset on click in flexdashboard


I'm working on a self-contained flexdashboard project and I'm wondering if it's possible when a user clicks to a new tab in one tabset, it changes to a new tab on a second tabset as well.

So for example, when you click on "Chart B1" below, I would also like to change view to "Chart B2" in the second column. And clicking on "Chart A2" would change back to "Chart A1", etc. etc.

enter image description here

---
title: "Untitled"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: fill
---

```{r setup, include=FALSE}
library(flexdashboard)
```

Column {.tabset}
-----------------------------------------------------------------------

### Chart A1

### Chart B1

Column {.tabset}
-----------------------------------------------------------------------

### Chart A2

### Chart B2

Note, this was first posted on RStudio Community, but did not receive any responses.


Solution

  • This can be implemented with JavaScript. Luckily, Knitr supports embedded Javascript. I am an R programmer foremost, so this may not necessarily be the most concise implementation, but it does achieve the correct effect.

        ---
        title: "Untitled"
        output: 
          flexdashboard::flex_dashboard:
            orientation: columns
            vertical_layout: fill
        ---
    
        ```{r setup, include=FALSE}
        library(flexdashboard)
        ```
    
        Column {.tabset}
        -----------------------------------------------------------------------
    
        ### Chart A1
        Chart A1 is Great
    
        ### Chart B1
        Chart B1 is Great
    
        Column {.tabset}
        -----------------------------------------------------------------------
    
        ### Chart A2
        Chart A2 is Great
    
        ### Chart B2
        Chart B2 is Great
    
        ```{js}
        // Once the Document Has Fully Loaded
        document.addEventListener("DOMContentLoaded", function(){
          // Select The Tabs
          window.a1Tab = document.querySelector("#column > ul > li:nth-child(1)");
          window.b1Tab = document.querySelector("#column > ul > li:nth-child(2)");
          window.a2Tab = document.querySelector("#column-1 > ul > li:nth-child(1)");
          window.b2Tab = document.querySelector("#column-1 > ul > li:nth-child(2)");
    
          // Select the Panel Content
          window.a1Panel = document.getElementById('chart-a1');
          window.b1Panel = document.getElementById('chart-b1');
          window.a2Panel = document.getElementById('chart-a2');
          window.b2Panel = document.getElementById('chart-b2');
    
          // If We Click on B1, Open B2, Close A2
          b1Tab.addEventListener('click', function(){
            a2Tab.classList.remove('active');
            a2Tab.children[0].setAttribute('aria-expanded', true);
            a2Panel.classList.remove('active');
    
            b2Tab.classList.add('active');
            b2Tab.children[0].setAttribute('aria-expanded', true);
            b2Panel.classList.add('active');
          }, false);
    
          // If We Click on B2, Open B1, Close A1
          b2Tab.addEventListener('click', function(){
            a1Tab.classList.remove('active');
            a1Tab.children[0].setAttribute('aria-expanded', true);
            a1Panel.classList.remove('active');
    
            b1Tab.classList.add('active');
            b1Tab.children[0].setAttribute('aria-expanded', true);
            b1Panel.classList.add('active');
          }, false);
    
          // If We Click on A1, Open A2, Close B2
          a1Tab.addEventListener('click', function(){
            b2Tab.classList.remove('active');
            b2Tab.children[0].setAttribute('aria-expanded', true);
            b2Panel.classList.remove('active');
    
            a2Tab.classList.add('active');
            a2Tab.children[0].setAttribute('aria-expanded', true);
            a2Panel.classList.add('active');
          }, false);
    
          // If We Click on A2, Open A1, Close B1
          a2Tab.addEventListener('click', function(){
            b1Tab.classList.remove('active');
            b1Tab.children[0].setAttribute('aria-expanded', true);
            b1Panel.classList.remove('active');
    
            a1Tab.classList.add('active');
            a1Tab.children[0].setAttribute('aria-expanded', true);
            a1Panel.classList.add('active');
          }, false);
        });
    
        ```
    
    

    Edit: For an unlimited number of tabs, assuming that you will always have the same number of tabs in the first and second column. Same disclaimer, this is not necessarily the most concise implementation, but achieves the desired effect.

    
        ---
        title: "Untitled"
        output: 
          flexdashboard::flex_dashboard:
            orientation: columns
            vertical_layout: fill
        ---
    
        ```{r setup, include=FALSE}
        library(flexdashboard)
        ```
    
        Column {.tabset}
        -----------------------------------------------------------------------
    
        ### Chart A1
        Chart A1 is Great
    
        ### Chart B1
        Chart B1 is Great
    
        ### Chart C1
        Chart C1 is Great
    
        ### Chart D1
        Chart D1 is Great
    
        Column {.tabset}
        -----------------------------------------------------------------------
    
        ### Chart A2
        Chart A2 is Great
    
        ### Chart B2
        Chart B2 is Great
    
        ### Chart C2
        Chart C2 is Great
    
        ### Chart D2
        Chart D2 is Great
    
        ```{js}
        // Once the Document Has Fully Loaded
        document.addEventListener("DOMContentLoaded", function(){
          // Select The Tabs
          window.col1Tabs = document.querySelector("#column > ul");
          window.col2Tabs = document.querySelector("#column-1 > ul");
    
          // Select the Panel Content
          window.col1Panels = document.querySelector("#column > div");
          window.col2Panels = document.querySelector("#column-1 > div");
    
          // Function to Make Tabs Active
          window.handleTab = function(tabIndex){
            for(i=0;i<col1Tabs.childElementCount;i++){
              col1Tabs.children[i].classList.remove('active');
              col2Tabs.children[i].classList.remove('active');
              col1Panels.children[i].classList.remove('active');
              col2Panels.children[i].classList.remove('active');
            }
            col1Tabs.children[tabIndex].classList.add('active');
            col2Tabs.children[tabIndex].classList.add('active');
            col1Panels.children[tabIndex].classList.add('active');
            col2Panels.children[tabIndex].classList.add('active');
          }
    
          // For All Tabs, Add Event Listener
          for(i=0;i<col1Tabs.childElementCount;i++){
            col1Tabs.children[i].setAttribute('onclick', 'handleTab(' + i + ');');
            col2Tabs.children[i].setAttribute('onclick', 'handleTab(' + i + ');');
          }
    
        });
    
        ```
    
    

    Edit: For anyone who may find this question later, a JQuery implementation with more flexibility was added to the Github Issue.