This is actually pretty straightforward. There are a couple different ways to go about it too: Two separate repeating sections and you populate the filtered section via sheetworker as the user changes the name to filter by Filtered section is a repeat of the non filtered section (i.e. same repeating section name) and you use css to control the display Both methods require some JS to do. The performance of each is probably close to identical, but I'm going to demonstrate the second option (reuse the section name) as I think it will provide a slightly better user experience and we won't need to create JS to keep the values of the filtered and unfiltered views in sync. You'll need a couple pieces for this method: The HTML for each section CSS to control the display in the filtered version of the section Javascript to control some attributes HTML I'm going to provide some very basic html. I'll note the sections that are essential for this method and you can add to it as you need to for your use case. <h2>Source Section</h2>
<fieldset class="repeating_equipment">
<!-- the name of the item. Currently this is all we are filtering for and the only data that the user sees in the section. You can add additional data for the section as you see fit -->
<input type="text" name="attr_name">
</fieldset>
<h2>Filtered Section</h2>
<!-- an input for the user to enter what to filter by (the equipment name in this case) -->
<label>
<span>name to filtere for:</span>
<input type="text" name="attr_equipment_filter">
</label>
<!-- a wrapper class to let us target when our filtering css applies -->
<div class="filtered-section">
<fieldset class="repeating_equipment">
<!-- the name of the item. Currently this is all we are filtering for and the only data that the user sees in the section. You can add additional data for the section as you see fit -->
<input type="text" name="attr_name">
<!-- this input will control whether the repeating item is displayed or not. This is the piece that actually makes this technique function. -->
<input type="checkbox" class="filter-control" hidden name="attr_filtered" value="1">
</fieldset>
</div> CSS The CSS for this is just one declaration. Note that I have prepended the css with .ui-dialog .tab-content .charseet . This is because this is a standard practice of mine to ensure that my css overrides the R20 default CSS and that all of my css operates from the same base specificity level. /*
Checks each repeating item to see if the filter control
checkbox contained in it is checked or not. Then sets the
ones that don't contain a checked on to display none.
*/
.ui-dialog .tab-content .charsheet .filtered-section .repitem:has(.filter-control:not(:checked)){
display: none !important;
} Note that :has is a relatively new CSS selector. Browser support for it is about 92% globally , but for Roll20 purposes it is much higher as all browsers that Roll20 actively supports have :has implementation. Javascript The javascript is probably the most complex part of this. it's also where you could do the most. This JS does a very simple text filter on the name, but you can do whatever logic you want. //List of attributes contained in our repeating section that we want to filter by, or that control the filtering. Make sure to update this with the actual attributes that you want to filter by.
const equipmentFilterAttributes = ['name','filtered'];
//We setup a listener to look for changes in our filter input and the names of the equipment so that if the user later adds an item that matches the filter, it will be added to our filtered view
on('change:equipment_filter change:repeating_equipment:name',(event) => {
getSectionIDs('repeating_equipment',(idArray) => {
//Assemble our array of attributes to get for the filter operation. This will include all the filterable attributes from the repeating section.
const getArr = idArray.reduce((m,id) => {
m.push(...equipmentFilterAttributes.map(n => `repeating_equipment_${id}_${n}`))
return m;
},['equipment_filter']);
getAttrs(getArr,(attributes) => {
//Empty object to accumulate our changes in so that we can use just a single setAttrs
const setObj = {}
//Iterate over each repeating row and check if the name contains the name to search for.
//Your actual filter logic can be anything you want, just make sure to set the row's filtered attribute to 1 if it meets the criteria and 0 if it doens't.
idArray.forEach(id => {
//create the row prepend so we don't have to remake it for all operations
const row = `repeating_equipment_${id}`;
let filtered = 0;
const rowName = attributes[`${row}_name`].toLowerCase();
const filterName = attributes.equipment_filter.toLowerCase();
if(
filterName &&
rowName.includes(filterName)
){
setObj[`${row}_filtered`] = 1;
}else{
setObj[`${row}_filtered`] = 0;
}
});
setAttrs(setObj,{silent:true});
})
})
}) And putting it all together gives us behavior like this: