As I wrote last month, code folding was the most requested feature in blogdown, of which I have given an implementation. Today I will demonstrate an implementation of another top requested feature: tabsets.
How a tabset works
The mechanism of tabsets is fairly simple. It boils down to a click
event on a
tab link, which triggers the display of a corresponding tab pane. The user
interface in HTML is like this:
<div class="tabset">
<div class="tab-link">Tab 1</div>
<div class="tab-link">Tab 2</div>
<div class="tab-pane">Pane 1</div>
<div class="tab-pane">Pane 2</div>
</div>
If the first tab link is clicked, we can add a class, say, active
, to both the
first link and the first pane.
<div class="tab-link active">Tab 1</div>
<div class="tab-pane active">Pane 1</div>
With some simple CSS, we can control the visibility of panes, and style the clicked link differently, e.g.,
.tab-pane {
display: none;
}
.tab-pane.active {
display: block;
}
.tab-link.active {
border: 1px solid;
}
My implementation
There are several existing implementations of tabsets (e.g., in Bootstrap). The problem is that they are usually not tailored to Markdown users, and you have to prepare the appropriate HTML code by yourself. I have done an implementation today that works for both Markdown and HTML users.
You can find the source code
tabsets.js
and
tabsets.css
in
my GitHub repo misc.js
. For users, you certainly do not need to read the
source, but can use it directly:
<script src="https://cdn.jsdelivr.net/npm/@xiee/utils/js/tabsets.min.js" defer></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xiee/utils/css/tabsets.min.css">
If you are not satisfied with the styling, you can provide your own CSS and do not have to use my CSS.
Demo tabs
Below is an example tabset.
First tab
Hello world! Ciao!
Second tab
Here is a table.
x | y | z |
---|---|---|
1 | ||
2 | ||
3 |
More text.
A level-4 heading
Third tab
Nested tabs!
Tab 1
Isn’t it cool? Keep going!
Tab 2
Where am I now?
You can also create a tabset using raw HTML:
Div pane 1
Div pane 2
You can keep nesting but I’ll stop here.
Fourth tab
Enough tabs? Let me show a tabset created from a bullet list instead of section headings:
-
First bullet
Hi, bullet!
-
Second bullet
This is the initial active tab.
-
Third bullet
Bye, bullet!
Okay, I’m done now.
Documentation
HTML users
If you know HTML and prefer writing HTML, the required DOM structure has been
mentioned in the first section of this post. Basically, you provide a container
element with the class tabset
(it does not have to be a <div>
). Inside the
container, you have a number of elements with the class tab-link
, and the same
number of elements with the class tab-pane
.
When the i-th link is clicked, the i-th pane will be shown. You can set a
certain tab link to be active initially by adding the class active
to its HTML
tag.
Note that you can have nested tabsets, e.g., a tabset inside a tab pane of a parent tabset.
Markdown users
If you prefer writing Markdown to be rendered to HTML by other tools (e.g., Hugo or the R package markdown), here is how you create a tabset:
-
Start with an element with the class
tabset
. This can be any type of element. For example, a heading:## Demo tabs {.tabset}
or an empty
<div>
:<div class="tabset"></div>
You can also use a fenced
Div
with the classtabset
:::: tabset :::
-
Below this element, write either a bullet list or a series of sections.
-
If you write a bullet list, the first element of each bullet item will become the tab link, and the rest of elements will become the tab pane, e.g.,
* Tab one Content of tab one. * Tab two <!--active--> Content of tab two.
I’d recommend this method since it is easier and more natural to create a tabset. However, please make sure to indent the tab pane content properly in the bullet list (using the visual mode to write Markdown in RStudio can help a lot).
To specify an initial active tab, add a comment
<!--active-->
to the bullet item. -
If you write sections, the first section heading level will be the level of headings to be converted to tabs, e.g.,
### First tab (level-3) Some tab content. ### Second tab More tab content. #### A normal heading This is a level-4 heading, so it will *not* be converted to a tab.
You can set a certain tab to be active initially by adding the class
active
to the heading, e.g.,### Second tab {.active}
One downside of using section headings to create a tabset is that the headings may be included in the table of contents of a page, which is why I do not recommend this method, unless you must specify an active tab manually.
-
-
If you use sections to create a tabset, there are two ways to end the tabset (if you create a tabset with a bullet list, you do not need a special way to end it—it just ends where the list ends):
-
Either start a upper-level heading (e.g., level 2 for the previous example), e.g.,
## My tabs {.tabset} ### Tab one ### Tab two ## A new level-2 section The previous tabset will be ended before this section.
-
or write an HTML comment of the form
<!-- tabset:ID -->
, whereID
is the ID of the element in Step #1, e.g.,## My tabs {.tabset #my-tabs} ... <!-- tabset:my-tabs --> The previous tabset will be ended before this comment.
-
You can nest tabsets in other tabsets if you want, e.g.,
<div class="tabset"></div>
- Tab one
Content
- Tab two
Content
<div class="tabset"></div>
- Child tab one
Content
- Child tab two
Content
- Tab three
If you use bullet lists to create tabsets, you can also put the list items
inside the tabset
div, which may be more intuitive, e.g.,
:::: tabset
- Tab one
Content
- Tab two
Content
::: tabset
- Child tab one
Content
- Child tab two
Content
:::
- Tab three
::::
I hope you can find this simple tabset implementation useful (it is not tied to blogdown or Hugo). Please feel free to let me know if you have any suggestions or comments.