Update to unofficial Tabs feature: Full customizable buttons + Responsive

I added the var iterator = document.evaluate() method.

Another method to make your JS snippets work when they’re related to multiple Softr buttons

Demo and full code snippet here for this method: https://test-play.softr.app/tabs-for-softr-with-var-iterator-js-snippet

1 Like

Update: Fully responsive version

I chose to change the button container to a dropdown menu, only when viewed on mobiles.

Here is the demo (you will see the change on mobile. The desktop version doesn’t change, of course): https://test-play.softr.app/tabs-with-custom-buttons-responsive

Now what do you have to do to make it fully responsive? (Buttons to dropdown menu)

Enter this code in a custom code block (if you used the not responsive version, replace everything by what’s below)

<!DOCTYPE html>
<html>
<head>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');
        
         .button-group {
            display: flex;
            justify-content: left;
        }

        .button {
            background-color: #182939;
            color: white;
            font-family: 'Inter', sans-serif;
            font-size: 15px; 
            padding: 10px 20px; 
            margin-right: 30px;
            padding-top: 8px;
            padding-bottom: 8px;
            border-radius: 14px;
            border: none;
            cursor: pointer;
        }
        
        .button:hover {
        background-color: #f5e507;
        color: #182939;
       }
        
        .button:focus {
        outline: none;
       }

        .button:active, .active {
            background-color: #f5e507;
            color: #182939;
            outline: none;
        }

        @media (max-width: 480px) {
            .button-group {
                display: flex;
                flex-direction: column;
                align-items: flex-start;
            }

            .button {
                display: none;
            }

            .dropdown {
                display: block;
                width: 100%;
                margin-top: 10px;
                padding: 10px 20px; 
                border-radius: 14px;
                border: none;
                background-color: #182939;
                color: white;
                font-family: 'Inter', sans-serif;
                font-size: 15px;
                cursor: pointer;
            }
        }

        @media (min-width: 481px) {
            .button-group {
                display: flex;
                justify-content: left;
            }

            .dropdown {
                display: none;
            }
        }
    </style>
</head>
<body>
    <div class="button-group">
        <button class="button active" onclick="runButton1Script()">Button 1</button>
        <button class="button" onclick="runButton2Script()">Button 2</button>
        <button class="button" onclick="runButton3Script()">Button 3</button>
        <select class="dropdown" onchange="runDropdownScript(this.value)">
            <option value="button1">Button 1</option>
            <option value="button2">Button 2</option>
            <option value="button3">Button 3</option>
        </select>
    </div>
    <script>
        const buttons = document.querySelectorAll('.button');
        buttons.forEach(button => {
            button.addEventListener('click', function() {
                buttons.forEach(b => b.classList.remove('active'));
                this.classList.add('active');
            });
        });

        function runDropdownScript(value) {
            switch(value) {
                case 'button1':
                    runButton1Script();
                    break;
                case 'button2':
                    runButton2Script();
                    break;
                case 'button3':
                    runButton3Script();
                    break;
                default:
                    break;
            }
        }
    </script>
</body>
</html>

<script>
    // hide blocks 
    window.addEventListener('DOMContentLoaded', (event) => {
        document.getElementById("table2").style.display = "block";
        document.getElementById("form2").style.display = "none";
        document.getElementById("chart2").style.display = "none"
    });

    function runButton1Script() {
        document.getElementById("table2").style.display = "block";
        document.getElementById("form2").style.display = "none";
        document.getElementById("chart2").style.display = "none";
        document.getElementById("table2").scrollIntoView("table2");
        console.log("Button 1 clicked"); 
    }

         function runButton2Script() {
        document.getElementById("form2").style.display = "block";
        document.getElementById("table2").style.display = "none";
        document.getElementById("chart2").style.display = "none";
        document.getElementById("form2").scrollIntoView("form2");
        console.log("Button 2 clicked");
    }
          function runButton3Script() {
        document.getElementById("chart2").style.display = "block";
        document.getElementById("table2").style.display = "none";
        document.getElementById("form2").style.display = "none";
        document.getElementById("chart2").scrollIntoView("chart2");
        console.log("Button 3 clicked");
    }

</script>
</html>
5 Likes

Amazing work Matthiew, this is much needed as you can see from my picture!

I wanted to ask, is there a way for a tab to display multiple lists? So for example, tab1 displaying “list 1”, “list2” and “list3” for example?

Yes! you just need to play with the ‘none’ / ‘block’ lines of the code

2 Likes

I’m not sure if this is already a thread somewhere else but I was wondering actually if there was a description for how you configured this page: https://test-play.softr.app/hide-show-tabs?recordId=recZV04ZNE3pvMnKM

Specifically interested in how you have a list of projects at the top and can select various ones without leaving the page in order to view the associated record details? I feel like maybe it’s really obvious and I might be missing something basic but would LOVE to figure out how to recreate for a management dashboard I am configuring :slight_smile:

