En smartare sök-dropdown med HTMX
Inleiding
I en tidigare artikel visade jag er hur man skapar sök nedåt med Alpine.js och HTMX så visade jag hur vi kunde aktivera Cross-Site Request förfalskningsskydd genom att använda AntiforgeryRequestToken i ASP.NET Core med JavaScript med HTMX för att implementera en Donut Hole cache. Ett olöst problem var hur den laddade sidorna
Problemet
Problemet var att jag använde HTMX AJAX för att ladda upp pagen efter att du hade valt resultatet från den nedgående sidan.
selectResult(result) {
htmx.ajax('get', result.url, {
target: '#contentcontainer', // The container to update
swap: 'innerHTML', // Replace the content inside the target
}).then(function() {
history.pushState(null, '', result.url);
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
Problemet var att medan det skulle ladda upp den rätta sidan och uppdatera den visat URL med den nya, störde det baken knappen.
Som med min den sista artikeln om bak-knappen shennanigans det var något jag ville fixa
Lösningen
Som förut så var lösningen att låta HTMX hantera detta direkt. För att göra det uppdaterade jag min template som jag använder för sökresultaten
_typeahead.cshtml
<div x-data="window.mostlylucid.typeahead()" class="relative" id="searchelement" x-on:click.outside="results = []">
@Html.AntiForgeryToken()
<label class="input input-sm bg-neutral-500 bg-opacity-10 input-bordered flex items-center gap-2">
<input
type="text"
x-model="query"
x-on:input.debounce.200ms="search"
x-on:keydown.down.prevent="moveDown"
x-on:keydown.up.prevent="moveUp"
x-on:keydown.enter.prevent="selectHighlighted"
placeholder="Search..."
class="border-0 grow input-sm text-black dark:text-white bg-transparent w-full"/>
<i class="bx bx-search"></i>
</label>
<!-- Dropdown -->
<ul x-show="results.length > 0"
id="searchresults"
class="absolute z-100 my-2 w-full bg-white dark:bg-custom-dark-bg border border-1 text-black dark:text-white border-neutral-600 rounded-lg shadow-lg">
<template x-for="(result, index) in results" :key="result.slug">
<li :class="{
'dark:bg-blue-dark bg-blue-light': index === highlightedIndex,
'dark:hover:bg-blue-dark hover:bg-blue-light': true
}"
class="cursor-pointer text-sm p-2 m-2">
<!-- These are the key changes.-->
<a
x-on:click="selectResult(index)"
@* :href="result.url" *@
:hx-get="result.url"
hx-target="#contentcontainer"
hx-swap="innerHTML"
hx-push-url="true"
x-text="result.title"
>
</a>
<-- End of changes -->
</li>
</template>
</ul>
</div>
Du ' ser att jag nu genererar rätt HTMX länkar i detta kodblock. Att låta oss använda det korrekta HT MX-beteendet.
typeahead.js
För att möjliggöra detta i min backend JavaScript-kod så lade jag in följande till min sökmetod ( som visas härunder this.$nextTick är en Alpine.js konstruktion som skjuter upp det här tills Alpine har avslutat att bearbeta mallen jag visade förut
Jag använder sedan htmx.process() på sökelementet som säkerställer att HTMX-Attributen fungerar som väntat.
.then(data => {
this.results = data;
this.highlightedIndex = -1; // Reset index on new search
this.$nextTick(() => {
htmx.process(document.getElementById('searchresults'));
});
})
typeahead.js search
search() {
if (this.query.length < 2) {
this.results = [];
this.highlightedIndex = -1;
return;
}
let token = document.querySelector('#searchelement input[name="__RequestVerificationToken"]').value;
console.log(token);
fetch(`/api/search/${encodeURIComponent(this.query)}`, { // Fixed the backtick and closing bracket
method: 'GET', // or 'POST' depending on your needs
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': token // Attach the AntiForgery token in the headers
}
})
.then(response => {
if(response.ok){
return response.json();
}
return Promise.reject(response);
})
.then(data => {
this.results = data;
this.highlightedIndex = -1; // Reset index on new search
this.$nextTick(() => {
htmx.process(document.getElementById('searchresults'));
});
})
.catch((response) => {
console.log(response.status, response.statusText);
if(response.status === 400)
{
console.log('Bad request, reloading page to try to fix it.');
window.location.reload();
}
response.json().then((json) => {
console.log(json);
})
console.log("Error fetching search results");
});
}
selectHighlighted() {
if (this.highlightedIndex >= 0 && this.highlightedIndex < this.results.length) {
this.selectResult(this.highlightedIndex);
}
},
selectResult(selectedIndex) {
let links = document.querySelectorAll('#searchresults a');
links[selectedIndex].click();
this.$nextTick(() => {
this.results = []; // Clear the results
this.highlightedIndex = -1; // Reset the highlighted index
this.query = ''; // Clear the query
});
}
Detta är valt genom att klicka på länken i sökresultatet.
<a
x-on:click="selectResult(index)"
:hx-get="result.url"
hx-target="#contentcontainer"
hx-swap="innerHTML"
hx-push-url="true"
x-text="result.title"
>
</a>
Vilket sedan laddar sidan och uppdaterar URL: s korrekt.
Jag har också kod i moderboksen som låter dig använda pilspetsar och svara:.
<label class="input input-sm bg-neutral-500 bg-opacity-10 input-bordered flex items-center gap-2">
<input
type="text"
x-model="query"
x-on:input.debounce.200ms="search"
x-on:keydown.down.prevent="moveDown"
x-on:keydown.up.prevent="moveUp"
x-on:keydown.enter.prevent="selectHighlighted"
placeholder="Search..."
class="border-0 grow input-sm text-black dark:text-white bg-transparent w-full"/>
<i class="bx bx-search"></i>
</label>
Du kommer att se att det här har all koden som behövs för att kunna klicka och navigera till den markerade sidan.
I slutsats
Bara en snabb uppdaterade artikel till den existerande sökdownen för att förbättra användarupplevelsen när man använder sökningen. Återigen är detta en MINIMAL användar som står inför förändring men som bara förbättrar användar upplevelsen ; som som webutvecklare är ditt huvudsakliga intresse M SK2 förutom att få betalt :)).