Search code examples
htmlrr-markdownknitrquarto

r Quarto subcaptions for more than 26 figures (HTML)


I'm rendering a Quarto document to HTML which contains a code chuck that generates more than 26 figures, to which I'm adding subcaptions. Quarto uses letters to sub-caption figures. In the English language, we only have 26 letters, so the 27th figure subcaption continues with the next ASCII character which is {. How do I label the 27th figure subcaption with aa, then bb, etc?

Below is the last row of charts. Notice the figure on the right is captioned ({) and I'd like that to be (aa).

enter image description here

EDIT: Note that the code below is a REPREX, and the answer should be for the general case. I.e., my figure subcaptions are NOT static and will change from script to script.

---
title: "27Figures"
format: 
  html
---

```{r}
#| label: fig-charts
#| layout-ncol: 2
#| fig-cap: "Plots of all data for all extensometer sensors."
#| fig-subcap: 
#|   - "DM0001_1"
#|   - "DM0001_2"
#|   - "DM0001_3"
#|   - "DM0001_4"
#|   - "DM0001_5"
#|   - "DM0002_3"
#|   - "DM0002_4"
#|   - "DM0003_1"
#|   - "DM0003_2"
#|   - "DM0003_3"
#|   - "DM0003_4"
#|   - "DM0003_5"
#|   - "DM0004_1"
#|   - "DM0004_2"
#|   - "DM0004_3"
#|   - "DM0004_4"
#|   - "DM0004_5"
#|   - "DM0005_1"
#|   - "DM0005_2"
#|   - "DM0005_3"
#|   - "DM0005_4"
#|   - "DM0005_5"
#|   - "DM0006_1"
#|   - "DM0007_1"
#|   - "DM0008_1"
#|   - "DM0009_1"
#|   - "DM0010_1"
#|   - "DM0010_2"
#|   - "DM0010_3"
  
    for (i in 1:30) { 
        plot(i, i)
    }
```

Solution

  • Add a JavaScript function that runs after the document loads that finds all subfigure captions using the class quarto-subfloat-caption and for captions beyond the 26th figure (index >= 26):

    • Extracts the figure_label -> everything behind the ") "
    • Calculates the appropriate double letter (aa, bb, etc.)
    • Replaces the caption text with the new format: (*letter**letter*) *figure_label*

    Figure 27 will show as "(aa) Fig-aa" as you wanted! Edit: It will reset this now for each new figure.

    ---
    title: "27Figures"
    format: 
      html:
        include-after-body:
          text: |
            <script>
              // Function to replace figure subcaptions beyond 'z'
              document.addEventListener('DOMContentLoaded', function() {
                const figures = document.querySelectorAll('.quarto-float-fig');
                
                figures.forEach(figure => {
                  // Get all subfigure captions within this figure
                  const subfigCaptions = figure.querySelectorAll('.quarto-subfloat-caption');
                    subfigCaptions.forEach((caption, index) => {
                      if (index >= 26) { // Only process captions after 'z'
                        const currentText = caption.textContent.trim();
                        const figLabel = currentText.match(/\) (.*)/)[1]; // Extract everything after ") "
                        
                        // Calculate the double letter (aa, bb, etc.)
                        const pos = index - 25; // 27th figure starts at pos 1
                        const letter = String.fromCharCode(96 + pos); // 97 is 'a' in ASCII
                        const newLabel = `(${letter}${letter}) ${figLabel}`;
                        
                        caption.textContent = newLabel;
                      }
                    });
                });
              });
            </script>
            
    
    ---
    
    
    ```{r}
    #| label: fig-charts
    #| layout-ncol: 2
    #| fig-cap: "Plots of all data for all extensometer sensors."
    #| fig-subcap: 
    #|   - "DM0001_1"
    #|   - "DM0001_2"
    #|   - "DM0001_3"
    #|   - "DM0001_4"
    #|   - "DM0001_5"
    #|   - "DM0002_3"
    #|   - "DM0002_4"
    #|   - "DM0003_1"
    #|   - "DM0003_2"
    #|   - "DM0003_3"
    #|   - "DM0003_4"
    #|   - "DM0003_5"
    #|   - "DM0004_1"
    #|   - "DM0004_2"
    #|   - "DM0004_3"
    #|   - "DM0004_4"
    #|   - "DM0004_5"
    #|   - "DM0005_1"
    #|   - "DM0005_2"
    #|   - "DM0005_3"
    #|   - "DM0005_4"
    #|   - "DM0005_5"
    #|   - "DM0006_1"
    #|   - "DM0007_1"
    #|   - "DM0008_1"
    #|   - "DM0009_1"
    #|   - "DM0010_1"
    #|   - "DM0010_2"
    #|   - "DM0010_3"
    
    for (i in 1:27) { 
        plot(i, i)
    }
    ```
    
    
    ```{r}
    #| label: fig-charts2
    #| layout-ncol: 2
    #| fig-cap: "Plots of all data for all extensometer sensors."
    #| fig-subcap: 
    #|   - "DM0001_1"
    #|   - "DM0001_2"
    #|   - "DM0001_3"
    #|   - "DM0001_4"
    #|   - "DM0001_5"
    #|   - "DM0002_3"
    #|   - "DM0002_4"
    #|   - "DM0003_1"
    #|   - "DM0003_2"
    #|   - "DM0003_3"
    #|   - "DM0003_4"
    #|   - "DM0003_5"
    #|   - "DM0004_1"
    #|   - "DM0004_2"
    #|   - "DM0004_3"
    #|   - "DM0004_4"
    #|   - "DM0004_5"
    #|   - "DM0005_1"
    #|   - "DM0005_2"
    #|   - "DM0005_3"
    #|   - "DM0005_4"
    #|   - "DM0005_5"
    #|   - "DM0006_1"
    #|   - "DM0007_1"
    #|   - "DM0008_1"
    #|   - "DM0009_1"
    #|   - "DM0010_1"
    #|   - "DM0010_2"
    #|   - "DM0010_3"
    
    for (i in 1:27) { 
        plot(i, i)
    }
    ```
    

    As you can see, that works for 27 plots and more. It resets for each new .quarto-float-fig class div. out