Hide or show blocks based on a record's attributes

When using list details blocks you may want to have the layout of your page vary based on some attribute from the current record, instead of the user.
For example, you may want to hide the booking form on an event’s page when a parameter called “event_status” is equal to “full” for that specific record. You can add the code below to your page’s (footer) custom code to do just that.

<script>
//Hide booking form if status is full
    
    document.addEventListener("DOMContentLoaded", function() {    
        var waitForData = setInterval(function () {
            if (typeof $ != 'undefined') {
                if (document.querySelectorAll('div[data-mappedto="event_status"][data-value="full" i]').length > 0) {
                    
                    var bookingForm = document.getElementById('form');

                    bookingForm.style.display = 'none';
                    
                    clearInterval(waitForData);
                }
            }
        }, 100);
    });
</script>

Parameters to edit :
event_status → Airtable name of the field where you condition resides.
full → value of the above field that triggers the hiding of the block
form → Softr id of the block you want to hide when the condition is met.

Note : some component bearing the content of your trigger field (here event_status) must be displayed on the page for that piece of code to work. If need be, you can display it in a button and toggle its visibility to hidden or collapse.

9 Likes

Dear Julien, thank you very much for sharing the custom code with the Community!

2 Likes

Excellent ! I was going to release a video on how to do it, without code, but the user experience was ridiculous :sweat_smile: (will change when action buttons will be released)
Thanks a lot, this is an important feature in my opinion

3 Likes

Thank you for this piece of code, is very very helpful in so many use cases.

2 Likes

Amazing! Is it possible to hide an an action button based on a row’s field value as well @Julien_Berawen ? Thanks!

Hi @rebeccajane , glad it can help :slight_smile:

You can apply it to any item that has a CSS id, so there should be no problem with an action button. It would probably look something like this :

                    var myActionButton = document.getElementById('action-button');

                    myActionButton.style.display = 'none';

where action-button is the id of the button you want to hide.

2 Likes

Hi @Julien_Berawen v many thanks for this. Would it be possible to use the same code to ‘unhide’ a block depending on a field value? That would be hugely helpful for us as we want to display a specific header from a set of hidden headers depending on field values rather than on user groups (eg to display the correct logo for the ‘owner’ of a specific record). It’s possible that the answer is in the code e.g. poss mod of ‘bookingForm.style.display = ‘none’;’ but I’m not that technical! V mny tks in advance if able to assist…

Hi @swshrey, happy new year and sorry for the time it took me to answer.

I think what you’re trying to do is definitely possible, you could use custom code in the header of your page to hide the blocks you want with a querySelector, and then use another snippet in the footer to toggle the visibility of the specific one you want to display back on.

For that last part, I would advise to have a look at Artur’s solution here, since mine above has been impacted by some changes in the meantime and might not be fully functional anymore (especially the “data-mappedto” attribute used in the selector).

Let me know if you manage to work your way around that, if not happy to help !

