APIs for Librarians

Helping you to help yourself in helping your patrons. Or something.

Dynamic New Books Display


Description

Do you have a subject heading for your new books? Would you like to display them on your website or libguide, with book cover images? Would you like to never have to update the page with newer new books? Then this is for you!

Every time a user loads the page our code will query Primo for our books labeled with our “new books” subject heading and will then randomly select a few (you determine how many) from that group. It will then query Google Books for a cover image for the book (and will not display the book without a cover image). Then it places them on the page for the user. We use an animated gif preloader to show the user that something is happening while these operations complete.

Also included here are styles that will allow a hover effect as shown in the screenshot, that displays the books title. Clicking any book will take you to its record in Primo.

Screenshot

Dynamic New Books Display screenshot

More details

One prerequisite for doing this is having an API key setup for your institution’s Primo. You can find instructions on this page: Primo REST Apis. That might be the hardest part of implementing this. If not, the hardest part will be determining the exact query to use in the call to the API, as far as how to refer to your new books subject heading.

This code is all vanilla javascript except for the API calls which use jQuery for stability and cross-browser support. Libguides and most school CMSes are already loading jQuery so this shouldn’t be a problem. If you’re using this code somehwere without jQuery you will need to either add it to the page or rewrite the code without it.

The Code

HTML

1
2
3
4
5
6
7
8
   <div id="new-books-1">
      <img
        src="https://www.example.com/preloader1.gif"
        alt="preloader"
        id="library-preloader"
      />
      <ul id="new-books"></ul>
    </div>

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#new-books-1 .book-cover {
    box-shadow: 0px 0px 2px darkgray;
}
#new-books-1 .new-books-li {
    box-shadow: 1px 1px 3px lightgray;
    padding: 1rem;
}
#new-books-1 ul li {
    display: inline-block;
    margin-right: 1rem;
    margin-bottom: 1rem;
}
#new-books-1 .book-cover {
    max-width: 145px;
    max-height: 215px;
}
 
.content {
    position: relative;
    max-width: 400px;
    margin: auto;
    overflow: hidden;
}
.content .content-overlay {
    background: rgba(0, 0, 0, 0.8);
    position: absolute;
    height: 99%;
    width: 100%;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    opacity: 0;
    -webkit-transition: all 0.4s ease-in-out 0s;
    -moz-transition: all 0.4s ease-in-out 0s;
    transition: all 0.4s ease-in-out 0s;
}
.content:hover .content-overlay {
    opacity: 1;
}
.content-image {
    width: 100%;
}
.content-details {
  
    position: absolute;
    text-align: center;
    padding-left: 5px;
    padding-right: 5px;
    width: 100%;
    top: 50%;
    left: 50%;
    opacity: 0;
    -webkit-transform: translate(-50%, -50%);
    -moz-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
    -webkit-transition: all 0.3s ease-in-out 0s;
    -moz-transition: all 0.3s ease-in-out 0s;
    transition: all 0.3s ease-in-out 0s;
}
.content:hover .content-details {
    top: 50%;
    left: 50%;
    opacity: 1;
}
.content-details {
    font-family: "Roboto Condensed", sans-serif;
}
.content-details .content-title {
    color: #fff;
    font-weight: 500;
    text-transform: uppercase;
    font-size: 14px;
    padding: 5px;
}
.content-text .fa-external-link-alt {
    font-size: 2.1rem;
}
.content-details p {
    color: #fff;
    font-size: 1.2rem;
}
.fadeIn-bottom {
    top: 80%;
}
p.content-text {
    margin-bottom: 1px;
}

JavaScript/jQuery

Notes for implementation:
The code itself:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
let newBooksDynamism = (function() {
  let totalDisplayed = 0;
  //Getting a list of books with the subject newbooks limit=100 is one hundred results to work with.
  $.getJSON(
    "https://api-na.hosted.exlibrisgroup.com/primo/v1/search?q=lsr03%2Cexact%2Cnewbooks&vid=01TRAILS_ROCKY&tab=default_tab&limit=100&scope=P-01TRAILS_ROCKY&apikey=YOUR_API_KEY_HERE",
    function(result) {
      var jsonContents = result.docs;
      var jsonResponseLength = jsonContents.length;

      //This is the function to generate as many random numbers we want - with the amount of API results as the upper range.
      var getRandomNumbers = function(howMany, upperLimit) {
        var limit = howMany,
          amount = 1,
          lower_bound = 1,
          upper_bound = upperLimit,
          unique_random_numbers = [];
        if (amount > limit) limit = amount; //Infinite loop if you want more unique natural numbers than exist in a given range
        while (unique_random_numbers.length < limit) {
          var random_number = Math.floor(
            Math.random() * (upper_bound - lower_bound) + lower_bound
          );
          if (unique_random_numbers.indexOf(random_number) == -1) {
            unique_random_numbers.push(random_number);
          }
        }
        return unique_random_numbers;
      };
      //This is where we actually specify how many random numbers (and therefore how many books) we want generated.
      var ourRandoms = getRandomNumbers(10, jsonResponseLength);

      bookCoverGrab(result, ourRandoms); //call a function with the full results from the API call and our random numbers.
    }
  );
  //function to get book cover image url strings
  function bookCoverGrab(input, randos) {
    for (i = 0; i < randos.length; i++) {
      let theIsbn = input.docs[randos[i]].pnx.search.isbn[0];
      let theTitle = input.docs[randos[i]].pnx.display.title;
      let theCatalogLink = `<a href="https://rocky-primo.hosted.exlibrisgroup.com/permalink/f/1j18u99/${input.docs[randos[i]].pnx.control.sourceid}${input.docs[randos[i]].pnx.control.sourcerecordid}"
                target="_blank">`;

      $.getJSON(
        `https://books.google.com/books?bibkeys=ISBN:${theIsbn}&jscmd=viewapi&callback=?`,
        function(data) {
          if (data[`ISBN:${theIsbn}`].thumbnail_url != undefined) {
            addToDom(
              data[`ISBN:${theIsbn}`].thumbnail_url,
              theTitle,
              theCatalogLink
            );
            $("#library-preloader").hide();
          } else {
            console.log("No ISBN", i);
          }
        }
      );
    }
  }

  function addToDom(theIMG, theTitle, catalogLink) {
    //change the 7 to however many you want to display
    if (totalDisplayed < 7) {
      class RmcNewBooks {
        constructor(theBookStuff) {
          this.theBookStuff = theBookStuff;
        }
        getToAppending() {
          var domsn = document.getElementById("new-books");
          domsn.insertAdjacentHTML("beforeend", this.theBookStuff);
          totalDisplayed++;
        }
      }
      var theBookStuff = `
      <li class="new-books-li">
        <div class="content">
         
            ${catalogLink}
                <div class="content-overlay"></div>
                <img class="content-image book-cover" src="${theIMG}">
                <div class="content-details fadeIn-bottom">
                    <div class="content-title">${theTitle}
                     
            
                </div>
            </a>
        </div>
    </li>`;

      var ttttt = new RmcNewBooks(theBookStuff);
      ttttt.getToAppending();
    }
  }

  // }
})();