Skip to main content

Adding Links to “Store Locator Tags” for RP Promap

A simple way to setup deep links (or marketing links) to pre-filter store results by tag.

Updated over 3 weeks ago

Overview: Add a small client-side script that reads a tags query param (e.g. ?tags=Outlets,Showroom) and checks the matching input[type="checkbox"][name="tagsvalue"] filters in your store-locator form, triggers the search, shows a persistent pill UI, and resiliently re-applies tags if other scripts briefly uncheck them — while respecting real user interaction.


When to use

Use this on Shopify themes that include a store-locator form which exposes tag filters as checkboxes such as:

<input type="checkbox" name="tagsvalue" value="Shoes">

It’s useful when you want deep links (or marketing links) to pre-filter store results by tag.


Features (what the script does)

  • Parses tags (also tag) from window.location.search or location.hash.

  • Matches tag names to checkboxes by:

    • exact value

    • label text, nearest <li> or container text

    • tolerant fuzzy rules (case-insensitive, plural/singular, punctuation-insensitive, substring)

  • Aggressively tries to toggle the checkbox in ways frameworks accept:

    • clicks label, clicks checkbox, sets .checked, dispatches input/change/blur events

  • Retries and watches for late-injected markup (MutationObserver + interval).

  • After success, triggers the form Search button (or form.submit()).

  • Shows a persistent, dismissable pill summarizing applied and unmatched tags.

  • Monitors and re-applies if other scripts uncheck the boxes — but:

    • respects a user’s manual uncheck (won’t fight user),

    • limits reapply attempts per tag to avoid infinite loops,

    • stops automatically after a safe timeout.

  • Exposes window.__store_locator_apply_tags() to re-run from the console.


Prerequisites & notes

  • Place the script after your store-locator form markup (best: include it just before </body> in theme.liquid, or add as an asset and include it in the layout).

  • The script assumes:

    • your form has id="bh-sl-user-location" (default); change FORM_ID if different

    • your tag checkboxes are input[type="checkbox"][name="tagsvalue"] (default)

    • the Search button is id="bh-sl-submit" (default)

  • If your theme uses a different structure or custom checkbox UI (pseudo-elements, hidden inputs or external components), you may need to adjust the selectors or add a small adapter to click the visible UI element.


Installation (Shopify Admin)

  1. Login to Shopify Admin > Online Store > Themes.

  2. On your active theme, click Actions > Edit code.

  3. Under Assets click Add a new asset → choose Create a blank file → name it store-locator-checkbox-adapter.js.

  4. Paste the script (see below) into that asset and Save.

  5. Open layout/theme.liquid (or your storefront layout) and before </body> add:

    <script src="{{ 'store-locator-checkbox-adapter.js' | asset_url }}" defer></script>
    • defer is okay; if you prefer to ensure it runs after everything, omit defer and include it as the last script before </body>.

  6. Save theme changes.

  7. Visit your store-locator page and test.


Configuration (quick)

Open the JS asset and adjust these constants near the top if needed:

const FORM_ID = 'bh-sl-user-location'; 

const CHECKBOX_SELECTOR = 'input[type="checkbox"][name="tagsvalue"]';

const SEARCH_BUTTON_ID = 'bh-sl-submit';

const OBSERVER_TIMEOUT = 15000; // initial retry window (ms)

const RETRY_INTERVAL = 350; // retry interval (ms)

const MONITOR_DURATION = 20000; // how long to monitor after success

const MAX_REAPPLY_PER_TAG = 5; // avoid infinite reapply

If your selectors differ, update FORM_ID, CHECKBOX_SELECTOR, and SEARCH_BUTTON_ID.


Usage / Testing

  1. Open a store-locator page with tags in the URL. Examples:

    • https://yourstore.com/pages/store-locator?tags=Outlets

    • https://yourstore.com/pages/store-locator?tags=Outlets,Showroom

    • https://yourstore.com/pages/store-locator#/?tags=Outlets

  2. Watch the console. The adapter logs attempts and results:

    • [sl-adapter] attempt 1/xx

    • [sl-adapter] tags applied: …

    • Reapply messages if other scripts uncheck the box.

  3. Verify that:

    • The checkbox gets checked,

    • The Search button is clicked and results update,

    • The persistent pill appears at top-right with applied/unmatched tags.

  4. You can manually re-run from the console:

    __store_locator_apply_tags();
  5. If a user manually unchecks a box, the adapter stops reapplying for that tag.


Troubleshooting & tips

1. Check script load order
If the script runs before the store-locator is inserted, it will retry and observe for up to OBSERVER_TIMEOUT. Make sure the script is included late (before </body>).

2. Checkbox never gets checked

  • Some themes replace checkboxes with custom components. Inspect the checkbox DOM (right-click → Inspect → Copy → OuterHTML) to see whether the visible control is a sibling or a pseudo-element. If so, you may need to click the visible wrapper (e.g. .custom-checkbox) instead — update checkCheckbox() to click that element.

3. External scripts undoing changes

  • Third-party plugins (Google Places or a store-locator plugin) sometimes run later and reset filters. The adapter monitors and re-applies, but if that plugin errors (see Cannot read properties of null), fix the plugin or increase MONITOR_DURATION / MAX_REAPPLY_PER_TAG. You can also add a small setTimeout before triggering the search if needed.

4. Infinite loops

  • v4 includes caps (MAX_REAPPLY_PER_TAG) and other guards. If you still see repeats, reduce MONITOR_DURATION or lower MAX_REAPPLY_PER_TAG. Also review console logs for the reason.

5. Accessibility

  • The adapter dispatches input and change events. It also sets aria-checked for parity. It respects user actions (if event.isTrusted === true).

6. Persisting pill dismissal

  • If you want the pill dismissal to persist across pages, add localStorage flag on dismiss and check it at startup in showPill().


Full script (drop-in)

Place the following code in store-locator-checkbox-adapter.js and include the asset in your theme as described above.

<script>
/*!
* store-locator-checkbox-adapter-v4.js
* Purpose: Aggressively check <input type="checkbox" name="tagsvalue" value="..."> checkboxes,
* then monitor and re-apply them if other scripts uncheck them (but respect real user unchecks).
* This version includes robust guards to prevent infinite loops.
* Author: Rose Perl Technology
*/

(function () {
'use strict';

// Config
const FORM_ID = 'bh-sl-user-location';
const CHECKBOX_SELECTOR = 'input[type="checkbox"][name="tagsvalue"]';
const SEARCH_BUTTON_ID = 'bh-sl-submit';
const OBSERVER_TIMEOUT = 15000; // initial retry window (ms)
const RETRY_INTERVAL = 350; // retry interval (ms)
const MONITOR_DURATION = 20000; // monitor duration after success (ms)
const MAX_REAPPLY_PER_TAG = 5; // maximum reapply attempts per tag to avoid infinite loops
const ADAPTER_MARK = 'data-sl-adapter'; // marker attribute to identify adapter-applied boxes
const PILL_ID = 'store-locator-tags-pill-v4';

// --- Utilities ---
function parseTagsFromURL() {
const params = new URLSearchParams(window.location.search || '');
let raw = params.get('tags') || params.get('tag') || null;
if (!raw && window.location.hash) {
const h = window.location.hash;
const qIdx = h.indexOf('?');
if (qIdx !== -1) {
const hashQS = h.slice(qIdx + 1);
try {
const hp = new URLSearchParams(hashQS);
raw = raw || hp.get('tags') || hp.get('tag');
} catch (e) {}
}
}
if (!raw) return [];
raw = raw.replace(/\+/g, ' ');
try { raw = decodeURIComponent(raw); } catch (e) {}
return raw.split(/\s*[;,|]\s*/).map(t => t.trim()).filter(Boolean);
}

function normalize(s) {
return String(s || '').toLowerCase().replace(/\s+/g, ' ').trim();
}

function tagsMatch(normalBox, normalTag) {
if (!normalBox || !normalTag) return false;
if (normalBox === normalTag) return true;
if (normalBox.endsWith('s') && normalBox.slice(0, -1) === normalTag) return true;
if (normalTag.endsWith('s') && normalTag.slice(0, -1) === normalBox) return true;
if (normalBox.replace(/[^a-z0-9]/g, '') === normalTag.replace(/[^a-z0-9]/g, '')) return true;
if (normalBox.includes(normalTag) || normalTag.includes(normalBox)) return true;
return false;
}

function checkboxContexts(box) {
const contexts = [];
if (box.value) contexts.push(box.value);
if (box.id) {
const lab = document.querySelector(`label[for="${box.id}"]`);
if (lab && lab.innerText) contexts.push(lab.innerText);
}
const labAncestor = box.closest('label');
if (labAncestor && labAncestor.innerText) contexts.push(labAncestor.innerText);
const li = box.closest('li') || box.closest('.bh-sl-filters') || box.closest('.scasl-tag-list') || box.closest('ul') || box.closest('div');
if (li && li.innerText) contexts.push(li.innerText);
if (box.getAttribute('aria-label')) contexts.push(box.getAttribute('aria-label'));
if (box.title) contexts.push(box.title);
return Array.from(new Set(contexts)).map(normalize).filter(Boolean);
}

// Event dispatch helpers
function dispatchChangeEvents(el) {
try {
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
el.dispatchEvent(new Event('blur', { bubbles: true }));
el.dispatchEvent(new CustomEvent('input', { bubbles: true }));
} catch (e) {}
}

function clickElement(el) {
try {
el.focus && el.focus();
el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
} catch (e) {}
}

// Robust checkbox toggler with adapter marking and timestamp
function checkCheckbox(box, canonicalTag) {
if (!box) return false;
if (box.disabled) return false;

// If already checked, ensure adapter mark exists and dispatch events
if (box.checked) {
markBox(box, canonicalTag);
dispatchChangeEvents(box);
return true;
}

// Try label clicks first (preferred for custom UIs)
try {
if (box.id) {
const lab = document.querySelector(`label[for="${box.id}"]`);
if (lab) {
clickElement(lab);
if (box.checked) { markBox(box, canonicalTag); dispatchChangeEvents(box); return true; }
}
}
const labAncestor = box.closest('label');
if (labAncestor) {
clickElement(labAncestor);
if (box.checked) { markBox(box, canonicalTag); dispatchChangeEvents(box); return true; }
}
} catch (e) {}

// Click the box itself and force checked + dispatch events
try {
clickElement(box);
if (!box.checked) box.checked = true;
try { box.setAttribute('aria-checked', 'true'); } catch (e) {}
markBox(box, canonicalTag);
dispatchChangeEvents(box);
return !!box.checked;
} catch (e) {
try {
box.checked = true;
try { box.setAttribute('aria-checked', 'true'); } catch (e) {}
markBox(box, canonicalTag);
dispatchChangeEvents(box);
return true;
} catch (err) {
console.warn('[sl-adapter] Failed to check checkbox', err, box);
return false;
}
}
}

// Mark the box as adapter-controlled and record tag/time
function markBox(box, canonicalTag) {
try {
box.setAttribute(ADAPTER_MARK, '1');
if (canonicalTag) box.setAttribute('data-sl-adapter-tag', canonicalTag);
box.setAttribute('data-sl-adapter-checked-at', Date.now().toString());
// clear any manual flag on a successful adapter check
try { box.removeAttribute('data-sl-manual-unchecked'); } catch (e) {}
} catch (e) {}
}

// Persistent pill UI
function showPill(applied, unmatched) {
try {
let container = document.getElementById(PILL_ID);
const pillStyles = `
position: fixed;
top: 14px;
right: 14px;
z-index: 99999;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
font-size: 13px;
color: #fff;
background: rgba(0,0,0,0.6);
padding: 8px 12px;
border-radius: 10px;
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
display: flex;
gap: 12px;
align-items: center;
min-width: 160px;
max-width: 520px;
word-break: break-word;
`;
const listStyles = 'display:flex; gap:8px; flex-wrap:wrap; align-items:center;';

if (!container) {
container = document.createElement('div');
container.id = PILL_ID;
container.setAttribute('role', 'status');
container.setAttribute('aria-live', 'polite');
container.style.cssText = pillStyles;

const list = document.createElement('div');
list.className = 'sl-tag-list';
list.style.cssText = listStyles;
container.appendChild(list);

const closeBtn = document.createElement('button');
closeBtn.type = 'button';
closeBtn.className = 'sl-pill-close';
closeBtn.setAttribute('aria-label', 'Dismiss tag pill');
closeBtn.title = 'Dismiss';
closeBtn.innerHTML = '&#x2715;';
closeBtn.style.cssText = `
margin-left: 8px;
background: transparent;
border: none;
color: #ffffff;
font-size: 16px;
line-height: 1;
cursor: pointer;
padding: 2px 6px;
border-radius: 6px;
`;
closeBtn.addEventListener('click', function (e) {
e.stopPropagation();
try { container.remove(); } catch (err) {}
cleanupMonitor(); // stop monitor when user dismisses
});

container.appendChild(closeBtn);
document.body.appendChild(container);
}

const list = container.querySelector('.sl-tag-list');
list.innerHTML = '';

if (applied && applied.length) {
applied.forEach(t => {
const span = document.createElement('span');
span.textContent = t;
span.style.cssText = 'background: #A4118D; padding: 4px 8px; border-radius: 999px; color:#fff; font-weight:500;';
list.appendChild(span);
});
} else {
const none = document.createElement('span');
none.textContent = 'No tags applied';
none.style.cssText = 'opacity:0.9; padding:4px 8px; border-radius:6px';
list.appendChild(none);
}

if (unmatched && unmatched.length) {
const sep = document.createElement('span');
sep.textContent = '•';
sep.style.cssText = 'opacity:0.85; margin:0 4px';
list.appendChild(sep);
unmatched.forEach(t => {
const span = document.createElement('span');
span.textContent = t;
span.style.cssText = 'background: rgba(255,80,80,0.18); padding: 4px 8px; border-radius: 999px; color:#fff; opacity:0.95;';
list.appendChild(span);
});
}
} catch (e) { console.warn('[sl-adapter] showPill error', e); }
}

// applyTagsOnce returns applied/unmatched plus matchedMap of originalTag -> [boxes]
function applyTagsOnce(tags) {
const result = { applied: [], unmatched: [], matchedMap: {} };
if (!tags || !tags.length) return result;

const form = document.getElementById(FORM_ID);
const root = form || document;
const boxes = Array.from(root.querySelectorAll(CHECKBOX_SELECTOR));
if (!boxes.length) {
result.unmatched = tags.slice();
return result;
}

const normalizedTags = tags.map(normalize);

normalizedTags.forEach((ntag, idx) => {
const original = tags[idx];
let matched = false;
result.matchedMap[original] = [];

// First pass: direct value match
for (const box of boxes) {
const val = normalize(box.value || '');
if (tagsMatch(val, ntag)) {
const ok = checkCheckbox(box, ntag);
if (ok) { matched = true; result.matchedMap[original].push(box); }
}
}
if (matched) { result.applied.push(original); return; }

// Second pass: label/parent contexts
for (const box of boxes) {
const contexts = checkboxContexts(box);
for (const ctx of contexts) {
if (tagsMatch(ctx, ntag)) {
const ok = checkCheckbox(box, ntag);
if (ok) { matched = true; result.matchedMap[original].push(box); break; }
}
}
if (matched) break;
}
if (matched) { result.applied.push(original); return; }

// Third pass: substring/fuzzy
for (const box of boxes) {
const val = normalize(box.value || '');
if (val.includes(ntag) || ntag.includes(val)) {
const ok = checkCheckbox(box, ntag);
if (ok) { matched = true; result.matchedMap[original].push(box); }
}
}
if (matched) result.applied.push(original);
else result.unmatched.push(original);
});

return result;
}

// Trigger the search action safely (small delay)
function triggerSearchAfterDelay() {
try {
const form = document.getElementById(FORM_ID);
const btn = document.getElementById(SEARCH_BUTTON_ID) || (form && form.querySelector('button[type="submit"], input[type="submit"]'));
if (btn) {
setTimeout(() => {
try { btn.click(); } catch (e) { try { form && form.submit(); } catch (err) {} }
}, 120);
} else if (form) {
setTimeout(() => { try { form.submit(); } catch (e) {} }, 120);
}
} catch (e) { console.warn('[sl-adapter] triggerSearch error', e); }
}

// --- Monitoring / reapply logic with safety guards ---
let monitorObserver = null;
let monitorInterval = null;
let reapplyCounts = {}; // { tagCanonical: number }
const manualUncheck = new Set(); // normalized contexts the user manually unchecked
let monitorActive = false;

function cleanupMonitor() {
try { if (monitorObserver) { monitorObserver.disconnect(); monitorObserver = null; } } catch (e) {}
try { if (monitorInterval) { clearInterval(monitorInterval); monitorInterval = null; } } catch (e) {}
try { document.removeEventListener('change', monitorChangeHandler, true); } catch (e) {}
reapplyCounts = {};
manualUncheck.clear();
monitorActive = false;
}

// Detect user manual change (trusted events); mark contexts to avoid reapply
function monitorChangeHandler(e) {
try {
const tgt = e.target;
if (!tgt || !tgt.matches || !tgt.matches(CHECKBOX_SELECTOR)) return;
if (e.isTrusted) {
// record normalized value and contexts as manual
const val = normalize(tgt.value || '');
manualUncheck.add(val);
const ctxs = checkboxContexts(tgt);
ctxs.forEach(c => manualUncheck.add(c));
// mark element specifically too
try { tgt.setAttribute('data-sl-manual-unchecked', '1'); } catch (err) {}
}
} catch (e) {}
}

// Monitor and reapply logic, but limited per-tag, and skip tags user manually changed
function monitorAndReapply(matchedMap, appliedTags, durationMs = MONITOR_DURATION) {
if (monitorActive) return; // already monitoring
monitorActive = true;
reapplyCounts = {};
appliedTags.forEach(t => reapplyCounts[normalize(t)] = 0);

// install change listener to detect user interactions
document.addEventListener('change', monitorChangeHandler, true);

// MutationObserver for 'checked' attribute changes
monitorObserver = new MutationObserver((records) => {
try {
for (const rec of records) {
const node = rec.target;
if (!node || !node.matches || !node.matches(CHECKBOX_SELECTOR)) continue;
// We only care when a box becomes unchecked
if (node.checked) continue;
// Was it previously adapter-checked?
if (!node.hasAttribute(ADAPTER_MARK)) continue;
const canonicalTag = normalize(node.getAttribute('data-sl-adapter-tag') || node.value || '');
// Skip if user manually unchecked
if (manualUncheck.has(canonicalTag) || node.hasAttribute('data-sl-manual-unchecked')) continue;
// Reapply limited times
if ((reapplyCounts[canonicalTag] || 0) >= MAX_REAPPLY_PER_TAG) {
console.info('[sl-adapter] max reapply reached for tag', canonicalTag);
continue;
}
// find candidate boxes for this tag and try re-checking
const candidates = Array.from(document.querySelectorAll(CHECKBOX_SELECTOR)).filter(b => {
const val = normalize(b.value || '');
return tagsMatch(val, canonicalTag) || checkboxContexts(b).some(ctx => tagsMatch(ctx, canonicalTag));
});
for (const cand of candidates) {
// skip if user manually changed this candidate
if (manualUncheck.has(normalize(cand.value || '')) || cand.hasAttribute('data-sl-manual-unchecked')) continue;
const ok = checkCheckbox(cand, canonicalTag);
if (ok) {
reapplyCounts[canonicalTag] = (reapplyCounts[canonicalTag] || 0) + 1;
console.info('[sl-adapter] Re-applied tag after external reset:', canonicalTag, 'count:', reapplyCounts[canonicalTag]);
break; // reapply to first success
}
}
}
} catch (e) { console.warn('[sl-adapter] monitor observer error', e); }
});

monitorObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['checked'] });

// Periodic reinforcement (in case attributes change without mutation records)
monitorInterval = setInterval(() => {
try {
appliedTags.forEach(t => {
const canonical = normalize(t);
if (manualUncheck.has(canonical)) return;
if ((reapplyCounts[canonical] || 0) >= MAX_REAPPLY_PER_TAG) return;
// find candidate boxes for this canonical tag
const candidates = Array.from(document.querySelectorAll(CHECKBOX_SELECTOR)).filter(b => {
const val = normalize(b.value || '');
return tagsMatch(val, canonical) || checkboxContexts(b).some(ctx => tagsMatch(ctx, canonical));
});
for (const cand of candidates) {
if (!document.contains(cand)) continue;
if (cand.checked) continue;
if (manualUncheck.has(normalize(cand.value || '')) || cand.hasAttribute('data-sl-manual-unchecked')) continue;
const ok = checkCheckbox(cand, canonical);
if (ok) {
reapplyCounts[canonical] = (reapplyCounts[canonical] || 0) + 1;
console.info('[sl-adapter] Periodically re-applied tag:', canonical, 'count:', reapplyCounts[canonical]);
break;
}
}
});
} catch (e) { console.warn('[sl-adapter] monitorInterval error', e); }
}, 900);

// Stop monitoring after durationMs
setTimeout(() => {
cleanupMonitor();
console.info('[sl-adapter] monitor expired, stopped reapply attempts.');
}, durationMs);
}

