Code to automatically generate a search bar in JS from XML page content in Hugo

hugo js r

NB: This is just a place to share code. You might have to adjust it to suit the XML file generated by your static site generator. Please do reach out via email if you want to learn more.

Step 1: Create a JSON file with search entries from XML feed via R.

library(purrr)
library(dplyr)
library(stringr)

XML::xmlParse("./docs/index.xml") %>%
    XML::xmlToList() %>%
    (function(x) x$channel) %>%                 # get entries
    (function(x) x[lapply(x, length)>4]) %>%    # get true entries
    map(., as.data.frame) %>%                   # cast to df
    do.call(rbind, .) -> d

# Clean entries

d <- d %>% 
    filter(title!="Flying DVD (JS)") %>%
    mutate_if(is.character, function(x) sapply(x, utils::URLdecode)) %>%   # %20 etc
    mutate_if(is.character, function(x) textclean::replace_html(x)) %>%    # &amp etc
    mutate_if(is.character, function(x) str_replace_all(x, "\n", " ")) %>% # verbatim \n
    mutate_if(is.character, function(x) base::trimws(x)) %>%    # redundant whitespace
    mutate_if(is.character, function(x) str_squish(x)) %>%      # redundant whitespace
    mutate_if(is.character, function(x) str_remove_all(x, "[\"\'{}]")) %>% # parse correctly
    mutate_if(is.character, function(x) str_remove_all(x, "[\r\n]"))       # line breaks

# Sample Entry

#title: "Reactive Word and Character Counter",
#url: "/project/js_wc/",
#date: "",   
#content: "word character counter count char text length", 
#summary: "Word Count: 0"

# cat()

cat_entry <- function(d, i) {
    cat('title: \"', d$title[i], '\",\n', sep="")
    cat('url: \"', d$link[i], '\",\n', sep="")
    cat('date: \"\",\n', sep="") # I personally find the dates unnecessary
    cat('content: \"', d$description[i], '\",\n', sep="") # search tokens
    cat('summary: \"', d$description[i] %>% str_trunc(500), '\"\n', sep="")  # visible
}

sink("./themes/min_night/layouts/partials/search_bar_entries.html")
cat("<script>\n\nvar searchIndex = [")
for (i in 1:nrow(d)){
    cat("{\n")
    cat_entry(d, i)
    if (i!=nrow(d)) cat("},\n") else cat("}\n") # last entry
}   
cat("];;\n</script>")
sink()

Step 2: Use an open source script and embed it in a Hugo shortcode together with the content created above.


	<h3>Search this Website</h3>
	
	<form
        action="https://www.google.com/search?q=site%3cborchers.com&amp;oq=site%3cborchers.com" id="form-search">
	<label class="screen-reader" for="input-search"></label>
	
	<input type="text" name="q" class="input-search input-inline
    no-margin-bottom" id="input-search" placeholder="Search for..."><button type="submit" class="btn-search" id="submit-search">
		<svg xmlns="http://www.w3.org/2000/svg" style="height: 1em; width: 1em;" viewBox="0 0 32 32" aria-labelledby="search-title"><title id="search-title">Search</title><path fill="currentColor" d="M31.008 27.23l-7.58-6.446c-.784-.705-1.622-1.03-2.3-.998C22.92 17.69 24 14.97 24 12 24 5.37 18.627 0 12 0S0 5.37 0 12c0 6.626 5.374 12 12 12 2.973 0 5.692-1.082 7.788-2.87-.03.676.293 1.514.998 2.298l6.447 7.58c1.105 1.226 2.908 1.33 4.008.23s.997-2.903-.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"></path></svg>
	</button>
</form>

<div id="search-results" aria-live="polite"></div>

</article>
{{ partial "search_bar_entries" . }}
			</div>
		</main>


		<script>
			/*! gmt v2.0.0 | (c) 2020 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/go-make-things | Credits: https://github.com/toddmotto/fluidvids */
