Phase 1 Project
Carl J
Posted on March 28, 2023
Here's my post about my html app.
My original idea was to do something related to medical diseases where the app would output a list of possible diseases, given a set of symptoms. I was hoping to possibly mimic WebMD or Mayo Clinic websites.
The API available to me was Lexigram Clinical NLP APIs (https://docs.lexigram.io/). Instructions to use it felt overwhelming to me and it required an API key, so I looked for another topic.
Anyway, I went with Dad Jokes. It can't get any simpler than this, right?
The api page: Dad Joke Link. I'll come back to this.
Project Start
So the project... let's look at the README.md
Some goals:
- Design and architect features across a frontend (I'm guessing the look and feel, and style of the html page)
- Communicate and collaborate in a technical environment (??)
- Integrate JavaScript and an external API (k got it)
- Debug issues in small- to medium-sized projects (sure)
- Build and iterate on a project MVP
Actually starting the project
Like many beginners in coding, I did not know where to start. Thankfully I can use pseudo code to describe what it is I want the app to do.
Essentially, I wanted the app to:
- Have a picture background related to the joke displayed
- Get a random joke. Preferably when page loads.
- Have a random button that produces a random joke.
- Have users search for a joke, which means the background should also change to the searched term.
- Have a search button exist.
- Search button does something when mouse is hovering over the buttons.
- Fetch a random joke if the search bar is empty.
- Save the joke onto a list of favorite jokes.
.html, .css, and .js documents were made.
touch `index.html`, touch `style.css`, and `script.js`
### HTML
When I type !
in he html document, I get a drop-down list containing pre-written set of html code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>
Designing the HTML and CSS, in a way is like creating an outline of what the app would do.
I know for sure that I would be using /.script.js for functions and /.style.css for page design. I did this by putting in links as below:
<!--STYLE-->
<link rel="stylesheet" href="./style.css">
<!--END OF STYLE-->
<!--JS SCRIPT-->
<script src="./script.js" defer></script>
<!--END OF JS SCRIPT-->
For example I want the fonts in the app to include:
- Gloria Hallelujah
- Open Sans
- Permanent Marker
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Gloria+Hallelujah&family=Open+Sans&family=Permanent+Marker&display=swap" rel="stylesheet">
Time for the Inputs (all inside the body)
Within the <body>
that was created, I had to insert a <div>
and <input>
within the card. And then I should be able to type some search item in the search box. Looks like so:
<body style="overflow:scroll">
<div class="card">
<div class="search">
<input type="text" class="searchBar">
But I need a button...
For this button, I decided to insert a photo search icon on it, so I used React Icons by opening up a Command Palette.
<button id="randomButton">
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 496 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
<path d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm33.8 161.7l80-48c11.6-6.9 24 7.7 15.4 18L343.6 180l33.6 40.3c8.7 10.4-3.9 24.8-15.4 18l-80-48c-7.7-4.7-7.7-15.9 0-20.6zm-163-30c-8.6-10.3 3.8-24.9 15.4-18l80 48c7.8 4.7 7.8 15.9 0 20.6l-80 48c-11.5 6.8-24-7.6-15.4-18l33.6-40.3-33.6-40.3zM398.9 306C390 377 329.4 432 256 432h-16c-73.4 0-134-55-142.9-126-1.2-9.5 6.3-18 15.9-18h270c9.6 0 17.1 8.4 15.9 18z">
</path>
</svg>
</button>
Next is the space/div for where the code will be displayed.
<div class="jokes">
<h2 class="theJoke">Here comes the Joke...</h2>
<div class="jokeList" >
<h4>All Jokes with searched topic</h4>
</div>
</div>
Style Sheet
General Header and body styling:
html {
/* Background image? */
background: url(https://source.unsplash.com/random/?father+child) no-repeat center center fixed;
background-size: cover;
/* End background image */
height: 100%;
overflow: hidden;
}
/* specify design of body of page */
body {
display: flex;
justify-content: center;
height: 100vh;
margin: 0;
align-items: center;
align-content: center;
/* Fonts here */
font-family: 'Open Sans', sans-serif;
font-family: 'Permanent Marker', cursive;
font-family: 'Gloria Hallelujah', cursive;
}
The header is where I declared a random image to be used when the page loads.
The rest of the .css design was basic and I used the Chrome inspector to see changes in the html in real time.
Javascript
This was probably the hardest part of the project for me.
We start with the main function of joke function getJoke () {}
and in this function we have our fetch
and .then
s.
The fetch uses the url, "https://icanhazdadjoke.com/" from the Dad Jokes API. This url apparently outputs a random Dad Joke. But let's test it! To see what kind/type/nature of data I get, I used console.log
of the data output. The .then
s were using arrow functions at this time. This is the code:
function getJoke () {
fetch ("https://icanhazdadjoke.com/")
.then ( (response) => response.json() )
.then ( (data) => console.log(data))
}
Output:
SyntaxError: Unexpected token < in JSON at position 0
So we got an error!
What I realized was that I needed to add a header. As mentioned in the Dad Joke API instructions, I needed a header in the fetch
to Accept: application/json
. So I tried again...
function getJoke () {
fetch ("https://icanhazdadjoke.com/", {
headers: {
'Accept': 'application/json'
}
})
.then ( (response) => response.json() )
.then ( (data) => console.log(data))
}
Which produced an output:
{
id: 'lV8hqHexHBd',
joke: 'Why did the worker get fired from the orange juice factory? Lack of concentration.',
status: 200
}
Ok great! I used this for a function:
- triggered by a button labeled "Random"
- produces a random Dad joke
- that is called on when a search value is blank Like so:
getRandom: function () {
fetch("https://icanhazdadjoke.com/",
{
headers: {
'Accept': 'application/json'
}
})
.then((response) => response.json())
.then((randJoke) => this.displayJoke(randJoke))
.catch((error) => console.log("error"))
}
The displayJoke
function would print out the joke in the section with the class name theJoke
.
displayJoke: function (data) {
const joke = data.joke;
document.querySelector(".theJoke").innerText = joke;
}
But, what if I wanted a joke that included values I typed in the search box?
The Dad joke API source had instruction to use the link, "https://icanhazdadjoke.com/search?term="
, in order to produce jokes with the search item. The default for "term" would list all the jokes available. Therefore, the variable "searchTerm" would be used as the input typed in the search box.
let joke = {
getJoke: function (searchTerm) {
fetch("https://icanhazdadjoke.com/search?term=" + searchTerm + ""
, {
headers: {
'Accept': 'application/json'
}
})
.then((response) => response.json())
}
The "https://icanhazdadjoke.com/search?term="
AP, however, produced a list of jokes with the search term. So in order to display a joke, I wanted to randomize jokes in the list. I did this as a function in a .then
.
getJoke: function (searchTerm) {
fetch("https://icanhazdadjoke.com/search?term=" + searchTerm + ""
, {
headers: {
'Accept': 'application/json'
}
})
.then((response) => response.json())
.then((data) => {
let randNum = Math.floor(Math.random() * data.results.length);
return this.displayJoke(data.results[randNum])
})
}
Now when I created the function searchJoke
, it would use call the function getJoke
and produce a random joke from the list of searched jokes.
searchJoke: function () {
this.getJoke(document.querySelector(".searchBar").value);
document.querySelector(".searchBar").value = "";
},
I decided to put all these functions in an object so that I can call on these functions in dot format (i.e. joke.getJoke).
let joke = {
getJoke: function (searchTerm) {
fetch("https://icanhazdadjoke.com/search?term=" + searchTerm + ""
, {
headers: {
'Accept': 'application/json'
}
})
.then((response) => response.json())
.then((data) => {
let randNum = Math.floor(Math.random() * data.results.length);
return this.displayJoke(data.results[randNum])
})
},
//get a random joke (prefer when loading page)
getRandom: function () {
fetch("https://icanhazdadjoke.com/",
{
headers: {
'Accept': 'application/json'
}
})
.then((response) => response.json())
.then((randJoke) => this.displayJoke(randJoke))
.catch((error) => console.log("error"))
},
//search for a joke
searchJoke: function () {
this.getJoke(document.querySelector(".searchBar").value);
document.querySelector(".searchBar").value = "";
},
//display the joke searched
displayJoke: function (data) {
const joke = data.joke;
document.querySelector(".theJoke").innerText = joke;
}
}
Add Event Listeners
Great! so next to do was create addEventListener
s.
Created a random button and when clicked should execute getRandom
function.
var randButt = document.querySelector("#randomButton")
randButt.addEventListener("click", function () {
joke.getRandom();
});
addEventListener for search button
var searchButt = document.querySelector(".searchButton");
//search for a joke when search button clicked or tap or selected
searchButt.addEventListener("click", function() {
joke.searchJoke();
});
Mouse-over & Mouse-out
When mouse cursor is over the random button, it would display the text "Random Dad Joke" and when mouse out, shows an emoji icon:
randButt.addEventListener("mouseover", function () {
randButt.innerText = 'Random Dad Joke';
});
randButt.addEventListener("mouseout", function () {
randButt.innerHTML = '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 496 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm33.8 161.7l80-48c11.6-6.9 24 7.7 15.4 18L343.6 180l33.6 40.3c8.7 10.4-3.9 24.8-15.4 18l-80-48c-7.7-4.7-7.7-15.9 0-20.6zm-163-30c-8.6-10.3 3.8-24.9 15.4-18l80 48c7.8 4.7 7.8 15.9 0 20.6l-80 48c-11.5 6.8-24-7.6-15.4-18l33.6-40.3-33.6-40.3zM398.9 306C390 377 329.4 432 256 432h-16c-73.4 0-134-55-142.9-126-1.2-9.5 6.3-18 15.9-18h270c9.6 0 17.1 8.4 15.9 18z"></path></svg>';
});
The same goes for the search button:
//change search button to solid when mouse over
searchButt.addEventListener("mouseover", function () {
searchButt.setAttribute("style", "background-color:white;")
}, false);
//change search button back to original when mouse out
searchButt.addEventListener("mouseout", function () {
searchButt.setAttribute("style", "background-color:#7c7c7c2b;")
}, false);
I wanted to edit a couple things for the search box. When pressing "enter" or "return" an input, I want the search button to activate. But when the input box is empty, I want it to run the random joke function joke.getRandom()
.
//search for joke when press Enter key
document.querySelector(".searchBar").addEventListener("keyup", function (e) {
if (e.key === "Enter") {
e.preventDefault()
document.querySelector(".searchButton").click();
}
});
then added this to the search button event listener
//run random joke if search bar is empty
if (document.querySelector(".searchBar").value === null || "")
{
joke.getRandom();
}
Posted on March 28, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.