Cleanly Converting an HTML/Javascript Searchpage to a File

Hi. Can someone please ELI5? (No, seriously! I want to be able to use Grav because of how light it is, but I’m losing my mind coming from Joomla because I’m not a strong coder)

I have a working songs.html live-search page using flexsearch.min.js and “songs.js” as my source. As I type in the search box, it pulls info from the songs.js file as anticipated and displays them neatly on my page. No issues there.

I can also use an iframe to inelegantly stick my html page into my, however I end up with the gross looking double page up/down slider on my browser if there are lots of results (which quite frequently there is as I end up with 30+ results just while searching for “Elvis”.)

I’ve tried putting pieces of my working songs.html directly into my 01.home/_songs/ file using divs, but while the searchbox is being seen, the javascript isn’t working so I’m not getting any results (my searchbox css looks like garbage, but that’s not my main concern right now)

The flexsearch.min.js is being called remotely from a CDN url and the songs.js (my database file) is located directly in the _songs folder (src="./songs.js"). Again, this works fine when using the songs.html page. I also tried downloading the flexsearch.min.js and calling it from the same directory as songs.js; also not working.

I’m begging for help trying to make this work without resorting to the ugly iframe?

I don’t know if I can do what I want from just inside my file or if I need to mess around with twig files or …???

If it’ll help, I can attach my code or provide a downloadable link to what I have…

As I’ve mentioned in another post, I’m willing to pay someone with real money for their time at this point because 90+% of my site is already working how I want it to.

I’m really trying to learn!!!

ANY help is really appreciated!!!

Hi @tlsnine

Let’s figure this out. Can you indeed post or add a link to the relevant parts of the code you have already?

OMG! Absolutely!! Here’s a link to the whole kit and kaboodle if that works…?

All the basics are in there (js files, html codes, etc).
If you run songsearch.html from the folder it should work.
If I need to provide more I certainly can!!

Thanks again!

EDIT: if it makes a difference, I’m using the mache skelton.

@tlsnine, Is this the result you are after?:

These are the steps I took in a default Grav installation using Quark:

  • Created folder ‘/pages/03.songs’ containing the following files:
    ├── runsearch.js  <-- will contain inline script from
    ├── songs.js
    └── style.css     <-- will contain inline style from
  • I ditched file ‘flexsearch.min.js’ because it is fetched from rawcdn.
  • Copied the inline script from ‘’ into ‘runsearch.js’.
    Needed to cleanup all sorts of debris, mostly caused by double html encoding of characters like <, >, &, "
  • Copied the inline style from ‘’ into ‘style.css’.
  • Removed debris and inline script/style from ‘’.

Below are the contents of ‘’, ‘runsearch.js’ and ‘style.css’

Click here for content of cleansed ''
title: Online Song Search
menu: SongSearch
<input type="text" id="autocomplete">
<input type="text" id="userinput" placeholder="Search Our Songs ...">
<div id="suggestions"></div>

<link href="/songs/style.css" type="text/css" rel="stylesheet">
<script src=""></script>
<script src="/songs/songs.js"></script>
<script src="/songs/runsearch.js"></script>
Click here for content of new 'runsearch.js'
(function () {
    var index = new FlexSearch({

        encode: "simple",
        tokenize: "reverse",
        suggest: true,
        cache: true

    for (var i = 0; i < songs.length; i++) {

        index.add(i, songs[i]);

    var suggestions = document.getElementById("suggestions");
    var autocomplete = document.getElementById("autocomplete");
    var userinput = document.getElementById("userinput");

    userinput.addEventListener("input", show_results, true);
    userinput.addEventListener("keyup", accept_autocomplete, true);
    suggestions.addEventListener("click", accept_suggestion, true);

    function show_results() {

        var value = this.value;
        var results =, 25);
        var entry, childs = suggestions.childNodes;
        var i = 0, len = results.length;

        for (; i < len; i++) {

            entry = childs[i];

            if (!entry) {

                entry = document.createElement("div");

            entry.textContent = songs[results[i]];

        while (childs.length > len) {


        var first_result = songs[results[0]];
        var match = first_result && first_result.toLowerCase().indexOf(value.toLowerCase());

        if (first_result && (match !== -1)) {

            autocomplete.value = value + first_result.substring(match + value.length);
            autocomplete.current = first_result;
        else {

            autocomplete.value = autocomplete.current = value;

    function accept_autocomplete(event) {

        if ((event || window.event).keyCode === 13) {

            this.value = autocomplete.value = autocomplete.current;

    function accept_suggestion(event) {

        var target = (event || window.event).target;

        userinput.value = autocomplete.value = target.textContent;

        while (suggestions.lastChild) {


        return false;
Click here for content of new 'style.css'
    margin:0 10px;
    width: 300px;
    table-layout: fixed;
    position: fixed;
    top: 0;
    padding-top: 10px;
    background-color: #fff;
    z-index: 1;
td, tr{
    width: 300px;
    border: none;
    width: 300px;
    border: 1px solid #ddd;
    border-radius: 4px;
    outline: none;
    background-color: transparent;
    position: absolute;
    -webkit-appearance: none;
    color: #999;
    background-color: #f5f5f5;
    padding:7px 5px;
    box-sizing: border-box;
    position: relative;
    top: 50px;
#suggestions div{
    padding: 10px 0;
    margin: 0 8px;
    border-bottom: 1px solid #ddd;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
@media only screen and (max-width: 600px) {
        width: 97%;


  • Hope this is ELI5 enough… :wink:
  • You might want to undup the songs list: ‭12362‬ out of 50852 are duplicates.
  • I’m not sure if ‘flexsearch’ is the ideal solution:
    • Everything (including the songs list) needs to be fetched into the browser.
      The current solution adds about 4sec to load the page.
    • Do you really require the full-text search capabilities?
    • I wonder if querying a SQLite db using Ajax requests would be a more performant solution…
  • Do I like the result? No, but this is probably the simplest way to understand…
    Ideally, once you improved your Grav coding skills you could consider:
    • Content should be separated from layout and scripts/styles. The template should define the layout.
    • Scripts and styles should be added by the template (or plugin) using the Asset Manager.
    • Turning this into a plugin would be a cleaner solution…

Notwithstanding above notes, at least you have a working solution…


Holy sweet Jeebus!! It’s alive!!!
Thank you SO VERY MUCH! (sorry for the yelling)
Initially it did NOT work for me while I used ATOM as my editor. I’m not sure if that’s been part of the problem all along where ATOM is reformatting my code to suite it’s own needs?
Regardless, I redid the files using TextWrangler and it runs fantastically!!!
The only thing I need to do is centre the searchbox.
Thank you again for taking time out to show and explain to me. I’m very smart once I know a concept, but I have a learning disability that sometimes turns these little things into really big deals for me lol

If you have a account or something, I’d gladly pass on a few dollars for your time and kindness… Please let me know!

It’s now 3:30AM where I live and need to go back to bed. Just had to try it lol

So happy!! :slight_smile:

EDIT: and yes I realize there are a lot of duplicates. A) it’s just a test dataset B) the data is actually karaoke songs and I pulled out the “ID” row where the song maker would have their sku (and some people like different versions of the songs so I’m going to put it back)

I also agree that sqlite would be more graceful, but I’m stuck with that too :stuck_out_tongue: This will work very well for now until I learn more.