!function(){"use strict";Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector);document.querySelectorAll(".edd-buy-now-button").length>0&&document.addEventListener("click",(function(e){e.target.classList.contains("edd-buy-now-button")&&(0!==e.button||e.metaKey||e.ctrlKey||(e.target.innerHTML="Adding to cart...",e.target.classList.add("disabled")))}),!1);var e={selector:["iframe"],players:["www.youtube.com","player.vimeo.com"]},t=[".fluidvids {","width: 100%; max-width: 100%; position: relative;","}",".fluidvids-item {","position: absolute; top: 0px; left: 0px; width: 100%; height: 100%;","}"].join(""),n=document.head||document.getElementsByTagName("head")[0],a=function(t){if(n=t.src,new RegExp("^(https?:)?//(?:"+e.players.join("|")+").*$","i").test(n)&&!t.getAttribute("data-fluidvids")){var n,a,i,s=document.createElement("div");t.parentNode.insertBefore(s,t),t.className+=(t.className?" ":"")+"fluidvids-item",t.setAttribute("data-fluidvids","loaded"),s.className+="fluidvids",s.style.paddingTop=(a=t.height,i=t.width,parseInt(a,10)/parseInt(i,10)*100+"%"),s.appendChild(t)}};e.render=function(){for(var t=document.querySelectorAll(e.selector.join()),n=t.length;n--;)a(t[n])},e.init=function(a){for(var i in a)e[i]=a[i];var s;e.render(),(s=document.createElement("div")).innerHTML="<p>x</p><style>"+t+"</style>",n.appendChild(s.childNodes[1])};e.init({selector:["iframe","object"],players:["www.youtube.com","player.vimeo.com","www.slideshare.net","www.hulu.com","videopress.com/embed/","noti.st"]}),document.querySelector("#mailchimp-form")&&function(e){var t=document.querySelector("#mailchimp-form");if(t){var n=t.querySelector("#mailchimp-email");if(n){var a=t.querySelector("#mc-status"),i=t.querySelector("[data-processing]"),s="Please provide an email address.",o="Please use a valid email address.",r="Success! Thanks for inviting me to your inbox.",c=function(e,t){a&&(a.textContent=e,t?(a.className="success-message",n.className=""):(a.className="error-message",n.className="error"))},x=function(){var n,a;t.setAttribute("data-submitting",!0),n=function(e){for(var t=[],n=0;n<e.elements.length;n++){var a=e.elements[n];a.name&&!a.disabled&&"file"!==a.type&&"reset"!==a.type&&"submit"!==a.type&&"button"!==a.type&&("checkbox"!==a.type&&"radio"!==a.type||a.checked)&&t.push(encodeURIComponent(a.name)+"="+encodeURIComponent(a.value))}return t.join("&")}(t),(a=new XMLHttpRequest).onreadystatechange=function(){if(4===a.readyState){var n=200===a.status,i=JSON.parse(a.responseText);c(n?r:i.message,n),t.removeAttribute("data-submitting"),e&&"function"==typeof e&&e(i)}},a.open("POST","https://gomakethings.com/checkout/wp-json/gmt-mailchimp/v1/subscribe?"+n),a.send()},d=function(){return n.value.length<1?(c(s),!1):!!/^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*(\.\w{2,})+$/.test(n.value)||(c(o),!1)};a&&a.setAttribute("role","status"),t.addEventListener("submit",(function(e){(e.preventDefault(),t.hasAttribute("data-submitting"))||(a&&c(i.getAttribute("data-processing"),!0),d()&&x())}),!1)}}}((function(e){200===e.code&&(window.location.href="https://gomakethings.com/newsletter-success")})),document.body.matches(".type-articles.page-single, .type-notes.page-single, [data-heading-links]")&&function(e,t,n){if(e){var a=document.querySelectorAll(e);t=t||"#",n=n||"anchor-link";for(var i=0;i<a.length;i++)a[i].id&&(a[i].innerHTML+=' <a class="'+n+'" href="#'+a[i].id+'">'+t+"</a>")}}("h2, h3, h4, h5, h6","#","link-no-underline")}();

			/*! gmt v2.0.0 | (c) 2020 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/go-make-things | Credits: https://github.com/toddmotto/fluidvids */
!function(){"use strict";var e,t,n,i,r;e=function(e,t){return'<div class="margin-bottom" id="search-result-'+t+'"><aside class="text-muted text-small"><time datetime="'+e.datetime+'" pubdate>'+e.date+'</time></aside><h2 class="h3 link-block-styled link-no-underline no-padding-top no-margin-bottom"><a class="link-no-underline" href="'+e.url+'">'+e.title+"</a></h2>"+e.summary.slice(0,150)+"...</div>"},t=document.querySelector("#form-search"),n=document.querySelector("#input-search"),i=document.querySelector("#search-results"),r=function(t){var n=t.split(" ").map((function(e){return new RegExp(e,"gi")})),r=searchIndex.reduce((function(e,t,i){var r=0;return n.forEach((function(e){e.test(t.title)&&(r+=20),e.test(t.content)&&(r+=1)})),r>0&&e.push({priority:r,article:t}),e}),[]).sort((function(e,t){return t.priority-e.priority}));i.innerHTML=r.length<1?"<p>Sorry, no matches were found.</p>":function(t){var n="<p>Found "+t.length+" matching pages</p>";return n+=t.map((function(t,n){return e(t.article,n)})).join("")}(r),function(e){history.pushState&&history.pushState({},document.title,window.location.origin+window.location.pathname+"?s="+encodeURI(e))}(t)},e&&"function"==typeof e&&t&&n&&i&&searchIndex&&(n.value=n.value.replace(" site:"+window.location.host,""),t.addEventListener("submit",(function(e){e.preventDefault(),r(n.value)}),!1),function(){var e,t,i,o,a=(e="s",i=t||window.location.href,(o=new RegExp("[?&]"+e+"=([^&#]*)","i").exec(i))?o[1]:null);if(a){var c=decodeURI(a);n.value=c,r(c)}}())}();

			
			/*! gmt v2.0.0 | (c) 2020 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/go-make-things | Credits: https://github.com/toddmotto/fluidvids */
!function(){"use strict";navigator&&navigator.serviceWorker&&navigator.serviceWorker.register("/sw.js"),navigator.serviceWorker.controller&&window.addEventListener("load",(function(){navigator.serviceWorker.controller.postMessage("cleanUp")}))}();

		</script>

Questions? Thoughts? Generate a Comment to this Post!


Enter Name:


Enter a Title for Later Reference:


If Applicable, Enter Reply Reference:


Enter Comment:



JS Code to Create and Format Comments in Static Websites like Hugo

hugo js

Recent Blogging on Statistical Concepts

stats r

Code Snippet: How to Embed a Jupyter Notebook in Your Hugo Static Website

code-snippet hugo python

Search this Website