| name | typo3-solr/FRONTEND |
| description | Custom JavaScript integration for EXT:solr search, suggest/autocomplete, and facet filtering without jQuery. Vanilla JS examples and AJAX endpoints for TYPO3 Fluid templates. |
EXT:solr Frontend: Custom JavaScript Integration
Why this supplement? EXT:solr ships with jQuery-based suggest and AJAX. Modern TYPO3 projects often avoid jQuery. This guide shows how to use EXT:solr's AJAX endpoints with vanilla JavaScript or lightweight frameworks.
1. EXT:solr AJAX Endpoints
EXT:solr exposes two key AJAX endpoints via TYPO3 page types:
graph LR
Input["User types query"]
Suggest["Suggest Endpoint<br/>(JSON)"]
Search["Search Endpoint<br/>(typeNum 7383, HTML)"]
DOM["Update DOM"]
Input -->|debounced fetch| Suggest
Input -->|form submit fetch| Search
Suggest --> DOM
Search --> DOM
typeNum 7383: AJAX Search Results
When configured, requests to ?type=7383&tx_solr[q]=... return the search results HTML fragment (without page layout). The response replaces the content of div.tx_solr.
Suggest Endpoint
The suggest controller returns JSON with suggestions and optional top results:
{
"suggestions": {
"typo3": 42,
"typo3 solr": 15,
"typo3 search": 8
},
"documents": {
"doc_1": {
"title": "Getting Started with Solr",
"url": "/getting-started",
"type": "pages",
"content": "Learn how to set up..."
}
}
}
TypoScript Setup
Do NOT include the jQuery TypoScript template. Instead, configure the AJAX page type manually:
plugin.tx_solr_PiResults_Results < tt_content.list.20.solr_pi_results
solr_ajax = PAGE
solr_ajax {
typeNum = 7383
config {
disableAllHeaderCode = 1
additionalHeaders.10.header = Content-Type: text/html;charset=utf-8
no_cache = 1
}
10 < plugin.tx_solr_PiResults_Results
}
2. Suggest / Autocomplete with Vanilla JS
Full working example with debouncing, keyboard navigation, and ARIA accessibility:
class SolrSuggest {
constructor(inputSelector, options = {}) {
this.input = document.querySelector(inputSelector);
if (!this.input) return;
this.options = {
suggestUrl: this.input.dataset.suggestUrl || '/suggest',
minChars: options.minChars || 3,
debounceMs: options.debounceMs || 250,
maxSuggestions: options.maxSuggestions || 10,
};
this.activeIndex = -1;
this.suggestions = [];
this.debounceTimer = null;
this.dropdown = document.createElement('ul');
this.dropdown.setAttribute('role', 'listbox');
this.dropdown.setAttribute(, );
...();
.. = ;
...(.);
..(, );
..(, );
..(, );
..(, );
..(, .());
..(, .(e));
.(, {
(!..(e.) && !..(e.)) {
.();
}
});
}
() {
(.);
query = ...();
(query. < ..) {
.();
;
}
. = ( .(query), ..);
}
() {
{
url = (.., ..);
url..(, query);
response = (url.(), {
: { : },
});
(!response.) ;
data = response.();
.(data);
} (err) {
.(, err);
}
}
() {
.. = ;
. = [];
. = -;
terms = .(data. || {});
terms.(, ..).( {
li = .();
li.(, );
li.(, );
li. = term;
li.(, .(term));
..(li);
..(term);
});
(data.) {
docs = .(data.);
(docs. > ) {
divider = .();
divider.(, );
divider..();
..(divider);
docs.( {
li = .();
li.(, );
link = .();
link. = doc.;
link. = doc.;
li.(link);
..(li);
});
}
}
.. = .. === ;
..(, (!..));
}
() {
(..) ;
(e.) {
:
e.();
. = .(. + , .. - );
.();
;
:
e.();
. = .(. - , -);
.();
;
:
(. >= ) {
e.();
.(.[.]);
}
;
:
.();
;
}
}
() {
..().( {
el..(, i === .);
});
(. >= ) {
..(, );
} {
..();
}
}
() {
.. = term;
.();
..()?.();
}
() {
.. = ;
.. = ;
. = -;
..(, );
..();
}
}
.(, {
();
});
Minimal CSS:
.solr-suggest-dropdown {
position: absolute;
z-index: 100;
background: var(--color-surface, #fff);
border: 1px solid var(--color-border, #ccc);
border-radius: 4px;
list-style: none;
padding: 0;
margin: 4px 0 0;
max-height: 300px;
overflow-y: auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.solr-suggest-dropdown [role="option"] {
padding: 8px 12px;
cursor: pointer;
}
.solr-suggest-dropdown [role="option"].active,
.solr-suggest-dropdown [role="option"]:hover {
background: var(--color-primary-light, #e8f0fe);
}
.solr-suggest-divider {
border-top: solid (--color-border, );
: ;
}
HTML:
<form action="/search" method="get">
<div style="position: relative;">
<input type="search"
name="tx_solr[q]"
data-solr-suggest
data-suggest-url="/index.php?type=7384"
placeholder="Search..."
autocomplete="off" />
</div>
</form>
3. Facet Filtering with Vanilla JS
Intercept facet links and load results via AJAX:
class SolrFacets {
constructor(containerSelector) {
this.container = document.querySelector(containerSelector);
if (!this.container) return;
this.resultsContainer = document.querySelector('.tx_solr');
this.bindFacetLinks();
}
bindFacetLinks() {
this.container.addEventListener('click', (e) => {
const link = e.target.closest('a[data-solr-facet]');
if (!link) return;
e.preventDefault();
this.loadResults(link.href);
});
}
async loadResults(url) {
this.resultsContainer.classList.add('solr-loading');
try {
const ajaxUrl = new URL(url);
ajaxUrl..(, );
response = (ajaxUrl.());
(!response.) ();
html = response.();
.. = html;
history.(, , url);
.();
} (err) {
.(, err);
} {
...();
}
}
}
.(, {
();
});
Add data-solr-facet to facet links in your Fluid partial:
<a href="{facetOption.url}" data-solr-facet>{facetOption.label} ({facetOption.count})</a>
4. Search Results with Vanilla JS
Full AJAX search form without jQuery:
class SolrSearch {
constructor(formSelector) {
this.form = document.querySelector(formSelector);
if (!this.form) return;
this.resultsContainer = document.querySelector('.tx_solr');
this.form.addEventListener('submit', (e) => this.onSubmit(e));
window.addEventListener('popstate', () => {
this.loadResults(window.location.href);
});
}
async onSubmit(e) {
e.preventDefault();
const formData = new FormData(this.form);
const params = new URLSearchParams(formData);
const url = `${this.form.action}?`;
history.(, , url);
.(url);
}
() {
..(, );
...();
{
ajaxUrl = (url, ..);
ajaxUrl..(, );
response = (ajaxUrl.());
(!response.) ();
html = response.();
.. = html;
} (err) {
.. =
;
.(, err);
} {
..(, );
...();
}
}
}
.(, {
();
});
5. Removing jQuery Dependency
To use EXT:solr without jQuery:
- Do NOT include the TypoScript template "Search - Ajaxify the searchresults with jQuery"
- Do NOT include the TypoScript template "(Example) Suggest/autocomplete with jquery"
- Configure the AJAX page type manually (see section 1)
- Replace
solr-ajaxified CSS class with data-solr-* attributes in your Fluid templates
- Use the vanilla JS classes from this guide