This is explained here: New - unofficial - feature: TABS - Hide/Show multiple blocks horizontally

Inside 2) “One page” apps enabled

Doh! I don’t know how I missed that… think I got so excited implementing the other goodies on that page LOL it’s SO pretty, though!


Thank you again!

Hi Matthieu – this is great! I’m a bit stuck on how to update the part though for the mobile responsive version. I had been implementing your different versions step by step to ensure it worked and it did (yay!). But when I went to do the responsive version, I don’t know how to link to the lists in the new script you provided (as it seems I had to replace all the code – unless I did that wrong). I’m a novice at code so it’s not intuitive to me.

Hi!

My bad, I was not clear enough for the code to insert for the responsive side. I updated it (the main script was missing)

1 Like

Thank you! Much appreciated.

Do the blocks need to be forms, charts, or lists, @matthieu_chateau ?
It seems to only work with those blocks and not when I add a CTA block, could that be?

Thanks so much for building this!! Such a game changer!!! :smiley:

Update: it seems like I simply can’t set up visibility settings within the blocks?

Hi Tim,

It works with any block + visibility settings and conditional filters apply normally. The code has 0 effect on visibility conditions.

The logic of your visibility conditions might need to be reflected in the custom code.
If you have different visibility settings for different blocks and you don’t reflect them in the custom code, it won’t work. But this is just visibility conditions applying normally, the code doesn’t change it itself.

To make it more simple: you can add one custom code block (related to the tab feature) for each of your visibility conditions. And you just hide/show the blocks that meet the visibility conditions for each of your blocks.

1 Like

Aah that’s amazing! Great to know! Thank you!!!
Have you seen anyone posting such a custom code in the community forum for your tabs feature?
I wouldn’t know where to start to write a custom code for visibility options. I still don’t really have coding skills :smiley:

You don’t need to write any code => everything should follow the normal Softr visibility conditions.

The only thing that might be needed is to duplicate the custom code block for the tabs feature each time you want to display different visibility conditions for a set of blocks (with the same visibility conditions they have)

1 Like

Aaah, that makes sense!!
It will be a bit messy to create that for three user groups, but at least it works then :smiley: Amazing! Thanks so much!!

Hey @matthieu_chateau :wave:
I’m struggling to combine this version with the code you shared to click one of the buttons via ?tab=chart within the URL. I thought I could just replace the JS code with the one you provided, but I seem to be missing something. Any tip how I could make this work? :slight_smile:
Would reeeeally appreciate it!!

Thanks so so much in advance!!

@matthieu_chateau thank you so much for writing and sharing this code, it was exactly what I was looking for!

I’m now trying to take it one step further, by creating a secondary row of buttons that shows up when one of the primary buttons is clicked and then those secondary buttons are showing again other blocks (related to the secondary buttons). So it’s kind of a nested row of buttons.

The screenshot is a schematic example of what I made: buttons A1, A2 and A3 are primary buttons, using the code that you wrote. Buttons A1 and A3 are pointing to regular blocks, while button A2 is pointing to a new code block which has your code but then for the secondary buttons B1, B2 and B3, which each are pointing to a secondary regular block. This means that when Button A2 is clicked, none of the primary A1 or A3 blocks are shown but instead the secondary buttons are shown. When clicking on the secondary buttons, those secondary blocks are shown.

This is working well, the only thing that I don’t know how to solve is that after button A2 is clicked and then one of the B-buttons is clicked and shown, clicking on A1 or A3 keeps visible the block of the last visible B button (below the correct A1 or A3 block which is correctly visible).

I hope that makes sense and I hope anyone here can point me in the right direction.

Hi,

Can you show the script you use?
Thanks

Yes. I don’t know anything about coding and only a little HTML and CSS, but I tried to follow the logic of your code and expand on that. Here is the code for the top buttons:

<!DOCTYPE html>
<html>
<head>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');
        
         .buttonx1-group {
            display: flex;
            justify-content: center;
        }

        .buttonx1 {
            background-color: #C2C2C2;
            color: white;
            font-family: 'Inter', sans-serif;
            font-size: 1rem; 
            padding: 10px 20px; 
            margin-right: 15px;
            padding-top: 8px;
            padding-bottom: 8px;
            border-radius: 0.5rem;
            border: none;
            cursor: pointer;
        }
        
        .buttonx1:hover {
        background-color: #FFA500;
        color: white;
       }
        
        .buttonx1:focus {
        outline: none;
       }

        .buttonx1:active, .active {
            background-color: #FFA500;
            color: white;
            outline: none;
        }

        @media (max-width: 480px) {
            .buttonx1-group {
                display: flex;
                flex-direction: column;
                align-items: flex-start;
            }

            .buttonx1 {
                display: none;
            }

            .dropdownx1 {
                display: block;
                width: 100%;
                margin-top: 10px;
                padding: 10px 20px; 
                border-radius: 0.5rem;
                border: none;
                background-color: #C2C2C2;
                color: white;
                font-family: 'Inter', sans-serif;
                font-size: 1rem;
                cursor: pointer;
            }
        }

        @media (min-width: 481px) {
            .buttonx1-group {
                display: flex;
                justify-content: center;
            }

            .dropdownx1 {
                display: none;
            }
        }
    </style>
