Make "Active Filters" accessible from Javascript

Hi there, @user9181 :waving_hand:

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 :slightly_smiling_face:

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)