@Julien_Berawen no probs at all and thanks v much for the reference. I think Artur’s solution MIGHT resolve a lot of user management issues that we currently have - will msg him and cc you - and if I need any help will get back to you, with huge thanks in advance in any event! I very much doubt if I can ever return the favour, unless you ever have to deal with data models relating to artworks, in which case I’m your man :slight_smile: :slight_smile: - if my tech colleagues ever need a softr coding resource, are you available on an hourly basis? If so, do let me know (didn’t see your name in the ‘Softr expert’ list…

1 Like

hey folks,

as there was a question on hiding and un-hiding it goes like this typically

Sure you can use

myElement.style.display = 'none'; // to hide

myElement.style.display = 'block'; // to unhide

Please keep in mind this is not a security measure if the data needs to be not shown at all user visibility rules or conditional filters.

1 Like

Mny tks @artur ! V useful - and noted re visibility/security.

Hello, all, I’m not a coder but this seems not to be working for me when I’m using a checkbox field and seeking a value of “true” so I’m assuming I’m missing something easy. If I just comment out the ‘if’ statement, it works.

The “markfinalbutton” is an action button in a form with hidden fields. The “marked_final” checkbox is displayed in a list details element.

document.addEventListener("DOMContentLoaded", function() {    
    var waitForData = setInterval(function () {
        if (typeof $ != 'undefined') {
            if (document.querySelectorAll('div[data-mappedto="marked_final"][data-value="true" i]').length > 0) {
                
                var myForm = document.getElementById('markfinalbutton');

                myForm.style.display = 'none';
                
                clearInterval(waitForData);
            }
        }
    }, 100);
});

@mb12142329 I think the data_mappedto attribute cannot be used anymore after some changes in the code structure. You’ll probably be better off using that solution.

Thank you but it’s still not working unfortunately. Here’s my version. Does it matter that it’s a linked field in Airtable? Should I be using a value other than “true”?

document.addEventListener(“DOMContentLoaded”, function() {
var waitForData = setInterval(function () {
if (typeof $ != ‘undefined’) {
const recordId = getUrlParam(‘recordId’);
if (window.records && window.records[recordId] && >
window.records[recordId].record.fields[‘markedfinal’] === ‘true’) {
var form1 = document.getElementById(‘markfinalbutton’);
form1.style.display = ‘none’;

                clearInterval(waitForData);
            }
        }
    }, 100);

    function getUrlParam(name) {
        const url = new URL(window.location.href);
        let param;
        for(var key of url.searchParams.keys()) {
            if(key.toLowerCase() === name.toLowerCase()) {
            param = url.searchParams.get(name);
            break;
            }
        }
    
        if(!param && name.toLowerCase() === 'recordid') {
            param = getRecordIdFromPath();
        }
        return param;
    }
    
    function getRecordIdFromPath() {
        let pathName = window.location.pathname;
        if (pathName.indexOf('/r/rec') !== -1) {
            pathName = pathName.substr(pathName.indexOf('/r/rec') + 3);
            if (pathName.indexOf("/") !== -1) {
            pathName = pathName(0, pathName.indexOf('/'))
            }
            return pathName;
        }
        return undefined;
    }
});

@mb12142329 Is there any error or warning in the browser console ? If so can you paste them here ?

  • policy-edits:9 The key “target-densitydpi” is not supported.
  • micromodal.min.js:1 MicroModal: :exclamation:Please specify at least one ‘micromodal-trigger’ data attribute.
  • (anonymous) @ micromodal.min.js:1
  • micromodal.min.js:1 Example:
  • (anonymous) @ micromodal.min.js:1
  • policy-edits:1 Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
  • DevTools failed to load source map: Could not load content for https://assets.softr-files.com/libs/iframe-resizer/4.2.11/iframeResizer.contentWindow.map: HTTP error: status code 403, net::ERR_HTTP_RESPONSE_CODE_FAILURE

Can you try with this slightly different version ?

document.addEventListener('DOMContentLoaded', function() {
  var waitForData = setInterval(function () {
    if (typeof $ != 'undefined') {
      const recordId = getUrlParam('recordId');
      if (window.records && window.records[recordId] && window.records[recordId].record && window.records[recordId].record.fields && window.records[recordId].record.fields['markedfinal'] === 'true') {
        var form1 = document.getElementById('markfinalbutton');
        form1.style.display = 'none';
        clearInterval(waitForData);
        }
      }
    }, 100);

    function getUrlParam(name) {
        const url = new URL(window.location.href);
        let param;
        for(var key of url.searchParams.keys()) {
            if(key.toLowerCase() === name.toLowerCase()) {
            param = url.searchParams.get(name);
            break;
            }
        }
    
        if(!param && name.toLowerCase() === 'recordid') {
            param = getRecordIdFromPath();
        }
        return param;
    }
    
    function getRecordIdFromPath() {
        let pathName = window.location.pathname;
        if (pathName.indexOf('/r/rec') !== -1) {
            pathName = pathName.substr(pathName.indexOf('/r/rec') + 3);
            if (pathName.indexOf("/") !== -1) {
            pathName = pathName(0, pathName.indexOf('/'))
            }
            return pathName;
        }
        return undefined;
    }
});

Still the same unfortunately. To be clear, I’m putting this code in the footer. I’m not sure what else to try unless I’m just messing something up that’s very basic.

Yes, the footer should be the right place. Last thing I can think of : can you try individually testing each of the conditions using the browser console and let us know what comes out of each test ?
That would be successively :

  • window.records
  • window.records[recordId]
  • window.records[recordId].record
  • window.records[recordId].record.fields
  • window.records[recordId].record.fields['markedfinal']

If that doesn’t help, I’ll let someone more talented or more awake than I am try and figure it out. :slight_smile:

1 Like

Thanks, going to the console proved helpful because I realized (to my question earlier about checkboxes) that the value should be “checked” not “true”. It looks like there were two issues though because changing the value only worked when I simplified the IF statement and hardcoded the recordId. The other two functions seem unable to get the recordID for me. If I then go with the commented line below, it does not work. If I then put “window.records[recordId]” into the console, I get “Uncaught ReferenceError: recordId is not defined at :1:16”

document.addEventListener(‘DOMContentLoaded’, function() {
var waitForData = setInterval(function () {
if (typeof $ != ‘undefined’) {
const recordId = window.records[‘reclEC7KDP8Jq67PM’].record.fields.MarkedFinal;
// const myrecordId = getUrlParam(‘recordId’);
if (recordId === ‘checked’) {
var form1 = document.getElementById(‘markfinalbutton’);
form1.style.display = ‘none’;
clearInterval(waitForData);
}
}
}, 100);

function getUrlParam(name) {
const url = new URL(window.location.href);
let param;
for(var key of url.searchParams.keys()) {
if(key.toLowerCase() === name.toLowerCase()) {
param = url.searchParams.get(name);
break;
}
}

        if(!param && name.toLowerCase() === 'recordid') {
            param = getRecordIdFromPath();
        }
        return param;
    }
    
    function getRecordIdFromPath() {
        let pathName = window.location.pathname;
        if (pathName.indexOf('/r/rec') !== -1) {
            pathName = pathName.substr(pathName.indexOf('/r/rec') + 3);
            if (pathName.indexOf("/") !== -1) {
            pathName = pathName(0, pathName.indexOf('/'))
            }
            return pathName;
        }
        return undefined;
    }
});