How to build a JavaScript Library

Please follow and like us:

If you’re a developer it’s almost certain that you’ve used Code Libraries. In simple terms, a Code Library is a collection of reusable code that you can use in your program.

Like most of us, I know how to plug libraries into my programs and use them, but used to get scary for me each time I went through the actual source code of that particular library. Then I got to learning Vanilla JS and it all stopped seeming so scary. Vanilla JS is plain JavaScript code that you write without the use of any external libraries.

When it comes to building a JavaScript library that you will use to create a website, It’s important that you learn DOM manipulation using Vanilla JS before attempting to actually build it. The reason that it’s important is that you won’t have any dependencies and your library will be a simply plug-n-play for your users. You may be familiar with  DOM manipulation using jQuery but that will create a dependency on jQuery and your library won’t function on a webpage that doesn’t include a reference to the jQuery library.

If you’re familiar with jQuery and want to learn more about DOM manipulation using Vanilla JS, you can read more about it here.

With all that said let’s dive into creating a JavaScript Library from scratch.

We’ll building an Image Lightbox. A lightbox is library that is used to view images on a webpage by filling the page with the image and de-emphasizing the background. Here’s what we’ll build.

If you want to jump ahead and see the code you can find the GitHub repository here.

When building a JavaScript Library there are a few things that you want to keep in mind.
Your code shouldn’t interfere with the existing code written by the user. To do this we’ll encapsulate all our code in a single Function object.

function FsLightbox() {
   // all the code goes here
 }

All the code that we write in this tutorial will be written in that function body. This will take care of the encapsulation.

We now have to create a list of all the methods that the user will be given access to and that will be useful the user. In this library, we’ll have four such methods

.render() .next().prev(), and .hideLightbox()

The FsLightbox object will thus have these four public members. By public members we mean all those methods and properties that are preceded with the this keyword.

First and foremost we’ll store a reference to the object context as using this keyword can get messy in JavaScript.

let _this = this;

This will eliminate all the confusion and let us use the object context without any worries.

With that out of the way, let’s go over the strategy that we’ll employ to get the lightbox working. 

  • The user will specify the images that will use the lightbox by adding a specific class to those img tags. So, every time DOM loads, we’ll be able to capture those images and save their references in an array.
  • After that, we’ll add a click listener to each image to make sure that every time the user clicks that image, it will be shown in the lightbox. We’ll leverage both JavaScript as well as CSS to achieve this.
  • We’ll also need navigation controls for Previous Image, Next Image and a button to Close the lightbox. We’ll wire these controls with their respective click listeners.
  • We’ll also display some meta information like Image Counter and Caption (we’ll get this from Alt text)

As you may have already realized the JavaScript library that we’ll create will use a lot of CSS and hence, we’ll be including a CSS file also.

Enough talk, let’s write some code. First, we’ll need to keep track of some data and hence, we’ll be declaring some variables that will be available throughout our little Function. These will be the following:

    let imagesArray = []; // Array containing the reference of all the images that have the specified class (fs-lightbox)
    let currentImage; // Image being displayed currently in the lightbox
    let isLightbox = false; // Boolean to tell you whether the image is being displayed in the lightbox or not 

After this we’ll write our first public method, .render(). In here we’ll initialize are “global” variables and get a reference of all the images stored in the array. This method will need to be run to actually initialize our Lightbox. Here’s the code:

        this.render= () => {
            imagesArray= [];
            currentImage=null;
            isLightbox=false;
            document.querySelectorAll('img.fs-lightbox').forEach((img_el, index) => {
                imagesArray.push(img_el);
                img_el.setAttribute("data-lightbox-index", index);
                img_el.addEventListener('click', () => {
                    _this.lightbox(img_el);
                });
             });
             addKeyListeners();
        }

As seen we’re using a function addKeyListeners() here. This method is used to hook up all the keyboard actions for Next, Prev and Close Lightbox. We’ll get to it later. The event listener that we are adding here is _this.lightbox(). This method will be responsible for actually displaying the image in the lightbox. Here’s it’s implementation.

    this.lightbox = _el => {
       this.hideLightbox();
       currentImage = _el;
       isLightbox = true;
       var overlay = document.createElement('div');
       overlay.classList.add('lightbox-overlay');
       var imageContainer = document.createElement('div');
       imageContainer.classList.add('lightbox-image');
       var image = document.createElement('img');
       image.src = _el.src;
       imageContainer.appendChild(image);
       document.querySelector('body').appendChild(overlay);
       document.querySelector('body').appendChild(imageContainer);
       prepareControls(_el);
   }

As evident in the render() method, this method expects the image element _el as a method parameter. This will be a reference to the img element that needs to be displayed in the lightbox. If we analyse this method further, we see that we first hideLightbox() if it’s already displaying some other image. Then we save the reference of the image in our global variable currentImage and also change the isLightbox variable to true. Then the real DOM manipulation begins. 

We create the overlay div overlay using createElement method and add the class lightbox-image to it. The CSS file that we create will contain all the necessary CSS for the overlay such as position and background-color. I won’t dive into that code as that is not the point of this post. That being said, here’s a snippet detailing the same:

.lightbox-overlay {
   position: fixed;
   top: 0;
   left: 0;
   width: 100vw;
   height: 100vh;
   background-color: rgba(0,0,0,0.7);
   z-index: 10000;
}

