Counting Books with Class

I read fiction for fun. I write a short note to remind myself about each book. I've done it for a few years now, a page for each year of "reviews."

As I learn some new trick in JavaScript, HTML or CSS, etc., I try to add the technique to some page on my site. I have found that doing something "real" makes a concept stick better than just doing a practice exercise. It also helps me to solidify the concepts by trying to explain them. That's the reason for this very page.

For those who cannot bear
to miss the real thing,
see Recent Reading.

The most recent idea was to add a book count, as illustrated here.

Comment, "review", etc.

Comment, "review", etc.

Comment, "review", etc.

Comment, "review", etc.

Comment, "review", etc.

Comment, "review", etc.

I have been using a CSS class which I called "bem" — meaning "bold-emphasized" — for each of the titles as I add them to the page after finishing a book. Only the tags for the book titles use that class:
<span class="bem">Book Title</span> on the web page. If I could count how many times the class is applied, I would have an automatic and quick count instead of having to count through the titles manually. That is what programming is for!

I looked up the idea with a search "JavaScript count elements by class" which lead me to the code explanations I needed. Then it was a matter of doing it myself.

Javascript Code

There are two pieces of code to make the idea work. In the head section of the page, add this little JavaScript function.

<script language="javascript">

function getBooks() {
  var allBooks = document.getElementsByClassName("bem");
  var num = allBooks.length;
  alert(num + " books this year.");

Then, in the body section, at the location you decide works best for you, add the button code to call the function.

<button onclick="getBooks();"> Click for book count </button>

Those two little code pieces add a nifty feature to my most recent page of books. I'll apply the trick as it fits elsewhere, too. Unfortunately, I wasn't using CSS style classes in earlier book pages and I'm probably not going to go back to recode them. The 2015 page of books will be the first to have the features described here.

UPDATE: June 13, 2015

Reading and Javascript

Calculating the number of books per week sounded like something to add to the alert popup which I'd figured out in August 2013.

In June of 2015, I read the second short novel by Walter Mosley, Love Machine. While updating the website recentreading.html file, I revisited the idea of adding the average books read per week. This was my second time around trying to work with the code. Second try was good enough for this, as it turned out. Now the popup message reports both the number of books for the year and the books per week.

I found the new code at the very clear article by Steven Chapman at article:

// Week of year:
Date.prototype.getWeek = function() {
var onejan = new Date(this.getFullYear(),0,1);
return Math.ceil((((this - onejan) / 86400000) + onejan.getDay()+1)/7);

The alert popup code has changed a little too, calling the new code through two new variables today and weekno and adding the printout elements in the alert line. Notice the use of \n to get the line break into the alert.

Update: June 30, 2015: modified the alert line to limit the books per week to two decimal places.

Update: August 2, 2015: modified the code to eliminate the alert box, instead displaying the book count in the left column using the same hide-and-show technique as was used in the spoiler technique. Clicking the button now shows or hides the book count. It is the code using "document.getElementById" which replaces the alert of multiple books which I've left in the code as a comment. I may go back and clean up the alert for just a one-book count. It will not happen for long in January, I hope.

// book count function
function getBooks() {
  var allBooks = document.getElementsByClassName("bem");
  var num = allBooks.length;
    if(num == 1)
      alert(num + " book this year.");
      var today = new Date();
      var weekno = today.getWeek();
      // alert(num + " books this year.\n" + num/weekno + " books per week."); 
      // alert(num + " books this year.\n" + (num/weekno).toFixed(2) + " books per week.");
      document.getElementById("bookCt").innerHTML = num + " books this year<br>" + (num/weekno).toFixed(2) + " books per week.";

Update September 29, 2015

Counting pages for a total from all the books was the next thing to try. Though it took me longer than it would for many, here's the code that worked. For each book, I added a span with a number of pages.

Each book entered into the page has the date I finished reading, the title, author and page count.

<p id="trials">2015-09-28<br>
<span class="bem">The Trials</span><br>
<span class="pagecount">447</span>
<span class="auth">Nagata, Linda</span>Linda Nagata</p>

Using CSS, the pagecount span is styled to be hidden from view, though it need not be. It is just a simple addition to the css already done in the head section of the page. The styling and JavaScript may be moved out to separate css and script files when I finally stop fiddling. During development, I find it easier to keep my coding all in one place for a mix of html, css and JavaScript like this. It works for me.

 .pagecount {
 display: none;

The Javascript for counting pages is another function which uses some of the same concepts of harvesting data from the page which was used for counting the books and pulling together the author list. The page count requires, adding a conversion from string to integer so the number of pages could be added together. The span contents holding the number of pages for each book is just text. We need integers to add so we must convert. Math.round is used as the variable round in the loop. Rounding is one common way to convert from text to integer in JavaScript.

function pageCount() {
var pageCt = 0; // initialize variables
var round = Math.round;
var pagePerBook =[]; // initialize an array to hold the duplicate data from the nodelist
var pageNode = document.getElementsByClassName('pagecount'); //get nodelist of all book page counts
for (var i = pageNode.length; i--; pagePerBook.unshift(pageNode[i].innerHTML)); //convert to regular array

for (var i =  pagePerBook.length; i--; pageCt = pageCt + round(pagePerBook[i])); // loop through array to get a total value of page counts.
document.getElementById("total").innerHTML = "total pages:  " + pageCt; // display in the navigation section

Though it wasn't described before, it is necessary to put a small script into the body section of the page which calls (activates) each of the functions I have discussed. The functions just sit there in the head section of the html code until they are called. All the work, including the display code, happens in the function, but each function must be kicked into action.

    // IMPORTANT It appears that the function call script must follow the block element which to be filled by the script
    // wait for the document to finish loading and displaying
document.addEventListener('DOMContentLoaded', function() {   
    //call the author function
    // call the book counting function
    // call the page counting function
  // pageCount();  // function code moved into the getBooks function
    // Be careful. The next line is how the event listener code  must end to properly complete the function.

Update October 24
Moved the code of the page count function into the book count function so the separate page count function is no longer needed. The relevant function code is next.

function getBooks() {
    var allBooks = document.getElementsByClassName("bem");
    var num = allBooks.length;
    if(num == 1)
      // document.getElementById("bookCt").innerHTML = num + " book this year.";
    alert(num + " book this year.");
      var today = new Date();
      var weekno = today.getWeek();
      // Code moved in from former pageCt function 2015-10-14
     var pageCt = 0;
     var round = Math.round;
     var pagePerBook =[];
     var pageNode = document.getElementsByClassName('pagecount');
     for (var i = pageNode.length; i--; pagePerBook.unshift(pageNode[i].innerHTML));

     for (var i =  pagePerBook.length; i--; pageCt = pageCt + round(pagePerBook[i]));
     // New display code combining book count and page count
     document.getElementById("bookCt").innerHTML =  num + " books this year
" + (num/weekno).toFixed(2) + " books per week.
" +"total pages: " + pageCt;; }

Update: January 21, 2016

  1. Modified the book count layout putting the numbers at the end of each line.
  2. Added a more accurate books-per-week calculation.
  3. Added a pages-per-day calculation.

// This code segment provides correct calculation of the books per week and pages per day - replacing the weekno variable
// which only showed which week a given day was in, not the current number of weeks based on a seven-day count of days.

// The first section of this code establishes how to calculate the day if the page is viewed after 2016.
var d2 = new Date("December 31, 2016 23:59:59"); //UPDATE FOR SUBSEQUENT YEARS
var day = 366;  // total days in 2016 (a leap year)
var now = new Date();
if (now > d2) {
    day = 366;   //If the date is later than the end of 2016, treat the total number of days as a fixed number
else  // If the date is still part of 2016, calculate how many days have passed since Jan 1.
    var now = new Date();
    var start = new Date(now.getFullYear(), 0, 0);
    var diff = now - start;
    var oneDay = 1000 * 60 * 60 * 24;
    var day = Math.floor(diff / oneDay);
// After all the calculations are done, display the counts
    document.getElementById("bookCt").innerHTML = " books this year: " + num + "<br> books per week:  "   \
        + (num/(day/7)).toFixed(2) + "<br>total pages:  " + pageCt + "<br>pages per day: " + (pageCt/day).toFixed(0);

There is always something new to learn. I take it step-by-step. Your experience may vary. Do it your way. Make it your own.

The Document Object Model (DOM) exposes page data to JavaScript, for example, information about element tags. I will gradually explore the model to develop new stuff. I hope YOU will, too.