Hi there, @user9181 ![]()
Thank you for waiting!
Here is a solution from our devs:
const waitForElement = (selector, root = document) =>
new Promise((resolve) => {
const element = root.querySelector(selector);
if (element) return resolve(element);
const observer = new MutationObserver(() => {
const el = root.querySelector(selector);
if (el) {
resolve(el);
observer.disconnect();
}
});
observer.observe(root, { childList: true, subtree: true });
});
const filters = {};
let tempFilters = {};
const params = new URLSearchParams(window.location.search);
for (const [key, value] of params.entries()) {
filters[key] = value.split('; ').filter(Boolean);
tempFilters[key] = value.split('; ').filter(Boolean);
}
const getFilterType = (type) => {
switch (type) {
case 'tag':
return 'tags';
case 'location':
return 'venue';
default:
return type;
}
};
const toggleValue = (store, type, value) => {
if (!store[type]) store[type] = [];
const idx = store[type].indexOf(value);
if (idx === -1) store[type].push(value);
else store[type].splice(idx, 1);
};
const updateUrlFromFilters = () => {
const params = new URLSearchParams();
for (const [key, values] of Object.entries(filters)) {
if (values.length) params.set(key, values.join('; '));
}
history.replaceState(
null,
'',
`${window.location.pathname}?${params.toString()}`
);
};
waitForElement(
'#eapps-events-calendar-5cd9b582-3f6e-498a-84c2-c6f17e9a1da1'
).then((container) => {
const filterButtons = container.querySelectorAll(
'.es-filters-item-container'
);
filterButtons.forEach((filterButton) => {
filterButton.addEventListener('click', () => {
const type = getFilterType(filterButton.getAttribute('data-type'));
waitForElement('div[data-radix-popper-content-wrapper]').then(
(popover) => {
popover.dataset.currentType = type;
if (!tempFilters) {
tempFilters = JSON.parse(JSON.stringify(filters));
}
if (popover.dataset.listenerAttached) return;
const optionsContainer = popover.querySelector(
'[class*="expandable__Container-sc"]'
);
if (!optionsContainer) return;
optionsContainer.addEventListener('click', (e) => {
if (e.detail === 0) return;
const clickedButton = e.target.closest('.es-filter-button-button');
const clickedCheckboxItem = e.target.closest(
'[class*="checklist-item__ItemContainer-sc"]'
);
const target = clickedButton ?? clickedCheckboxItem;
if (!target) return;
const textEl =
target.querySelector('.es-filter-button-text') ||
target.querySelector('[class*="checklist-item__Label-sc"]');
const currentType = popover.dataset.currentType;
const value = textEl?.textContent?.trim();
if (!value || !currentType) return;
toggleValue(tempFilters, currentType, value);
});
// --- Apply ---
const applyButton = popover.querySelector(
'.es-apply-filters-button-button'
);
if (applyButton && !applyButton.dataset.listenerAttached) {
applyButton.addEventListener('click', () => {
if (!tempFilters) return;
Object.assign(filters, tempFilters);
updateUrlFromFilters();
tempFilters = null;
});
applyButton.dataset.listenerAttached = '1';
}
// --- Clear ---
waitForElement(
'div[data-radix-popper-content-wrapper] .es-clear-filters-button-button'
).then((clearButton) => {
if (clearButton.dataset.listenerAttached) return;
clearButton.addEventListener('click', () => {
const currentType = popover.dataset.currentType;
if (!currentType) return;
if (tempFilters && tempFilters[currentType]) {
tempFilters[currentType] = [];
}
});
clearButton.dataset.listenerAttached = '1';
});
popover.dataset.listenerAttached = '1';
}
);
});
const buttonEl = filterButton.querySelector(
'button[aria-haspopup="dialog"][type="button"]'
);
if (!buttonEl) return;
const observer = new MutationObserver((mutations) => {
mutations.forEach((m) => {
if (m.type === 'attributes' && m.attributeName === 'data-state') {
const state = buttonEl.dataset.state;
if (state === 'closed') {
const popover = document.querySelector(
'div[data-radix-popper-content-wrapper]'
);
const currentType =
popover?.dataset.currentType ||
getFilterType(filterButton.getAttribute('data-type'));
if (!currentType) return;
if (tempFilters) {
tempFilters[currentType] = filters[currentType] ?? [];
}
}
}
});
});
observer.observe(buttonEl, {
attributes: true,
attributeFilter: ['data-state'],
});
});
const wrapper = container.querySelector('.es-filters-wrapper');
if (wrapper) {
const attachGlobalClearListener = (button) => {
if (button && !button.dataset.globalListenerAttached) {
button.addEventListener('click', () => {
if (!filters) filters = {};
if (!tempFilters) tempFilters = {};
Object.keys(filters).forEach((key) => (filters[key] = []));
Object.keys(tempFilters).forEach((key) => (tempFilters[key] = []));
updateUrlFromFilters();
});
button.dataset.globalListenerAttached = '1';
}
};
const existingButton = wrapper.querySelector(
':scope > .es-clear-filters-button-button'
);
attachGlobalClearListener(existingButton);
const globalObserver = new MutationObserver((mutations) => {
mutations.forEach((m) => {
if (m.type === 'childList') {
const clearAllButton = wrapper.querySelector(
':scope > .es-clear-filters-button-button'
);
attachGlobalClearListener(clearAllButton);
}
});
});
globalObserver.observe(wrapper, { childList: true, subtree: true });
}
});
Please add this code to the Custom JS field on the Settings tab of your widget’s settings and let me know if it worked ![]()
I also agree that it would be great to have links to specific categories by default and added this idea to the Wishlist on your behalf - Add query params to website URL when changing filters (get links to events filtered by specific criteria)