</head>
<body>
    <div class="buttonx1-group">
        <buttonx1 class="buttonx1 active" onclick="runButtonX11Script()">Details</buttonx1>
        <buttonx1 class="buttonx1" onclick="runButtonX12Script()">Preparation</buttonx1>
        <buttonx1 class="buttonx1" onclick="runButtonX13Script()">Resources</buttonx1>
        <buttonx1 class="buttonx1" onclick="runButtonX14Script()">Tags</buttonx1>
        <select class="dropdownx1" onchange="runDropdownX1Script(this.value)">
            <option value="buttonx11">Details</option>
            <option value="buttonx12">Preparation</option>
            <option value="buttonx13">Resources</option>
            <option value="buttonx14">Tags</option>
        </select>
    </div>
    <script>
        const buttonx1s = document.querySelectorAll('.buttonx1');
        buttonx1s.forEach(buttonx1 => {
            buttonx1.addEventListener('click', function() {
                buttonx1s.forEach(b => b.classList.remove('active'));
                this.classList.add('active');
            });
        });

        function runDropdownX1Script(value) {
            switch(value) {
                case 'buttonx11':
                    runButtonX11Script();
                    break;
                case 'buttonx12':
                    runButtonX12Script();
                    break;
                case 'buttonx13':
                    runButtonX13Script();
                    break;
                case 'buttonx14':
                    runButtonX14Script();
                    break;
                default:
                    break;
            }
        }
    </script>
</body>
</html>

<script>
    // hide blocks 
    window.addEventListener('DOMContentLoaded', (event) => {
        document.getElementById("details").style.display = "block";
        document.getElementById("preparation").style.display = "none";
        document.getElementById("menu2").style.display = "none";
        document.getElementById("linked-tags").style.display = "none"
    });

    function runButtonX11Script() {
        document.getElementById("details").style.display = "block";
        document.getElementById("preparation").style.display = "none";
        document.getElementById("menu2").style.display = "none";
        document.getElementById("linked-tags").style.display = "none";
        console.log("Details clicked"); 
    }

         function runButtonX12Script() {
        document.getElementById("preparation").style.display = "block";
        document.getElementById("details").style.display = "none";
        document.getElementById("menu2").style.display = "none";
        document.getElementById("linked-tags").style.display = "none";
        console.log("Preparation clicked");
    }
          function runButtonX13Script() {
        document.getElementById("menu2").style.display = "block";
        document.getElementById("details").style.display = "none";
        document.getElementById("preparation").style.display = "none";
        document.getElementById("linked-tags").style.display = "none";
        console.log("menu2 clicked");
    }
          function runButtonX14Script() {
        document.getElementById("linked-tags").style.display = "block";
        document.getElementById("details").style.display = "none";
        document.getElementById("preparation").style.display = "none";
        document.getElementById("menu2").style.display = "none";
        console.log("linked-tags clicked");
    }

</script>
</html>

And this is the code for the code block called “menu2”:

<!DOCTYPE html>
<html>
<head>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');
        
         .buttonx2-group {
            display: flex;
            justify-content: center;
        }

        .buttonx2 {
            background-color: white;
            color: grey;
            font-family: 'Inter', sans-serif;
            font-size: 0.6rem; 
            padding: 10px 20px; 
            margin-right: 15px;
            padding-top: 8px;
            padding-bottom: 8px;
            border-radius: 0.5rem;
            border: 1px solid grey;
            cursor: pointer;
        }
        
        .buttonx2:hover {
        background-color: grey;
        color: white;
       }
        
        .buttonx2:focus {
        outline: none;
       }

        .buttonx2:active {
            background-color: grey !important;
            color: white;
            outline: none;
        }

        @media (max-width: 480px) {
            .buttonx2-group {
                display: flex;
                flex-direction: column;
                align-items: flex-start;
            }

            .buttonx2 {
                display: none;
            }

            .dropdownx2 {
                display: block;
                width: 100%;
                margin-top: 10px;
                padding: 10px 20px; 
                border-radius: 0.5rem;
                border: none;
                background-color: #C2C2C2;
                color: white;
                font-family: 'Inter', sans-serif;
                font-size: 1rem;
                cursor: pointer;
            }
        }

        @media (min-width: 481px) {
            .buttonx2-group {
                display: flex;
                justify-content: center;
            }

            .dropdownx2 {
                display: none;
            }
        }
    </style>
</head>
<body>
    <div class="buttonx2-group">
        <buttonx2 class="buttonx2 active" onclick="runButtonX21Script()">Linked Resources</buttonx2>
        <buttonx2 class="buttonx2" onclick="runButtonX22Script()">Associated Resources</buttonx2>
        <buttonx2 class="buttonx2" onclick="runButtonX23Script()">Resource Library</buttonx2>
        <select class="dropdownx2" onchange="runDropdownX2Script(this.value)">
            <option value="buttonx21">Linked Resources</option>
            <option value="buttonx22">Associated Resources</option>
            <option value="buttonx23">Resource Library</option>
        </select>
    </div>
    <script>
        const buttonx2s = document.querySelectorAll('.buttonx2');
        buttonx2s.forEach(buttonx2 => {
            buttonx2.addEventListener('click', function() {
                buttonx2s.forEach(b => b.classList.remove('active'));
                this.classList.add('active');
            });
        });

        function runDropdownX2Script(value) {
            switch(value) {
                case 'buttonx21':
                    runButtonX21Script();
                    break;
                case 'buttonx22':
                    runButtonX22Script();
                    break;
                case 'buttonx23':
                    runButtonX23Script();
                    break;
                default:
                    break;
            }
        }
    </script>
</body>
</html>

<script>
    // hide blocks 
    window.addEventListener('DOMContentLoaded', (event) => {
        document.getElementById("linked-resources").style.display = "none";
        document.getElementById("associated-resources").style.display = "none";
        document.getElementById("resource-library").style.display = "none"
    });

    function runButtonX21Script() {
        document.getElementById("linked-resources").style.display = "block";
        document.getElementById("associated-resources").style.display = "none";
        document.getElementById("resource-library").style.display = "none";
        console.log("Linked Resources clicked"); 
    }

         function runButtonX22Script() {
        document.getElementById("associated-resources").style.display = "block";
        document.getElementById("linked-resources").style.display = "none";
        document.getElementById("resource-library").style.display = "none";
        console.log("Associated Resources clicked");
    }
          function runButtonX23Script() {
        document.getElementById("resource-library").style.display = "block";
        document.getElementById("linked-resources").style.display = "none";
        document.getElementById("associated-resources").style.display = "none";
        console.log("Resource Library clicked");
    }

</script>
</html>

Thank you!

There could be a better way to write the whole code (everything in one script), but it will get the job done => Replace the last script of your first custom code block (top buttons) by:

<script>
    // hide blocks 
    window.addEventListener('DOMContentLoaded', (event) => {
        document.getElementById("details").style.display = "block";
        document.getElementById("preparation").style.display = "none";
        document.getElementById("menu2").style.display = "none";
        document.getElementById("linked-tags").style.display = "none";
        document.getElementById("linked-resources").style.display = "none";
        document.getElementById("associated-resources").style.display = "none";
        document.getElementById("resource-library").style.display = "none"
    });

    function runButtonX11Script() {
        document.getElementById("details").style.display = "block";
        document.getElementById("preparation").style.display = "none";
        document.getElementById("menu2").style.display = "none";
        document.getElementById("linked-tags").style.display = "none";
        document.getElementById("linked-resources").style.display = "none";
        document.getElementById("associated-resources").style.display = "none";
        document.getElementById("resource-library").style.display = "none";
        console.log("Details clicked"); 
    }

         function runButtonX12Script() {
        document.getElementById("preparation").style.display = "block";
        document.getElementById("details").style.display = "none";
        document.getElementById("menu2").style.display = "none";
        document.getElementById("linked-tags").style.display = "none";
        document.getElementById("linked-resources").style.display = "none";
        document.getElementById("associated-resources").style.display = "none";
        document.getElementById("resource-library").style.display = "none";
        console.log("Preparation clicked");
    }
          function runButtonX13Script() {
        document.getElementById("menu2").style.display = "block";
        document.getElementById("details").style.display = "none";
        document.getElementById("preparation").style.display = "none";
        document.getElementById("linked-tags").style.display = "none";
        document.getElementById("linked-resources").style.display = "none";
        document.getElementById("associated-resources").style.display = "none";
        document.getElementById("resource-library").style.display = "none";
        console.log("menu2 clicked");
    }
          function runButtonX14Script() {
        document.getElementById("linked-tags").style.display = "block";
        document.getElementById("details").style.display = "none";
        document.getElementById("preparation").style.display = "none";
        document.getElementById("menu2").style.display = "none";
        document.getElementById("linked-resources").style.display = "none";
        document.getElementById("associated-resources").style.display = "none";
        document.getElementById("resource-library").style.display = "none";
        console.log("linked-tags clicked");
    }

</script>