In this article, I will explain how I used JavaScript to build an image search app. It fetches and displays images from Unsplash based on the user's search query. This is one of several personal projects I've built to enable me to gain some practice on all I've been learning.
Setting up the markup and styles
I thought about what I wanted the page to look like and decided on a simple white page (as shown above) with the input and submit button at the top, and the results in a grid formation. You can view the whole code below:
Tackling the JavaScript
I first globally stored references to the form and some other HTML elements that I was going to work with. I would still select others within functions later. I also added a submit
event listener to the form.
const form = document.querySelector('.js-form');
form.addEventListener('submit', handleSubmit);
const nextBtn = document.querySelector('.js-next');
const prevBtn = document.querySelector('.js-prev');
let resultStats = document.querySelector('.js-result-stats');
let searchQuery;
Then I defined the callback function handleSubmit
in the event listener as shown below:
function handleSubmit(event) {
event.preventDefault();
const inputValue = document.querySelector('.js-search-input').value;
searchQuery = inputValue.trim();
console.log(searchQuery);
fetchResults(searchQuery);
}
This function takes an event as its argument and first of all prevents the page from reloading using the preventDefault()
method. It then stores the value of the search input in inputValue
and removes any whitespace with the trim()
method. It stores the trimmed input value in searchQuery
and passes it as an argument to the fetchResults
function which is being called there. I logged the value of searchQuery
to the console to make sure the right value was being passed.
Fetch results from Unsplash
async function searchUnsplash(searchQuery) {
const endpoint = `https://api.unsplash.com/search/photos?query=${searchQuery}&client_id=YOUR_ACCESS_KEY`;
const response = await fetch(endpoint);
if (!response.ok) {
throw Error(response.statusText);
}
const json = await response.json();
return json;
}
async function fetchResults(searchQuery) {
try {
const results = await searchUnsplash(searchQuery);
console.log(results);
displayResults(results);
} catch(err) {
console.log(err);
alert('Failed to search Unsplash');
}
}
To be able to use Unsplash's API, you have to create a developer account. Only after that do you get your unique API key with which you can access the photos on the site. An AJAX request is made to Unsplash using a URL containing the endpoint and the relevant query parameters. More information on this is provided in the Documentation page on their website.
The function searchUnsplash
takes one parameter (searchQuery
), which is inserted into the endpoint as the value of the query
query parameter. Now, the URL is stored in a variable endpoint
which is passed as a parameter to fetch
. The fetch()
method takes one argument, the path to the resource you want to fetch, which is stored in endpoint
in this case. It always returns a Promise. Now, if the response is 200 OK, it is parsed as JSON which is stored in the json
variable. The result is logged to the console so as to view the contents of the JSON data.
Both functions above are asynchronous which means that the await
keyword can be used to pause the execution of the function until a promise is resolved. This is achieved by placing the async
keyword before declaring a function. I used a try...catch
block in the fetchResults
function. The try
block 'tries' to execute the code within it and, should there be an exception or error, the catch
block saves the day and responds appropriately with whatever code is written within it. This is a control flow mechanism which prevents the code from crashing if an error occurs while fetching the results.
Display the results on the page
The next thing is to display the results on the page. If you check the JSON data looged to the console, you will find that it contains several properties which have different contents.
The results
property is an array in which the search results are contained. Each search result is an object and can be accessed using either dot or bracket notation.
function displayResults(json) {
const searchResults = document.querySelector('.js-search-results');
searchResults.textContent = '';
json.results.forEach(result => {
const url = result.urls.small;
const unsplashLink = result.links.html;
const photographer = result.user.name;
const photographerPage = result.user.links.html;
searchResults.insertAdjacentHTML(
'beforeend',
`<div>
<a href="${unsplashLink}" target="_blank">
<div class="result-item" style="background-image: url(${url});"></div>
</a>
<p class="photographer-name">
<a href="${photographerPage}" target="_blank" style="color: black; text-decoration: none;">Photo by ${photographer}</a>
</p>
</div>`
);
});
totalResults = json.total;
resultStats.textContent = `About ${totalResults} results found`;
};
An empty div
with a class of search-results
was already created in the HTML file. It is then selected in the JS file within a new function called displayResults
which takes a JSON object as an argument. The textContent
is also set to an empty string to clear all previous results.
Now, the results
array is iterated over using the forEach
method. Within the callback function, the nested object can be accessed through the result
parameter. If you study the above image closely, you will find that each object in the array contains several keys such as links
, user
, urls
. These can be used to access different categories of information on the object in question.
The first lines within the callback function are variables in which the different values needed are stored. They were all accessed using dot notation and include:
- the image url
- the link to the image on Unsplash
- the name of the photographer
- the link to the photographer's Unsplash profile
Afterwards, each result is inserted into the searchResults
element using the insertAdjacentHTML method. This method takes two arguments: the position of the element, and the text to be parsed as HTML. Template literals are used in the second argument because of the parts of the code that will be changing constantly. These are represented by placeholders in the code. The function displayResults
is then called in fetchResults
.
Each image is set to be the background of its container, and is also a link to its Unsplash page. The name of the photographer, which links to his/her Unsplash profile, is placed right under the image and the result display was styled using CSS Grid.
Show a loading indicator
This is something to be displayed when a search query is being executed to let the user know that the operation is still in progress. I selected a spinner from this website and pasted the HTML and CSS into my code. A reference to the spinner was stored globally in a variable spinner
and then the fectchResults
function was updated as follows:
const spinner = document.querySelector('.js-spinner');
async function fetchResults(searchQuery) {
spinner.classList.remove('hidden');
try {
const results = await searchUnsplash(searchQuery);
pagination(results.total_pages);
console.log(results);
displayResults(results);
} catch(err) {
console.log(err);
alert('Failed to search Unsplash');
}
spinner.classList.add('hidden');
}
Conclusion
In this tutorial, we have seen how an interactive application can be built using JavaScript. I hope you learnt a lot from it.
I'm currently open to job offers. Any information on openings will be really appreciated.
Thanks for reading!
Top comments (1)
Hi Arthur,
Thanks a lot! I didn't know about it and I'm currently reading up on it.