A simple but powerful search library for Ghost Blogging Platform.
Get the latest version of Ghost Content API Client Library from unpkg.com.
<script src="https://unpkg.com/@tryghost/content-api@{version}/umd/content-api.min.js"></script>
Add the script before the {{ghost_foot}}
tag. This will, most likely, be in default.hbs
.
Open your theme directory and navigate to assets
subdirectory.
Create a directory called js
, if there isn't already one in there, and add the minified version of ghost-search
in it.
Open default.hbs
that is located at the root of your theme.
At the bottom of this file you should see {{ghost_foot}}
.
Add the following code above it (after the content-api script at Step 1) and save it:
<script type="text/javascript" src="{{asset "js/ghost-search.min.js"}}"></script>
Add the following code, in a .hbs
file, where you want to show the search input:
<input id="ghost-search-field">
Add the following code, in a .hbs
file, where you want to show the search results:
<div id="ghost-search-results"></div>
You will need to initialize ghost-search
to make the search functional.
Add the following js code after you included ghost-search.min.js
:
<script>
let ghostSearch = new GhostSearch({
key: '22444f78447824223cefc48062', // This is just a demo key. Replace the key with a real one. See Step 3.
url: 'https://demo.ghost.io', // This is just a demo host. Replace the demo url with a real one. See Step 3.
})
</script>
Go in your Ghost's dashboard -> Integrations -> Add custom integration
Set a name: Haunted Themes Search
Get the Content API Key and replace the demo key with this one. More details.
Get the admin domain. This will be different in some cases. More details.
<script src="https://cdn.jsdelivr.net/npm/ghost-search@1.0.1/dist/ghost-search.min.js"></script>
npm install ghost-search
Set a basic search
Display a message if there are no posts found
Search only through tags
Search posts from a custom collection
Search posts that are published after 01 Jan 2018
Search through the title and content of posts
Get the results when a button is clicked
Get proper results when your Ghost is on a sub-path
Set multiple instances of ghost-search on the same page
Add a loading icon when you have a lot of posts
Limit the results displayed
{
host: '',
key: '',
version: 'v2',
input: '#ghost-search-field',
results: '#ghost-search-results',
button: '',
development: false,
defaultValue: '',
template: function(result) {
let url = [location.protocol, '//', location.host].join('');
return '<a href="' + url + '/' + result.slug + '/">' + result.title + '</a>';
},
trigger: 'focus',
options: {
keys: [
'title'
],
limit: 100,
threshold: -3500,
allowTypo: false
},
api: {
resource: 'posts',
parameters: {
limit: 'all',
fields: ['title', 'slug'],
filter: '',
include: '',
order: '',
formats: '',
},
},
on: {
beforeDisplay: function(){},
afterDisplay: function(results){},
beforeFetch: function(){},
afterFetch: function(){}
}
}
The url that needs to be set in order for Content API to properly authenticate. More details.
The key that needs to be set in order for Content API to properly authenticate. More details.
The version that needs to be set in order for Content API to properly authenticate. More details.
Default value: 'v3'
The ID of the input field that will be transformed into search filter.
You can set your own id if you want like this:
<input id="my-custom-input">
<script type="text/javascript">
let ghostSearch = new GhostSearch({
input: '#my-custom-input'
})
</script>
Default value: '#ghost-search-field'
The ID of the element that will be transformed into search results.
You can set your own id if you want like this:
<div id="my-custom-results"></div>
<script type="text/javascript">
let ghostSearch = new GhostSearch({
results: '#my-custom-results'
})
</script>
Default value: '#ghost-search-results'
The ID of the element that will trigger the search results after it's clicked.
By default, the button parameter is empty because ghost-search
displays the results when you write in the input.
To make this work you need to add the input and the button in a form
element:
<form>
<input id="my-custom-input">
<input type="submit" id="my-custom-button">
</form>
<div id="my-custom-results"></div>
<script type="text/javascript">
let ghostSearch = new GhostSearch({
input: '#my-custom-input',
results: '#my-custom-results',
button: '#my-custom-button'
})
</script>
Default value: ''
A parameter that will set a default value for the input and performs the search.
<script type="text/javascript">
let ghostSearch = new GhostSearch({
defaultValue: 'ghost'
})
</script>
Default value: ''
The template that will be used to render individual items in the search result.
The method has a parameter result
that stores all the data that you can use inside the method.
Make the results a list and wrap each result with <li>
:
<div id="my-custom-input"></div>
<ul id="my-custom-results"></ul>
<script type="text/javascript">
let ghostSearch = new GhostSearch({
input: '#my-custom-input',
results: '#my-custom-results',
template: function(result) {
let url = [location.protocol, '//', location.host].join('');
return '<li><a href="' + url + '/' + result.slug + '">' + result.title + '</a></li>';
}
})
</script>
Set url with sub-path:
<script type="text/javascript">
let ghostSearch = new GhostSearch({
input: '#my-custom-input',
results: '#my-custom-results',
template: function(result) {
let url = [location.protocol, '//', location.host].join('') + '/sub-path/';
return '<a href="' + url + '/' + result.slug + '">' + result.title + '</a>';
}
})
</script>
Default value:
function(result) {
let url = [location.protocol, '//', location.host].join('');
return '<a href="' + url + '/' + result.slug + '">' + result.title + '</a>';
}
Tells the script when to fetch the collection of data. The default value is focus
, that means when a user clicks the input, all the data is fetched.
You can also use load
. This will fetch the data when the page loads.
load
might create a DDOS effect because it loads all the data every time a page loads. Use carefully.
Default value: 'focus'
ghost-search
is using fuzzysort
as an algorithm for search. The option
parameter supports all the options from fuzzysort.
By default, ghost-search
is showing the first 10 results and searches only based on title.
Let's try another example that will show the first 3 results and searches both title and the content of a collection:
<script type="text/javascript">
let ghostSearch = new GhostSearch({
options: {
keys: [
'title',
'plaintext'
],
limit: 3,
},
api: {
resource: 'posts',
parameters: {
fields: ['title', 'slug', 'plaintext'],
formats: 'plaintext',
},
},
})
</script>
Default value:
{
keys: [
'title'
],
limit: 10,
threshold: -3500,
allowTypo: false
}
The api parameter is an object that supports most of the resources and parameters by Content API.
Resources: posts, tags, authors
Parameters: fields, filter, include, order, formats, limit
Examples:
Search through tags:
<script type="text/javascript">
let ghostSearch = new GhostSearch({
options: {
keys: [
'name',
],
},
api: {
resource: 'tags',
parameters: {
fields: ['name', 'slug'],
},
},
template: function(result) {
let url = [location.protocol, '//', location.host].join('') + '/tag';
return '<a href="' + url + '/' + result.slug + '/">' + result.name + '</a>';
},
})
</script>
Search through a custom collection:
Let's say we have a routes.yaml
like this:
routes:
collections:
/themes/:
permalink: /themes/{slug}/
filter: tag:themes
data: tag.themes
/:
permalink: /{slug}/
filter: tag:-themes
template:
- index
taxonomies:
tag: /tag/{slug}/
author: /author/{slug}/
/themes/
is a collection that will show posts with tag themes
. A post like this will have the url example.com/themes/post-slug
.
Our ghost-search
will become:
let ghostSearch = new GhostSearch({
options: {
keys: [
'title',
],
},
api: {
resource: 'posts',
parameters: {
fields: ['title', 'slug'],
filter: 'tags:[themes]',
include: 'tags'
},
},
template: function(result) {
let collection = 'themes';
let url = [location.protocol, '//', location.host].join('') + '/' + collection;
return '<a href="' + url + '/' + result.slug + '/">' + result.title + '</a>';
},
})
This parameter has 4 methods in it: beforeDisplay
, afterDisplay
, beforeFetch
, afterFetch
.
afterDisplay
and afterFetch
have a parameter results
that contains the results fetched.
They are useful to do things before results are visible to users.
Example:
let ghostSearch = new GhostSearch({
on: {
beforeFetch: function(){
// Create a div that has a spinning icon
console.log('Loading appears');
},
afterFetch: function(results){
// Remove the spinning icon
console.log('Loading disappears');
}
}
})
All changes should be committed to src/
files only.
- DDOS effect when
trigger
is set toload
. If you have a lot of posts and settrigger
toload
you might get a DDOS effect because you are loading all the post everything a page loads. It would be better to just settrigger
tofocus
.
- Migrated to Ghost v3
- Editable limit parameter. 5
- Public API (deprecated) will not work anymore. The library is accessing Content API.
- Added
defaultValue
parameter.
- Initial release
ghost-search is using as a search algorithm fuzzysort.
Thank you farzher for creating fuzzysort, a simple and usable search library.
Copyright (c) 2019 Haunted Themes - Released under the MIT license.
Ghost is a trademark of The Ghost Foundation