Change second tabset on click in flexdashboard - r

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.
---
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.

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.

Just to add another answer using JQuery and boostrap tabset JS function.
It is more concise than other answer but works the same with any number of tabs (but the same number) in your two column format.
---
title: "Untitled"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
```
```{js}
document.addEventListener("DOMContentLoaded", function(){
$('a[data-toggle="tab"]').on('click', function(e){
// find the tab index that is click
child = e.target.parentNode;
tabnum = Array.from(child.parentNode.children).indexOf(child);
// find in which column we are
column = $(e.target).closest("div[id]");
// show the same tab in the other column
columnid = column.attr("id");
if (columnid == "column") {
columnto = "column-1";
} else {
columnto = "column";
}
$("div[id="+columnto+"] li:eq("+tabnum+") a").tab('show');
})
});
```
Column {.tabset}
-----------------------------------------------------------------------
### Chart A1
This is a text
### Chart B1
```{r}
plot(iris)
```
Column {.tabset}
-----------------------------------------------------------------------
### Chart A2
```{r}
plot(mtcars)
```
### Chart B2
This is another text

Related

How to add headers and linebreaks?

I've created a dashboard using flexdashboard with row layout. My first row includes valueboxes and my second row is a leaflet map. How do I add a header above the 2nd row?
As in the code below, I used ### Map header under the 2nd row but that renders as a small title (shown in output). Is there a way to:
Add newlines between the 2 rows?
To format the header to be increase font size and bold?
title: "Dashboard"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: scroll
smooth_scroll: true
---
<br><br><br>
```{r setup, include=FALSE}
library(flexdashboard)
library(leaflet)
```
Row
-----------------------------------------------------------------------
### Articles per Day
```{r}
valueBox("5 articles", icon = "fa-pencil")
```
### Comments per Day
```{r}
valueBox("10 comments", icon = "fa-comments")
```
Row
-----------------------------------------------------------------------
### Map
```{r}
m <- leaflet() %>%
addTiles() %>%
setView(lng=-77.030137, lat=38.902986, zoom = 16) %>%
addMarkers(lng=-77.030137, lat=38.902986, popup="<b>Hello</b><br><a href='https://www.washingtonpost.com'>-Me</a>")
m
```
Output:
You can increase font-size and bold the header ### Map specifically, you can target it by using a css class, say, .custom and apply the css rules for .chart-title class.
And to create space between value-boxes and the map, one option could be increasing the top margin.
---
title: "Dashboard"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: scroll
smooth_scroll: true
---
<br><br><br>
```{css, echo=FALSE}
.custom {
margin-top: 30px;
}
.custom .chart-title{
font-size: 30px;
font-weight: bold;
}
```
```{r setup, include=FALSE}
library(flexdashboard)
library(leaflet)
```
Row
-----------------------------------------------------------------------
### Articles per Day
```{r}
valueBox("5 articles", icon = "fa-pencil")
```
### Comments per Day
```{r}
valueBox("10 comments", icon = "fa-comments")
```
Row
-----------------------------------------------------------------------
### Map {.custom}
```{r}
m <- leaflet() %>%
addTiles() %>%
setView(lng=-77.030137, lat=38.902986, zoom = 16) %>%
addMarkers(lng=-77.030137, lat=38.902986, popup="<b>Hello</b><br><a href='https://www.washingtonpost.com'>-Me</a>")
m
```

R flexdashboard crosstalk date filter_slider does not fit in the box

I am having trouble using flexdashboard with crosstalk's filter_slider with dates. It does not seem to fit well in the box in the flexdashboard dashboard (see the image below). Increasing the size of the box does not help as the filter just gets bigger.
Is there a way to make the filter fit in the box? I assume that adding css specifying padding might help, but I can't make it work.
Here is how to reproduce my example:
---
title: "test"
output:
flexdashboard::flex_dashboard
---
```{r setup, include=FALSE}
library(crosstalk)
dat <- data.frame(
a = seq.Date(from = Sys.Date()-100, to = Sys.Date(), by = "days")
)
dat_shared <- SharedData$new(dat)
```
Column {data-width=800}
-------------------------------------
### Filtry
```{r}
filter_slider("date", "Date", dat_shared, ~a)
```
Column {data-width=400}
-------------------------------------
Following is a jQuery function that fires every second. For the left side of the filter, it checks if the date from value (css property .irs-from) is equal to the min value (.irs-min). If so, it changes the css property to 0% (originally under 0). This way the slider fits on the screen. The if clause is important because if one uses the filter, the box moves to the position of the selection. In that case you probably want it to stay there, not to move to the beginning. For the right side, it is very similar, but with .irs-from and .irs-max.
var fitTheScreen = window.setInterval(function(){
if ( $(".irs-from").text() === $(".irs-min").text() ) {
$(".irs-from").css({"left": "0%"});
}
if ( $(".irs-to").text() === $(".irs-max").text() ) {
$(".irs-to").css({"left": "86%"});
}
}, 1000);
This is what the result looks like:
Runnable example here:
---
title: "test"
output:
flexdashboard::flex_dashboard
---
```{r setup, include=FALSE}
library(crosstalk)
dat <- data.frame(
a = seq.Date(from = Sys.Date()-100, to = Sys.Date(), by = "days")
)
dat_shared <- SharedData$new(dat)
```
```{js, echo=FALSE}
var fitTheScreen = window.setInterval(function(){
if ( $(".irs-from").text() === $(".irs-min").text() ) {
$(".irs-from").css({"left": "0%"});
}
if ( $(".irs-to").text() === $(".irs-max").text() ) {
$(".irs-to").css({"left": "86%"});
}
}, 1000);
```
Column {.sidebar data-width=400}
-------------------------------------
### Filtry
```{r}
filter_slider("date", "Date", dat_shared, ~a)
```
Column {data-width=800}
-------------------------------------

Flexdashboard: set anchors for ### headings (level3)

In flexdashbard, we can create different headings:
Heading level 1: they are pages
Heading level 2: we specify Row or Column
Heading level 3: tabs
Now for anchors, it seems that we can only set them for heading level 1. As we can see it in this article.
My question is, it is possible to create anchors for tabs (so heading level 3)?
I tried to find a solution. For example, with the following code:
Page 4
=====================================
## Row {.tabset}
### tab 1 {#test1}
### tab 2 {#test2}
The anchor is automatically created for "Page 4" which is #page-4. For "tab 1", I tried to add {#test1}, but it doesn't work.
EDIT: solution with javascript
Another solution that would work for me is to use javascript, to go the the next tab.
First we can add a javascript
<script type="text/javascript">
$('.btnNext').click(function(){
$('.nav-tabs > .active').next('li').find('a').trigger('click');
});
$('.btnPrevious').click(function(){
$('.nav-tabs > .active').prev('li').find('a').trigger('click');
});
</script>
Then, we can create buttons to navigate
<a class="btn btn-primary btnNext">Next</a>
<a class="btn btn-primary btnPrevious">Previous</a>
But I testd in R Markdown, it doesn't work.
update
I now figured out a way to go from #page-4 #test1 to #page-5 #test4 by clicking a link. I use a bit of javascript to read the URLs parameters. This allows us to define links like a(href="?page5&tab=4"). The javascript will get the parameters, in our case page as 5 and tab as 4 and then execute two clicks, one to get to #page-5 and another one to get the tab 4 called #test4. There are probably better options which allow you to set the active page tab and tabset tab, but I didn't get them working with {flexdashboard}. Anyway, I hope the approach below solves your problem.
---
title: "Untitled"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
library(htmltools)
```
```{js}
document.addEventListener("DOMContentLoaded", function(){
var url_string = window.location.href;
var url = new URL(url_string);
var page = url.searchParams.get('page');
var pageid = '#page-'.concat(url.searchParams.get('page'));
var tab = 'tab'.concat(url.searchParams.get('tab'));
var tabid = '#test'.concat(url.searchParams.get('tab'));
$('a[href="'+ pageid +'"]').click();
$('a[href="'+ tabid +'"]').click();
});
```
Page 4
=====================================
## Row {.tabset}
### tab 1 {#test1}
```{r}
tags$a(href = "?page=5&tab=4",
shiny::actionButton("btn1",
"go to page-5 tab4"
))
```
### tab 2 {#test2}
```{r}
tags$a(href = "#test1",
shiny::actionButton("btn4",
"go to tab1"
))
```
Page 5
=====================================
## Row {.tabset}
### tab 3 {#test3}
```{r}
tags$a(href = "#test4",
shiny::actionButton("btn5",
"go to tab4"
))
```
### tab 4 {#test4}
```{r}
tags$a(href = "?page=4&tab=2",
shiny::actionButton("btn6",
"go to page-4 tab2"
))
```
old answer
In my case your header level 3 anchors ({#test1} etc.) are working even when not using runtime: shiny. You can change the tabs via action buttons, but only if you are on the same page. For example you can from tab1 to tab2 on page 4 but you cannot go from tab1 on page 4 to tab4 on page 5. But changing from page 4 to page 5 is again possible.
---
title: "Untitled"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
library(htmltools)
```
Page 4
=====================================
## Row {.tabset}
### tab 1 {#test1}
```{r}
tags$a(href = "#test2",
shiny::actionButton("btn1",
"go to tab2"
))
tags$a(href = "#test1",
shiny::actionButton("btn2",
"go to tab3 (not working)"
))
tags$a(href = "#page-5",
shiny::actionButton("btn3",
"go to page5 (working)"
))
```
### tab 2 {#test2}
```{r}
tags$a(href = "#test1",
shiny::actionButton("btn4",
"go to tab1"
))
```
Page 5
=====================================
## Row {.tabset}
### tab 3 {#test3}
```{r}
tags$a(href = "#test4",
shiny::actionButton("btn5",
"go to tab4"
))
```
### tab 4 {#test4}
```{r}
tags$a(href = "#test3",
shiny::actionButton("btn6",
"go to tab3"
))
```

remove text post clicking the button

below is my reprex. Post clicking the upload button, the text appears. Upon clicking the clear button the text should go off. Wanted to check the way to do this. Can anyone help me here
---
title: "Untitled"
runtime : shiny
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
code <- "This is code"
```
Column {data-width=650}
-----------------------------------------------------------------------
### Chart A
```{r}
actionButton("upload","Upload",width = 150)
actionButton("clear_upload","Clear",width = 150)
verbatimTextOutput("code")
get_code <- eventReactive(input$upload,{
code
})
output$code <- renderPrint(
get_code()
)
```
If I've correctly understood your problem, you may use the the observeEvent statement:
---
title: "Untitled"
runtime : shiny
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
code <- "This is code"
```
Column {data-width=650}
-----------------------------------------------------------------------
### Chart A
```{r}
actionButton("upload","Upload",width = 150)
actionButton("clear_upload","Clear",width = 150)
verbatimTextOutput("code")
get_code <- eventReactive(input$upload,{
code
})
observeEvent(input$upload, {output$code <- renderPrint(get_code())})
observeEvent(input$clear_upload, {output$code <- renderPrint("")})
```

align text side by side to figure

How can I put the figure on left and text on right and align it left (the text)?
---
title: "Untitled"
author: "George"
date: "12/3/2018"
output:
flexdashboard::flex_dashboard:
orientation: rows
runtime: shiny
---
```{r global, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
library(flexdashboard)
library(dplyr)
library(GGally)
x <- c(1,2,3)
y <- c(11,22,33)
z <- data.frame(x, y)
```
Introduction
=======================================================================
### General info
- A
- B
Corr
=======================================================================
### Correlation
Here is some text
- One
- Two
```{r}
renderPlot({
GGally::ggpairs(z)
})
```
try this, this layout fills the page completely and gives prominence to a single chart on the left where you can have your img and two secondary charts included to the right- where you can have your text
title: "Focal Chart (Left)"
output: flexdashboard::flex_dashboard
---
Column {data-width=600}
-------------------------------------
### Chart 1
```{r}
```
Column {data-width=400}
-------------------------------------
### Chart 2
```{r}
```
### Chart 3
```{r}
```

Resources