A simple book library application, a complicated JS problem.

Alexander Gould
4 min readSep 19, 2022

--

I built an application that allows you to call the Google Books API and use the resultant data to save your personal library saved in a JSON server.

Building this application was relatively straightforward. Calling the api, using Javascript to render the information and data on the screen, building a JSON server to mock the storage of information and persist that information upon a refresh or reload of the application were all enjoyable if mildly challenging problems to solve.

I integrated several features into the application, allowing for the display and saving of books in a simple UI.

A Simple Library Application:

I accomplished this by building multiple DIV elements in the HTML, using event listeners to build the buttons, and several objects to persist the data.

Then, I wanted to build some stars, so that users could add a rating to the books they had saved in their library. This proved more difficult than I had initially thought, and it took some serious thinking to work out how to make these stars work properly.

Here is how I did it.

First, I had to make the elements, I built a function to append the stars to the container element that held the information on any given book. This gave me an array full of span elements whose inner text was set to the value of the empty star glyph.

Then, I added an event listener that took a callback function to each of the stars, calling the function each time my event — a click — occurred.

I quickly realized that I needed to find a way to get the siblings of this star, the stars that come before and after it, if i wished to create a working set of highlightable stars. To accomplish this, I wrote two more functions:

function getPreviousSiblings(element, siblings =[]){siblings = []while(element = element.previousElementSibling){siblings.push(element);}return siblings}

and

function getNextSiblings(element){siblings = []while(element = element.nextElementSibling){siblings.push(element)}return siblings}

These operate on a global siblings variable that i have set at the beginning of the application, and use a while loop to add either the previous siblings of the target element, or the ones that follow it to the siblings array.

Then I set about making my callback function; what the click would do:

function starRating(e){const star = e.target;const previousStars = getPreviousSiblings(star)const nextStars = getNextSiblings(star)console.log(nextStars)if(star.innerText === emptyStar){previousStars.unshift(star)previousStars.forEach(s => {s.innerText=fullStars.className=”activated-star”})} else {nextStars.unshift(star)nextStars.forEach(s=>{s.innerText = emptyStars.className = “star-glyph”})}}

In this function, e is the event, and e.target is the star that is clicked. It uses my earlier previous and next siblings functions to get those elements, and the checks wether the clicked upon star is empty or full. If it is empty, it iterates over all the the stars that come before it, filling them in, and if it is full, it iterates over the stars that come after it, making them empty, thus allowing you to change the rating from between one and five.

Finally, I fed this function back into my event listener for each star. I still, however, needed a way to persist that information.

function createStars(bookObj){const bookStars = document.createElement('div')bookStars.className = "book-stars"for(var i = 0; i<5; i++){const bookStar = document.createElement("span")bookStar.className = "star-glyph"bookStar.id = `${i+1}`if(i < bookObj.stars){bookStar.innerHTML = fullStarbookStar.className = "activated-star"}else{bookStar.innerHTML=('☆')}bookStars.appendChild(bookStar)bookStar.addEventListener("click",(e)=>{const starId = e.target.idstarRating(e)if(e.target.className == "activated-star"){bookObj.stars = starIdupdateLibraryBook(bookObj)}else{bookObj.stars = 0updateLibraryBook(bookObj)}})}return bookStars}

To accomplish this, in the above function which creates the stars, i give each star an ID which is just a number of 1 through 5, this is set when the stars are created at the beginning of the function. Then, I gave my event listener multiple functionalities, by giving my callback function a single parameter of “e”. I feed the event into the above “starRating” function, that operates on the functionality of the stars on the page, while being able to also persist e.target.id (the number of the star) to my bookObj object, which is passed in from the function that builds the entire card that displays the book, and is pulled from my JSON library. I am then able to use the number of the star, and add it to the book object as an integer through a patch request sent to my server. Finally, when a book is created, it operates on the stars to fill the number of stars that was previously saved in the server.

This problem proved more difficult to solve than I had initially imagined, and gave me the opportunity to learn about thinking through the various elements of the DOM, and how they can work together and dynamically effect one another. I look forward to continuing to improve my skills with the DOM, and think through increasingly challenging problems like this in the future.

--

--