// Apply with retries and avoid re-entrant attempts
function applyWithRetries(tags) {
let attempts = 0;
const maxAttempts = Math.ceil(OBSERVER_TIMEOUT / RETRY_INTERVAL);
let isAttempting = false;
let localObserver = null;
let localInterval = null;

// cleanup local watchers
function localCleanup() {
try { if (localInterval) { clearInterval(localInterval); localInterval = null; } } catch (e) {}
try { if (localObserver) { localObserver.disconnect(); localObserver = null; } } catch (e) {}
}

function attempt() {
if (isAttempting) return false; // avoid re-entrancy
isAttempting = true;
attempts++;
console.info(`[sl-adapter] attempt ${attempts}/${maxAttempts}`);
const res = applyTagsOnce(tags);
showPill(res.applied, res.unmatched);
if (res.applied && res.applied.length) {
console.info('[sl-adapter] tags applied:', res);
triggerSearchAfterDelay();
// stop any global monitor and restart fresh for this mapping
cleanupMonitor();
monitorAndReapply(res.matchedMap, res.applied, MONITOR_DURATION);
isAttempting = false;
localCleanup();
return true;
}
isAttempting = false;
if (attempts >= maxAttempts) {
console.info('[sl-adapter] max attempts reached. Final result:', res);
localCleanup();
return false;
}
return false;
}

// initial synchronous attempt
if (attempt()) return;

// set up local observer & interval to retry attempts during initial window
localObserver = new MutationObserver(() => {
if (attempt()) {
try { localObserver.disconnect(); } catch (e) {}
}
});
localObserver.observe(document.body, { childList: true, subtree: true });

localInterval = setInterval(() => {
if (attempt()) {
try { clearInterval(localInterval); } catch (e) {}
try { localObserver && localObserver.disconnect(); } catch (e) {}
}
}, RETRY_INTERVAL);

// ensure stop after OBSERVER_TIMEOUT
setTimeout(() => {
try { clearInterval(localInterval); } catch (e) {}
try { localObserver && localObserver.disconnect(); } catch (e) {}
// final attempt for UX
const final = applyTagsOnce(tags);
showPill(final.applied, final.unmatched);
console.info('[sl-adapter] final attempt after timeout:', final);
}, OBSERVER_TIMEOUT + 100);
}