Once we create the overlay, we need to create a container for the actual image. The imageContainer variable will contain the same. The variable will be a reference to a div that we create and then we will create an img element image and append it to the same div. With the supplied _el function parameter we’ll have the src of the clicked image and will set the src of image element to the same value. Then, we append these newly created elements, overlay and imageContainer to the DOM. That’s it! Now every time the user clicks an image that has the fs-lightbox class added to it, it will trigger the lightbox() method and the image will be displayed in the lightbox.

That’s most of the hard-work done. Now comes the polish. That’s just us making our library a little more user friendly. This is very important when creating a library. Always think from a user’s perspective. The more convenient it’s for the user, better it will be received.

So, let’s get to it then. Remember, earlier in the post I mentioned all the four public methods that will be available to the users. We have just implemented one of those four methods – render(). We need to implement the rest – next(), prev() and hideLightbox().

This will be done in the prepareControls() method. You can see in the lightbox()  method we’re calling this method. This method will take care of both rendering the UI Elements, i.e., buttons for the above actions as well as attaching click listeners to them. Here’s the implementation.

    function prepareControls(imgElement) {
       let controls = document.createElement('div');
       controls.innerHTML += controlsHtml;
     document.querySelector('body').appendChild(controls.querySelector('.lightbox-controls'));
       let imgIndex = getCurrentImageIndex();
       if (imgIndex > 0) {
           document.querySelector(".lb-prev").addEventListener('click', () => {
               _this.prev();
           })
       }
       else {
           document.querySelector(".lb-prev").classList.add(['lb-disabled'])
       }
       if (imgIndex < _this.imagesArray.length - 1)
             document.querySelector(".lb-next").addEventListener('click', () => {
               _this.next();
           })
       }
       else {
           document.querySelector(".lb-next").classList.add(['lb-disabled'])
       }
       document.querySelector('.lb-close').addEventListener('click', () => {
           _this.hideLightbox();
       })
       showCounter();
   }

As I said, creating controls and hooking them up with click listeners is all that’s being done here. The handlers this.next() and this.prev() are implemented as:

    this.next = () => {
       let imgIndex = getCurrentImageIndex();
       if (imgIndex === _this.imagesArray.length - 1)
           return;
       _this.lightbox(_this.imagesArray[getCurrentImageIndex() + 1]);
   }
   this.prev = () => {
       let imgIndex = getCurrentImageIndex();
       if (imgIndex === 0)
           return;
       _this.lightbox(_this.imagesArray[getCurrentImageIndex() - 1]);
   }
    this.hideLightbox = () => {
       let overlay = document.querySelector('.lightbox-overlay');
       let image = document.querySelector('.lightbox-image');
       let controls = document.querySelector('.lightbox-controls');
       if (overlay)
           document.querySelector('body').removeChild(overlay);
       if (image)
           document.querySelector('body').removeChild(image);
       if (controls)
           document.querySelector('body').removeChild(controls);
       this.isLightbox = false;
   };

.next() gets the next image from the array imagesArray and passes it to .lightbox() method and similarly for .prev() method. hideLightbox() removes the displayed image from the DOM.

We’re left with two more things – Showing meta information and Binding keyboard shortcuts.

We’ll write two methods for this, addKeyListeners() and showCounter() (poor name choice, should be showMetaInformation), Here’s the implementation. It’s fairly simple to understand once you go through it.

   function showCounter() {
       let imgIndex = getCurrentImageIndex();
       let counter = document.createElement("span");
       let counter_Html = `<br/>${imgIndex + 1} of ${_this.imagesArray.length}`;
       if(_this.currentImage.alt){
           counter_Html += ` - ${_this.currentImage.alt}`;
       }
       counter.innerHTML = counter_Html;
       document.querySelector('.lightbox-image').appendChild(counter);
   }
    function addKeyListeners() {
       document.removeEventListener('keydown', bindKeys);
       document.addEventListener('keydown', bindKeys);
   }
   function bindKeys(e) {
       // left arrow key
       if (e.keyCode === 37 && _this.isLightbox) {
           _this.prev();
           return;
       }
       // right arrow key
       else if (e.keyCode === 39 && _this.isLightbox) {
           _this.next();
           return;
       }
       // escape key
       else if (e.keyCode === 27 && _this.isLightbox) {
           _this.hideLightbox();
           return;
       }
   }

That’s all the code that we’re going to write in our function body. We now need to initialize it. This will be done by calling the .render() method that we created. Remember, it’s the method responsible for actually initializing everything. We can do this outside the Function body like this: 

var fsLightbox = new FsLightbox;
fsLightbox.render();

Now the user will have to reference the .js as well as the .css files in their html and they will be up and running with using the library.

That’s about it. There are some smaller functions that I didn’t mention here like getCurrentImageIndex(), etc as those aren’t really important to understanding the workflow. You can still check those in the Github Repo.

Since we’re done with building our library, we need to understand how to publish it so that other users can use it. We have many options for that. We can use GitHubnpm, etc to host our library. Always remember to include a small readme file that details the library’s usage. You can find the example in the same Github Repo.

TL;DR; To create a JavaScript library, you need to keep in mind that you’re encapsulating the code so that it doesn’t interfere with user code, making it simple and easy to use, and trying to make sure it doesn’t have any dependencies. You follow these and you shall be fine.

Please follow and like us:
Avatar

Author: Faisal Rashid

Working as a Software Developer at PQube Business Solutions

4 thoughts on “How to build a JavaScript Library”

  1. It is always good to see articles on the basics. This is a clear post on how to build a library, neat, concise, precise and to the point. Keep up the good work.

Leave a Reply

Your email address will not be published. Required fields are marked *