Select Page

F[Part of the Speaking Javascript project.]

This is the second of two vanilla JS projects I did for my recent front end developer course on Skillcrush.com. I’m going to describe how the user experiences the program. Then I will create a logical overview of the program by listing the DOM variables, the global variables, and the functions; and explaining how those components work together to create the user’s experience. Then I will write out each function and explain how it works internally, as well as its generic functionality (how it can be used in other projects).

The idea is to start growing a sense for how all the pieces of JS programs fit together to create user experiences.

User Experience

The top part of the display has my name, bio, location, and number of public repositories. The bottom part lists the names of all my repos. There is a search function that eliminates the names of all the non-matching repositories from the list. If you click on the name of a repository, the bottom part doesn’t show any repositories, but instead displays that repository’s name, description, default branch, and languages. And below that info about the selected depository there are two buttons: One takes you to that repo’s GitHub page; the other takes you back to the initial display.

DOM variables

overview – connects to info about the GitHub repo owner
repoList – connects to a ul of all the repos that we are currently displaying
allRepos – connects to all the repos
repoData – connects to info about the selected repo (initially hidden; is unhidden when user selects a specific repo)
backToRepos – connects to the button that takes you back to all the repos (initially hidden; is unhidden when user selects a specific repo)
filterInput – connects to the search field

Global Variables

const username = “werdnamac”

Question: Why is this a constant, but the global variables in the guess-the-word program were all let? I mean, this variable never changes, and the ones in guess-the-word do, so I guess that’s a pretty simple explanation.

Functions

getUserInfo – Uses the global variable (username), so no parameter needed.  An async function that fetches info about the GitHub owner, converts the info into JSON, and then sends it to the displayInfo function. 

displayInfo – Accepts the userInfo parameter from getUserInfo. It creates a div and fills the div with information about the user by call the API. That div is then appended to the overview variable / display-area.

getRepoInfo – Uses the global variable (username), so no parameter needed. An async function that fetches the info about all users, converts the info to JSON, and sends it to displayRepoInfo. 

displayRepoInfo – Accepts the information from getRepoInfo and uses it to make a list of all repo names and append that list to the variable / display-area (unordered list) repoList. But the first thing it does is unhide the search bar field. I am not sure why it was hidden in the first place. It isn’t hidden when the page first loads. But it does get hidden if you click on a repo. So when you go back to displaying all the repos it makes sense to unhide it then. The last thing the function does is 

repoList.addEventListener – If a repo name is clicked on, then that repoName variable is passed to the getRepoDetails function.

getRepoDetails – Accepts repoName parameter from repoList.addEventListener, and uses it to fetch info about that repo from the API. Creates and fills an array with all the languages used in the repo.  Passes repoDetails and languageArray to displayRepoDetails.

displayRepoDetails – accepts repoDetails and languageArray from getRepoDetails. Unhides repoData section and hides the allRepos section (to transition from showing all the repos to showing info about the selected info). Creates a div with info about the selected repo and appends it to repoData. Included in the div display is changes to the “visit” class combined with a link to the repo — this creates a button that the user can click to go to the repo in GitHub. Finally, the function unhides the backToRepos button. The selected repo is now on display.

filterInput.addEventListener(“input”, function(e)) – If text is entered into the search field, all repo names that contain that text are still displayed, but the other repo names are hidden. [Basic search function]

Discussion of App Flow

Let’s come back to this. Now I want to look at the code snippets

The Functions in Detail


//fetch user info from github
const getUserInfo = async function() {
const userRequest = await fetch(`https://api.github.com/users/${username}`);
const userInfo = await userRequest.json();
displayInfo(userInfo);
}

async await function fetching from an API. Converts fetched data to Json. 


