Kestrels FAQ-side var vokset så meget, at kunderne scrollede forbi det svar, de ledte efter. Butiksejeren sendte os en konkurrents hjælpeside med en søgeboks i toppen og bad om det samme: skriv et ord, og se kun de spørgsmål, der passer.
Dette er historien om den Shopify FAQ-søgning: snippetten, fejlen der fik første version til at finde intet i den rigtige butik, og rettelsen der viste sig at være en lektion om inline-scripts i Shopify-temaer.
Om dette casestudie: det er en sammensat historie baseret på rigtigt Shopify-arbejde. Brandnavnet og visse detaljer er ændret. Koden, fejlen og rettelsen er præcis, som det skete.

Hvorfor ikke bruge Shopifys egen søgning?
Shopifys søgning i butikken dækker produkter, kollektioner, sider og blogindlæg. Tekst, som butiksejeren skriver i temaets sektionsblokke, hvor de fleste FAQ-sektioner gemmer deres spørgsmål, er ikke en del af det indeks. En kunde, der søger efter “retour”, finder produkter, ikke svaret om returnering.
FAQ-svarene ligger dog allerede i sidens HTML. At filtrere dem i browseren kræver intet kald, ingen app og intet indeks. Resultaterne vises, mens kunden stadig skriver.
Version ét: en snippet og én render-linje
Butiksejeren redigerer temaet i kodeeditoren, så vi holdt ændringen så lille som muligt: én ny snippet med markup, styles og script, og én linje i FAQ-sektionen, der viser den.
{%- render 'faq-search', section: section -%}
<div class="faqs ..."> <!-- the theme's existing FAQ markup -->
Scriptet er et lille custom element. Ved hver indtastning gør det fire ting:
- Normaliserer teksten: små bogstaver og uden accenter, så “e” også finder “é”.
- Deler søgningen op i ord og beholder kun et spørgsmål, hvis det indeholder dem alle, i titlen eller i svaret.
- Skjuler en hel kategori inklusive overskrift, når ingen af dens spørgsmål passer.
- Viser en “ingen spørgsmål fundet”-besked med en opfordring til at kontakte support, når intet passer.
Vi testede den mod en kopi af FAQ-markuppen i en headless browser. Alle søgninger gav de rigtige spørgsmål. Vi sendte den afsted.
Skærmbilledet
Svaret kom med et skærmbillede. Butiksejeren havde indsat en hel sætning fra et af svarene i søgeboksen. Siden viste “ingen spørgsmål fundet”, og hvert eneste spørgsmål var stadig synligt nedenunder. Intet var skjult, og intet var fundet.
Begge symptomer pegede på det samme: scriptet troede, at siden ikke havde nogen spørgsmål. Vi åbnede den rigtige side og spurgte elementet direkte. Siden havde to FAQ-grupper med syv spørgsmål. Søgeelementets egen liste over grupper var tom.
Årsagen: et inline-script, der kører for tidligt
Snippetten vises over spørgsmålene, og dens <script>-tag ligger inde i snippetten. Browseren læser HTML oppefra og ned. Når den når scriptet, defineres custom elementet, og fordi <faq-search> allerede er i dokumentet, kører dets connectedCallback med det samme. På det tidspunkt er parseren endnu ikke nået til spørgsmålene nedenunder.
Vores første version slog FAQ-grupperne op i connectedCallback og gemte dem. Den gemte en tom liste, én gang, for altid. Vores test bestod, fordi testsiden tilfældigvis havde scriptet efter spørgsmålene.

Rettelsen er én getter. I stedet for at gemme grupperne, når elementet tilknyttes, slår scriptet dem op, hver gang kunden skriver:
get groups() {
return Array.from(this.closest('.section').querySelectorAll('.faq'));
}
Når nogen begynder at skrive, er siden færdiglæst. Opslaget koster næsten intet for et par dusin spørgsmål, og det bliver ved med at virke, når temaeditoren gentegner sektionen, mens butiksejeren redigerer, hvilket en gemt liste ikke ville.
En anden, mere stille fejl
Mens vi flyttede koden til den endelige snippet, fandt vi endnu et problem. CSS-reglen, der skjulte filtrerede elementer, var skrevet som .faq [data-faq-hidden] med et mellemrum. Den skjuler spørgsmål inde i en gruppe, men ikke selve gruppen, så en kategorioverskrift kunne blive stående uden noget under. Reglen dækker nu begge dele: .faq [data-faq-hidden], .faq[data-faq-hidden].

Test, hvor det rent faktisk kører
Denne gang testede vi på selve den rigtige FAQ-side ved at køre den rettede logik i browseren mod den ægte markup, før vi bad butiksejeren ændre noget.
| “levertijd” | Kun spørgsmålet om leveringstid |
| “garantie” | Garantispørgsmålene fra to forskellige kategorier |
| “€70 gratis” | Kun spørgsmålet om fragt, fundet via svaret |
| “retour” | Kun spørgsmålet om returnering, den anden kategori skjult |
| “xyz” | Intet, med beskeden “ingen spørgsmål fundet” |
Det tog vi med os
- Test i den rækkefølge, den rigtige side bruger. Et custom element, der defineres i et inline-script, ser kun HTML’en over sig.
- Gem ikke DOM-opslag i
connectedCallback, medmindre du ved, at indholdet allerede findes. En getter er enklere end at vente påDOMContentLoadedog overlever gentegninger i temaeditoren. - Når en butiksejer sender et skærmbillede, så læs symptomerne sammen. “Ingen resultater” plus “intet skjult” indsnævrede fejlen, før vi åbnede konsollen.
Søgningen blev senere en del af en hel hjælpeside; det skrev vi om i vores casestudie om en Shopify FAQ-sideskabelon. Tilbuddet på produktsiden, vi byggede til samme butik, finder du i køb én, få én gratis-vælgeren. Har din FAQ eller en anden del af dit tema brug for den slags arbejde, så se vores Shopify-temaudvikling.
Ofte stillede spørgsmål
Skader en FAQ-søgelinje SEO?
Nej. Alle spørgsmål og svar bliver i HTML’en. De skjules kun i kundens browser, efter at kunden har skrevet en søgning.
Skal jeg bruge en app for at få søgning på en Shopify FAQ-side?
Nej. Fordi spørgsmålene allerede er på siden, kan et lille script filtrere dem. Det er hurtigere end en app og koster intet om måneden.
Virker det med en stor FAQ?
Ja. At filtrere et par hundrede spørgsmål i browseren sker øjeblikkeligt. Har du flere, er det som regel bedre for kunden at dele FAQ’en op på flere sider.
Søger den i svarene og ikke kun i spørgsmålene?
Ja. Hvert spørgsmål matches på titel og svartekst uden accenter, og alle ord i søgningen skal forekomme.