// Expose public helper
window.__store_locator_apply_tags = function() {
const tags = parseTagsFromURL();
if (!tags.length) { console.log('[sl-adapter] no tags in URL'); return; }
cleanupMonitor(); // stop any previous monitor
applyWithRetries(tags);
};

// Entrypoint
function init() {
const tags = parseTagsFromURL();
if (!tags.length) {
console.log('[sl-adapter] no tags in URL to apply.');
return;
}
cleanupMonitor();
applyWithRetries(tags);

// Re-apply on SPA navigation
window.addEventListener('popstate', function() {
const newTags = parseTagsFromURL();
applyWithRetries(newTags);
});
}

if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(init, 20);
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
</script>

Troubleshooting checklist

  • Script didn’t run? Confirm it is included in the theme and the file URL is correct.

  • Checkbox never toggled? Inspect the DOM OuterHTML of the checkbox — if it’s a fake UI, adapt the script to click the wrapper or invoke theme JS.

  • Search fires but results aren’t filtered? Confirm the store-locator expects checkboxes to be checked (not separate query params or JS-only filters). If it uses a JS API (e.g., StoreLocator.search({ tags: [...] })), call that function directly from the script.

  • Other scripts uncheck repeatedly? Increase MONITOR_DURATION, but also inspect which plugin is resetting — solving at the source (plugin order) is preferred.

  • Console errors from Google Places plugin? Those can break plugin logic and produce resets — fix plugin errors separately.

Did this answer your question?