RP Promap Help Article
How to Replace the Search Radius Dropdown with a Slider
This guide explains how to convert the RP Promap Search Radius dropdown into a visual slider with tick labels, active highlighting, and a filled track.
What this customization does
This script changes the default Search Radius dropdown into a slider.
For example, instead of showing:
100
200
300
No Limit
as a dropdown, customers will see a slider with labeled tick points.
The original dropdown is still used behind the scenes, so RP Promap continues to receive the selected radius value normally.
Where to install the script
Add the script to the page where RP Promap is embedded.
Recommended locations:
Shopify theme β Online Store β Themes β Edit code
Then place the script in one of these locations:
theme.liquid
before the closing:
</body>
Or place it directly on the page/template where RP Promap is displayed.
Installation steps
Open your Shopify theme editor.
Go to the page or theme file where RP Promap loads.
Paste the full script before the closing
</body>tag.Save the file.
Open the storefront page with RP Promap.
Confirm the Search Radius dropdown is replaced by a slider.
Required HTML selectors
This script is designed for the RP Promap / Store Locator structure that uses:
#scasl-app-container
for the locator container, and:
#scasl-radius-container
for the Search Radius dropdown.
The script also waits for displayed results using:
.beside-scasl-item
How the script works
The script uses a MutationObserver to wait until RP Promap has finished rendering on the page.
It checks that:
The locator container exists
The radius dropdown exists
The dropdown has options
Store results are displayed
Once those conditions are true, it converts the dropdown into a slider and disconnects the observer.
This prevents the script from continuously watching the page and avoids performance issues.
Troubleshooting
Open your browser console and run:
rpSliderDebug.isReady()
If it returns:
true
RP Promap is ready.
You can manually retry the conversion with:
rpSliderDebug.run()
To confirm the dropdown exists:
rpSliderDebug.getSelect()
If this returns null, the selector may need to be updated.
Customization
To change the active slider color, update:
const ACTIVE_COLOR = "#111";
To change the inactive track color, update:
const INACTIVE_COLOR = "#d1d1d1";
To adjust the slider handle size, update:
const THUMB_SIZE = 18;
To adjust the track height, update:
const TRACK_HEIGHT = 6;
Notes
This script hides the original dropdown visually, but keeps it in the DOM. That allows RP Promap to continue using the existing radius field normally.
To Install the script copy it from here:
<script>
/**
* RP Promap / Store Locator Radius Slider
* ---------------------------------------
* Converts the Search Radius dropdown into a styled range slider.
*
* Features:
* - Waits until the store locator app is loaded
* - Waits until search results are displayed
* - Converts #scasl-radius-container into a slider
* - Adds tick dots and tick labels
* - Highlights the active tick
* - Shows an active value label under the thumb
* - Colors the slider track up to the selected value
* - Disconnects the MutationObserver after successful setup
*
* Target dropdown:
* <select id="scasl-radius-container">
*
* Debug helpers available in console:
* rpSliderDebug.run()
* rpSliderDebug.isReady()
* rpSliderDebug.getSelect()
*/
(function () {
/**
* =========================
* CONFIGURATION
* =========================
*/
const CONTAINER_SELECTOR = "#scasl-app-container";
const DROPDOWN_SELECTOR = "#scasl-radius-container";
const RESULTS_SELECTOR = ".beside-scasl-item";
const WRAPPER_CLASS = "rp-slider-wrapper";
const VALUE_LABEL_CLASS = "rp-slider-value-label";
const TICKS_CLASS = "rp-slider-ticks";
const TICK_CLASS = "rp-slider-tick";
const TICK_DOT_CLASS = "rp-slider-tick-dot";
const TICK_TEXT_CLASS = "rp-slider-tick-text";
const TICK_ACTIVE_CLASS = "is-active";
const ACTIVE_COLOR = "#111";
const INACTIVE_COLOR = "#d1d1d1";
const TRACK_HEIGHT = 6;
const THUMB_SIZE = 18;
let observer = null;
let hasConverted = false;
/**
* Adds all required CSS once.
*/
function injectStyles() {
if (document.getElementById("rp-slider-styles")) return;
const style = document.createElement("style");
style.id = "rp-slider-styles";
style.textContent = `
.${WRAPPER_CLASS} {
width: 100%;
margin-top: 10px;
position: relative;
}
.${WRAPPER_CLASS} input[type="range"] {
width: 100%;
margin: 0;
appearance: none;
height: ${THUMB_SIZE}px;
background: transparent;
cursor: pointer;
}
.${WRAPPER_CLASS} input[type="range"]::-webkit-slider-runnable-track {
height: ${TRACK_HEIGHT}px;
border-radius: 999px;
background: var(--rp-slider-track);
}
.${WRAPPER_CLASS} input[type="range"]::-webkit-slider-thumb {
appearance: none;
width: ${THUMB_SIZE}px;
height: ${THUMB_SIZE}px;
border-radius: 50%;
background: ${ACTIVE_COLOR};
margin-top: -${(THUMB_SIZE - TRACK_HEIGHT) / 2}px;
}
.${WRAPPER_CLASS} input[type="range"]::-moz-range-track {
height: ${TRACK_HEIGHT}px;
border-radius: 999px;
background: var(--rp-slider-track);
}
.${WRAPPER_CLASS} input[type="range"]::-moz-range-thumb {
width: ${THUMB_SIZE}px;
height: ${THUMB_SIZE}px;
border: 0;
border-radius: 50%;
background: ${ACTIVE_COLOR};
}
.${TICKS_CLASS} {
position: relative;
height: 28px;
margin-top: 4px;
}
.${TICK_CLASS} {
position: absolute;
top: 0;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
font-size: 11px;
color: #888;
white-space: nowrap;
}
.${TICK_DOT_CLASS} {
width: 7px;
height: 7px;
border-radius: 999px;
background: #bbb;
margin-bottom: 4px;
transition: transform 160ms ease, background 160ms ease;
}
.${TICK_CLASS}.${TICK_ACTIVE_CLASS} .${TICK_DOT_CLASS} {
background: ${ACTIVE_COLOR};
transform: scale(1.45);
}
.${TICK_CLASS}.${TICK_ACTIVE_CLASS} .${TICK_TEXT_CLASS} {
color: ${ACTIVE_COLOR};
font-weight: 700;
}
.${VALUE_LABEL_CLASS} {
position: relative;
display: inline-block;
margin-top: 8px;
font-size: 12px;
font-weight: 700;
transform: translateX(-50%);
white-space: nowrap;
transition: left 180ms ease;
will-change: left;
}
`;
document.head.appendChild(style);
}
function getSelect() {
return document.querySelector(DROPDOWN_SELECTOR);
}
/**
* Confirms that the locator app, radius dropdown,
* dropdown options, and displayed results are present.
*/
function isReady() {
const container = document.querySelector(CONTAINER_SELECTOR);
const select = getSelect();
const resultsCount = document.querySelectorAll(RESULTS_SELECTOR).length;
return container && select && select.options.length > 1 && resultsCount > 0;
}
/**
* Converts a dropdown index into a slider percentage.
*/
function getThumbCenterPercent(index, count) {
if (count <= 1) return 0;
return (index / (count - 1)) * 100;
}
/**
* Colors the slider track from left to selected value.
*/
function setTrackFill(slider, percent) {
slider.style.setProperty(
"--rp-slider-track",
`linear-gradient(to right, ${ACTIVE_COLOR} 0%, ${ACTIVE_COLOR} ${percent}%, ${INACTIVE_COLOR} ${percent}%, ${INACTIVE_COLOR} 100%)`
);
}
/**
* Creates dot ticks and text labels from the dropdown options.
*/
function createTicks(select) {
const ticks = document.createElement("div");
ticks.className = TICKS_CLASS;
const options = Array.from(select.options);
const count = options.length;
options.forEach(function (option, index) {
const tick = document.createElement("div");
tick.className = TICK_CLASS;
tick.style.left = getThumbCenterPercent(index, count) + "%";
const dot = document.createElement("div");
dot.className = TICK_DOT_CLASS;
const text = document.createElement("div");
text.className = TICK_TEXT_CLASS;
text.textContent = option.text;
tick.appendChild(dot);
tick.appendChild(text);
ticks.appendChild(tick);
});
return ticks;
}
/**
* Keeps slider, dropdown, active label, ticks,
* and track fill in sync.
*/
function updateUI(select, slider, label, ticks) {
const index = select.selectedIndex >= 0 ? select.selectedIndex : 0;
const count = select.options.length;
const percent = getThumbCenterPercent(index, count);
slider.value = String(index);
label.textContent = select.options[index]?.text || "";
label.style.left = percent + "%";
setTrackFill(slider, percent);
ticks.querySelectorAll("." + TICK_CLASS).forEach(function (tick, i) {
tick.classList.toggle(TICK_ACTIVE_CLASS, i === index);
});
}
/**
* Builds the slider UI and hides the original dropdown.
*/
function convert() {
if (hasConverted) return true;
const select = getSelect();
if (!select || select.options.length < 2) return false;
hasConverted = true;
injectStyles();
const wrapper = document.createElement("div");
wrapper.className = WRAPPER_CLASS;
const slider = document.createElement("input");
slider.type = "range";
slider.min = "0";
slider.max = String(select.options.length - 1);
slider.step = "1";
const ticks = createTicks(select);
const label = document.createElement("div");
label.className = VALUE_LABEL_CLASS;
slider.addEventListener("input", function () {
const index = Number(slider.value);
select.selectedIndex = index;
select.dispatchEvent(new Event("change", { bubbles: true }));
updateUI(select, slider, label, ticks);
});
select.addEventListener("change", function () {
updateUI(select, slider, label, ticks);
});
select.style.display = "none";
select.insertAdjacentElement("afterend", wrapper);
wrapper.appendChild(slider);
wrapper.appendChild(ticks);
wrapper.appendChild(label);
updateUI(select, slider, label, ticks);
return true;
}
/**
* Runs only when the app is ready.
*/
function run() {
if (!isReady()) return false;
const done = convert();
if (done && observer) {
observer.disconnect();
observer = null;
}
return done;
}
run();
/**
* Watches for delayed app rendering.
* Uses childList only to avoid page hangs from Google Maps updates.
*/
if (!hasConverted) {
observer = new MutationObserver(run);
observer.observe(document.body, {
childList: true,
subtree: true
});
}
window.rpSliderDebug = {
run,
isReady,
getSelect
};
})();
</script>
Β© Rose Perl Technology