// get user info for display
const displayInfo = function(userInfo) {
const userDiv = document.createElement("div");
userDiv.classList.add("user-info");
userDiv.innerHTML =
BACKTICK (messes up this page's formatting when I leave the symbol in place)

Then comes figure tags and this image: img alt=”user avatar” src=${userInfo.avatar_url} />

And then comes a div tag and details like this: <p><strong>Name:</strong> ${userInfo.name}</p> And then a closing div and another backtick.

And the code ends like this:

overview.append(userDiv);
getRepoInfo();
}

Creates a div tag with document.createElement. Adds the css class “user-info” to that div. Uses .innerHTML = to dictate the div’s content

The info for the div tags uses userInfo.APIKEYNAME like “.avatar_url” for the url. This userInfo variable holds a JSON file of all the info about the user of this GitHub account with the keys that allow one access to the different features like the user’s avatar image and etc. userInfo was passed to this function from the getUserInfo function.

This function ends by appending the div it created to the overview variable/display-area; and then calling the getRepoInfo() function 


// fetch names and info about all repos
const getRepoInfo = async function() {
const repoInfoRequest = await fetch(`https://api.github.com/users/${username}/repos?sort=updated&per_page=100`);
const repoInfo = await repoInfoRequest.json();
displayRepoInfo(repoInfo);
}

async await api fetch that calls info about all repos 
Does not take a parameter but is called by the displayInfo function (the whole machine starts when the displayInfo function is called at the beginning of the code)
The api parameters sort from most recently updated to least recently updated and show up to 100 repos on a page
The repo info is converted in a JSON file and passed as the repoInfo variable to displayRepoInfo


// display a search bar and all the repos
const displayRepoInfo = function (repoInfo) {
// first show the search bar
filterInput.classList.remove("hide");
//loop through all repo info and append to a list of repos
for (const repo of repoInfo) {
const repoItem = document.createElement("li");
repoItem.classList.add("repo");
repoItem.innerHTML = BACKTICKHERE
H3Tag${repo.name}ENDTAG
BACKTICKHERE
repoList.append(repoItem);
}
}

Called by getRepoInfo.

Why do they choose to show the search bar here? Because the search bar is displayed when all the repos are displayed and hidden when only one repo is highlighted.

To unhide a variable / display area, they use .classList.remove(“hide”)

Loops through every repo in the repoInfo file. How come the repoInfo file is automatically an array?
For every repo in repoInfo, they use document.createElement(“li”) to create a list item.
That list item is then given the “repo” class with .classList.add And the list item’s HTML is set (with .innerHTML) to the repo’s name.
Then this list item, with the correct list css class and the HTML content of the repo’s name, is appended to repoList 


//if a repo is selected
repoList.addEventListener("click", function(e) {
if (e.target.matches("h3")) {
const repoName = e.target.innerText;
getRepoDetails(repoName);
console.log(repoName);
}
});

repoList.addEventListener(“click”, function(e)) listens for someone to click on a list item in the repoList
how does it know to look for a list item and not the whole list? Because on the inside if e.target.matches(“h3”) selects for only parts of the list that have an h3 tag, and that is only the list items.
The function then captures the name of the selected repo with e.target.innerText (what’s inside the h3 tags)
And the name of the selected repo is then passed to the getRepoDetails function


// fetch info about a specific repo
const getRepoDetails = async function(repoName) {
const repoDetailsRequest = await fetch(`https://api.github.com/repos/${username}/${repoName}`);
const repoDetails = await repoDetailsRequest.json();
//fetch info about languages and put all languages in an array
const fetchLanguages = await fetch(repoDetails.languages_url);
const languageData = await fetchLanguages.json();
console.log(languageData);
const languageArray = [];
for (const key in languageData) {
languageArray.push(key);
}
displayRepoDetails(repoDetails, languageArray);
}

This function is called by the repoList event listener and is passed the name of the selected repo.
It is another async await fetch function. It uses the username and repoName to select all the info about a specific repo from the API
Why do we fetch the languages_url in this function instead of waiting until the next function?
In any case, the languages are converted to JSON and must be an array because they are broken down into another array with a for in loop

The for-in operator returns the keys of an object of array, whereas the for-of operator provides access to the values of these keys.

So this loop returns the keys of the languages_url, which must be the language names. What then are the values of the languageData array?

The function concludes by passing repoDetails and languageArray to the displayRepoDetails function.


// display repo details
const displayRepoDetails = function (repoDetails, languageArray) {
//get the repo-data class ready to be shown
repoData.innerHTML = "";
repoData.classList.remove("hide");
allRepos.classList.add("hide");
// create a div with all repo info and add it to the repo Data section
const repoDataDiv = document.createElement("div");
repoDataDiv.innerHTML =
Then comes the backtick and the div tag
The div is full of h3 and p tags with content like: Name: ${repoDetails.name}
Which uses the JSON file and the keys to select specific details.
I guess this is why we can’t do the languages_url here — since there we wanted the keys, not the values.
The last bit of this HTML has an a tag with the css class “visit” that creates a button. When clicked on, the button takes the user to the repo’s url.
This functionality is created with ${repoDetails.html_url} inside the href parantheses.
After the HTML comes another backtick and a closing div tag.
The code concludes:

repoData.append(repoDataDiv);
backToRepos.classList.remove("hide");
};

The displayRepoDetails function takes the repoDetails and languageArray parameters from getRepoDetails.
It gets the repoData class ready to be shown by setting the innerHTML to an empty string and removing the hide class from repoData while adding the hide class to allRepos [We are transitioning from showing the names of all the repos to showing details about one repo]

The function then creates a div to display the repo info, which I discuss in the middle of the code itself. [Because I couldn’t post the repo info code without messing up the HTML on this page.

The div with the repo details is then appended to repoData and thus displayed in that section of the display.
The backToRepos button is also unhid so users can go back to the page with all the repos


// button to return to the list of repos
backToRepos.addEventListener ("click", function () {
// hide the specific repo data
repoData.classList.add("hide");
// unhide the list of repos
allRepos.classList.remove("hide");
// hide this button
backToRepos.classList.add("hide");
});

This function listens for a click on the backToRepos button and then hides the repoData css (for the individual repos), unhides the allRepos css (for all the repos) and hides the backToRepos button.


filterInput.addEventListener("input", function (e) {
const searchText = e.target.value;
// turn the captured text to lower case
const lowerSearchText = searchText.toLowerCase();
//select all repos in document
const repos = document.querySelectorAll(".repo");
for (const repo of repos) {
// looping through all the repos, capturing lower case versions of all inner text
const lowerRepo = repo.innerText.toLowerCase();
// only show repos that include some of the searched text
if (lowerRepo.includes(lowerSearchText)) {
repo.classList.remove("hide");
}
else {
repo.classList.add("hide");
} // if else clause ends here
} //for look ends here
});

 filterInput.addEventListener(“input”, function (e)) listens for someone to type in the input field (search box)
It then lower cases the input text, selects every repo [by selecting everything with a class repo — we added that class to each repo in the displayRepoInfo function), and then loops through every repo in that list of repos with a for of loop [the kind that returns values rather than keys]. Inside that for of loop, we lower case the repo.innerText so it matches the searched text. For every repo, if it includes the search text (we use .includes), we remove the “hide” class from the repo; otherwise we add the “hide” class to the repo.

[Part of the Speaking Javascript project.]