Appearance
locator tag
A ready-made store locator in one tag: a search form, a list of the locations and a map. It works without Javascript and a search is in the URL, so it can be shared and bookmarked. (from v4.0.0, requires Maps 5.0.0+)
Like every map it needs the html_header and html_footer tags of Maps on the page. Without html_footer the form and the list work, but the map stays empty:
<html>
<head>
{exp:reinos_maps:html_header}
</head>
<body>
{exp:reinos_maps_locator:locator channel="stores"}
{exp:reinos_maps:html_footer}
</body>
</html>Before a visitor searches the tag shows all locations. A search shows the locations within the chosen distance, nearest first. When there are none, it shows the nearest locations.
Categories
Add checkboxes for the categories with category_group. A visitor can filter with them with or without a location.
{exp:reinos_maps_locator:locator channel="stores" category_group="3" label:categories="Store type"}Use my location
The form has a Use my location button: the browser asks the visitor for permission and the tag searches around the device, without a geocoding request. The button is only shown with Javascript, on a browser that supports it and on an HTTPS page. Turn it off with locate="no".
Suggestions while typing
The search field suggests the cities and postal codes of your own locations, with their number of locations. The suggestions come from the search index on your own server: they are free, don't need an API key or geocoder and no typed text goes to a third party. A place without locations isn't suggested, but a visitor can still type and search it.
A city or postal code is only in the index after it is geocoded, see Reverse geocoding. Turn the suggestions off with suggest="no".
Search without a page reload
With Javascript a search updates the list, the text and the markers on the page. The address in the browser changes with it, so a search can still be shared and the back button works. The script gets the same page, so your own template and tags work the same. Without Javascript, or when the update fails, it is a normal page load. A form with a result_url to another page always loads that page.
When a visitor moves the map and not all locations are on it, a Search this area button appears, e.g. when there are more locations than the limit or after a search with a small distance. When all locations are on the map there is no button, the visitor can just look at the map. It searches around the center of the map, with the smallest of the radius_options that covers the map, and the map stays where it is.
A new search is the page again, so it counts for the Searches per minute setting, like a search with a page reload. Turn this off with sync="no".
The list and the map together
When the map is loaded, a small script keeps the list and the map together:
- Hover or focus a location and its marker lights up, and the other way around.
- Click a location, or its Show on the map button, and the map shows the marker with its popup. A marker in a cluster is shown first.
- Click a marker and its location is marked in the list, the list scrolls to it.
Without Javascript, or while the map waits for consent, the form and the list keep working. The buttons are only added when there is a map to show the marker on.
To place the button yourself, add data-maps-locator-show-button to your own button in the template of a location. Turn the script off with sync="no".
Your own layout
Use the tag as a tag pair to place the parts in your own HTML, e.g. in the grid of your theme. You only need the parts you use.
{exp:reinos_maps_locator:locator channel="stores"}
<header class="search-bar">{locator:form}</header>
<div class="grid">
<main>{locator:map}</main>
<aside>
{locator:status}
{locator:results}
<h3><a href="{path='stores/detail'}/{url_title}">{title}</a></h3>
<p>{address}</p>
{if distance}<p>{distance} {distance_unit}</p>{/if}
<a href="{directions_url}">Route</a>
{/locator:results}
</aside>
</div>
{/exp:reinos_maps_locator:locator}| Part | |
|---|---|
{locator:form} | The search form |
{locator:status} | The text with the result of the search, or an error message |
{locator:results} | The list of the locations. As a tag pair: your own template of one location, with the variables and other tags, e.g. the nested channel:entries tag for custom fields |
{locator:map} | The map |
Only a search form
For example in the header of your site. A layout with only the form doesn't search. Point result_url to the page with the result, that page needs a locator tag too.
{exp:reinos_maps_locator:locator channel="stores" result_url="stores" stylesheet="no"}
{locator:form}
{/exp:reinos_maps_locator:locator}Your own form
Any form to the page with the tag works, the search is in the query string. Use the same names as the location_field and radius_field_max params.
{exp:reinos_maps_locator:locator channel="stores"}
<form method="get" class="my-form">
<input type="search" name="location_field" value="{locator:location}" required>
<select name="radius_field_max">
<option value="10" {if locator:radius == 10}selected{/if}>10 km</option>
<option value="25" {if locator:radius == 25}selected{/if}>25 km</option>
</select>
<button>Search</button>
</form>
{if locator:searched}<p>{locator:count} results</p>{/if}
{locator:results}
{locator:map}
{/exp:reinos_maps_locator:locator}A radius that is not in radius_options is ignored.
| Variable | |
|---|---|
{locator:location} | The searched location, escaped for HTML |
{locator:radius}, {locator:unit} | The distance of the search and its unit |
{locator:searched} | If the visitor searched |
{locator:count}, {locator:total} | The number of locations in the list, and the total without the limit |
Tag parameters
channel
The channel(s) to search, separate multiple channels with a |. Required.
channel="stores" [REQUIRED]layout
The place of the list: list-left, list-right or map-top. On a small screen the list is always below the map. This param is for the tag without your own layout.
layout="list-left" [OPTIONAL]theme
light, dark or auto (dark when the visitor uses dark mode). It is also the theme of the map.
theme="light" [OPTIONAL]radius
The default distance.
radius="25" [OPTIONAL]radius_options
The distances a visitor can choose from. A visitor can't search with another distance.
radius_options="10|25|50|100" [OPTIONAL]metric
km or mile.
metric="km" [OPTIONAL]sticky_first
The locations of sticky entries first (featured stores), then the rest by distance. They get the class .maps-locator__item--featured and a Featured label. With no they are sorted by distance like the others.
sticky_first="yes" [OPTIONAL]limit
The maximum number of locations in the list and on the map. The tag has no paging: the text above the list says when there are more locations. Use the search_result tag when you need paging.
limit="50" [OPTIONAL]nearest
The number of nearest locations to show when a search has no results, 0 to turn it off.
nearest="5" [OPTIONAL]category_group
The category group(s) to show as checkboxes in the form, separate multiple groups with a |. Without this param there are no checkboxes.
category_group="3" [OPTIONAL]category_match
any: a location needs one of the checked categories. all: a location needs all of them.
category_match="any" [OPTIONAL]search_category_field
The name of the checkboxes in the URL, the same as on the search_form tag.
search_category_field="search_category_field" [OPTIONAL]suggest
With no the search field doesn't suggest places.
suggest="yes" [OPTIONAL]locate
With no the form has no Use my location button.
locate="yes" [OPTIONAL]status
The status of the entries.
status="open" [OPTIONAL]default:show_all
Show all locations before a visitor searches (up to the limit). With no the tag only shows the form.
default:show_all="yes" [OPTIONAL]result_url
The page of the search result, by default the current page. That page needs a locator tag too.
result_url="stores/search" [OPTIONAL]location_field, radius_field_max
The names of the fields in the URL, the same as on the search_form tag.
location_field="location_field" radius_field_max="radius_field_max" [OPTIONAL]sync
With no the list and the map stay separate and a search reloads the page.
sync="yes" [OPTIONAL]class
An extra class name for the locator.
class="" [OPTIONAL]stylesheet
With no the stylesheet is not loaded, so you can write your own.
stylesheet="yes" [OPTIONAL]map:
Every param of the map tag with a map: prefix, e.g. the tiles or the consent gate. The list keeps working when the map waits for consent.
The map fits around the markers, one marker gets zoom level 15. Use map:fit_map:force_zoom to change that.
map:tiles="openstreetmap" map:consent="click" map:marker_cluster:enabled="no" [OPTIONAL]Texts
Every text can be changed with a param, e.g. to translate it. [location], [radius], [unit], [count] and [total] are replaced where they apply.
| Param | Default |
|---|---|
label:location | City, postal code or address |
label:radius | Distance |
label:submit | Search |
label:directions | Get directions |
label:show | Show on the map |
label:featured | Featured |
label:locate | Use my location |
label:search_area | Search this area |
text:this_area | this area (the [location] after Search this area) |
label:categories | Categories |
text:my_location | your location (the [location] after Use my location) |
text:locate_error | We could not get your location. Enter a place instead. |
text:result | 1 location within [radius] [unit] of [location] |
text:results | [count] locations within [radius] [unit] of [location] |
text:results_limited | The nearest [count] of [total] locations within [radius] [unit] of [location] |
text:nearest | No locations within [radius] [unit] of [location]. These are the nearest locations. |
text:no_results | No locations within [radius] [unit] of [location]. |
text:not_found | We could not find [location]. Check the spelling and try again. |
text:rate_limit | Too many searches. Try again in a minute. |
text:map | Map of the locations (the name of the map for screen readers) |
Variables
The variables of a location in {locator:results}:
| Variable | |
|---|---|
{title}, {url_title}, {entry_id}, {channel_id} | The entry |
{entry_date}, {expiration_date} | The dates of the entry |
{address} | The street, postal code, city and country as one text |
{street_name}, {street_number}, {postal_code}, {city}, {country}, {country_code} | The parts of the address |
{lat}, {lng}, {latlng} | The coordinate |
{distance}, {distance_unit} | The distance to the searched location, empty before a search |
{directions_url} | A link to get directions in Google Maps |
{sticky} | 1 for a sticky entry (a featured store), use {if sticky} |
{location_key} | The key of the marker for the Javascript API of Maps |
Styling
Change the look with CSS variables on .maps-locator, you don't need to override the styles:
css
.maps-locator {
--maps-locator-height: 640px;
--maps-locator-list-width: 400px;
--maps-locator-accent: #0a7c55;
}| Variable | |
|---|---|
--maps-locator-height | The height of the map and the list |
--maps-locator-list-width | The width of the list |
--maps-locator-gap, --maps-locator-radius | The space between the parts and their rounded corners |
--maps-locator-bg, --maps-locator-bg-hover | The background |
--maps-locator-text, --maps-locator-muted | The text |
--maps-locator-border | The borders |
--maps-locator-accent, --maps-locator-accent-text | The button and links |
--maps-locator-error | An error message |
Every part has a class: .maps-locator__form, .maps-locator__field, .maps-locator__submit, .maps-locator__locate, .maps-locator__locate-error, .maps-locator__categories, .maps-locator__category, .maps-locator__suggestions, .maps-locator__suggestion, .maps-locator__status, .maps-locator__body, .maps-locator__list, .maps-locator__item, .maps-locator__title, .maps-locator__address, .maps-locator__distance, .maps-locator__directions, .maps-locator__show, .maps-locator__area and .maps-locator__map.
While a search is loading the locator has aria-busy="true". The script adds .maps-locator__item--highlight on hover and focus, .maps-locator__item--active (with aria-current="true") while the popup is open, and .maps-locator-marker--active to the icon of the marker.
