Are you a fan of libraries like jQuery, D3 or GSAP?

Most probably because they do your work really quicker in an easy way, right? Saying regardless of whether you are a fan of these popular libraries, what if you’ve got an awesome idea/feature for your project but no other library exists to help you in implementing your idea in a quick way?

The most straightforward answer is to code one of your own, your first javascript library. Perhaps, this might not be the only reason you would want to build a library of your own.

Another reason to do so is to learn! It is well said that there’s no better way to be better at JavaScript than developing a Side Project. Not necessarily for others to use but most of the time for your own needs.

I’m pretty sure that even developers of jQuery, before its release, wouldn’t have an idea that their library would get so popular. Who knows if your idea is going to make you build next big JavaScript library? In this post, we’re going to create a simple library which will enable us to display in-app (not a ‘Web Notification’) notifications.

Things To Know Before Moving Ahead

Before we start writing the main code of our library, we need to be aware few things which I assume you already know. Still, let’s have a refresher.

Scope

Before ES2015, JavaScript had no scope concept, the only (and powerful) way you could implement make use of scope was through functions and objects. It’s a good idea to use an IEFE(anonymous function wrapped in parenthesis which is called immediately) than a traditional function while implementing scope.

var Module= (function() {
    var foo= "I'm private";
    
    //return foo in an object
    return {
        foo: foo
    }
})();
console.log(foo) //Reference Error
console.log(Module.foo) //I'm private

var foo= "I'm global";
console.log(foo); //I'm global

In the above code, Module has a separate scope. It has a private identifier foo. We then return an object with a property also named foo having a value of private foo (“I’m private”).

Now, as it is an IEFE, so it is evoked immediately and {foo: foo} is returned and gets stored in Module. So finally, Module ends up storing(being) an object with property foo which you can access like Module.foo.

Without going any deeper, I would suggest you to go through a article on Javascript Scope by Todd Motto if you want to further understand the scope concept.

Design Pattern and API

The code in the last section is an example of the design pattern. Here’s what a design pattern means in the words of Addy Osmani

“A pattern is a reusable solution that can be applied to commonly occurring problems in software design – in our case – in writing JavaScript web applications. Another way of looking at patterns are like templates for how we solve problems – ones which can be used in quite a few different situations.”- Addy Osmani

A Design Pattern allows us to write our code in a scalable and cleaner way and makes sure it is well abstracted i.e. only what’s really needed is exposed/made accessible to the public scope. Here’s an example

var UI= (function() {
    //private data members
    var wrapper= document.querySelector(".wrapper");
  
    function changeTheme() {
        wrapper.classList.toggle("dark");
    }
    function toggleLightBox() {
        var lightbox= document.querySelector(".lightbox");
        lightbox.classList.toggle("is-visible");
    }
    function toggleModal() {
        var modal= document.querySelector(".modal");
        modal.classList.toggle("is-open");
    }
  
    //public data members (API)
    return {
        changeTheme: changeTheme,
        toggleModal: function() {
        toggleModal();
        toggleLightBox();
    }
}
})();

If we now console.log(UI), we get following

UI_log - Coding your first JavaScript Library

As the image shows, the UI contains changeTheme and toggleModal methods. Here, UI is our API. Now you can use it as following

UI.changeTheme() //toggle class 'dark' on body
UI.toggleModal() //opens modal (and also lightbox)

Note that it’s just like performing window.close(), Element.remove() etc. The only difference is window, element etc. are built-in APIs while the UI is hosted one (we created it).

It’s very important to know that UI.changeTheme() makes use of wrapper and UI.toggleModal makes use of private toggleModal and toggleLightBox internally. However, these internally-used members (which are stored as closures) aren’t exposed to be accessed like UI.toggleLightBox, UI.wrapper because we expect users will not need these directly.

Other Important Things to Follow

  • Our library should use DRY code (of course).
  • Every part should be as independent as possible. This makes sure that very few or no edits have to be made when we need some changes or add new features.
  • Our library must be able to handle errors very well. It should be able to inform users through appropriate errors that where they are going wrong.

Getting Started

Now we come to code our little library which will enable us to display an in-app notification with passed message and state(e.g. general, error, etc.). Let’s name our library Notifier.js to make use feel awesome.

Markup for our Notification

We’ll use only one element for our notification. Whenever there’s is new notification we update our notification element with new content and classes. We’ll be generating our markup in JavaScript. Here is how it is structured

<div class="notification warning">
    <div class="logo">
          <img src=""/>
    </div>
    <h3 class="notification-message">You've a lot of work unsaved. Make Sure you save it</h3>
    <button class="btn-close">x</button>
</div>

The CSS stuff

I will be discussing just the main css and the rest is trivial. You can see the rest CSS in the CSS tab of demos in the following sections of the post. Here’s the main CSS

.notification {
      position: fixed;
    z-index: 100;
    top: 0;
    left: 0;
    right: 0;
    pointer-events: none;   
    transform: translateY(-105%);
    transition: all .1s ease-out;
}

Initially our notification element has position set to fixed which doesn’t affect normal flow of our page and the z-index is set to 100, which keeps it at the top of every element on the page (maybe you will need to set it to much higher value to win over the values like 999 which people often use but shouldn’t). Pointer-events is set to none which prevents any interaction on our notification element. To hide it from the viewport, we translate it by -105% (not by -100% because then box-shadow(see las demo’s CSS tab’) will be visible).

When the notification is opened, it is set following .is-open class

.notification .is-open {
    position: static;
    transform: translate(0);
    pointer-events: auto;
}

We reset the position to static (to shift other content downwards, we can keep the position to be fixed if don’t want this behavior). We then translate it to it is origin position and set pointer-evets to auto to allow user interaction now.

Along with is-open class, we also set one of the following state classes when notification is opened.

.notification.success {
    background: #5bc467;
}

.notification.error {
    background: #f86016;
}

.notification.gen {
    background: #26b5c1;
}

.notification.warning {
    background: #f8cd16;
}

The Javascript

Here comes the heart of our library, the Javascript code. Here is the pattern that we are going to use in our Notifier.js

var notifier= (function() {
    function createAndAppendNotification() { }
    var elements= createAndAppendNotification(),
  
    not= elements.notification,
    trans= getComputedStyle(not).getPropertyValue("transition-duration"),
    stateClass= "gen";
    const delay=Number( trans.substring(0, trans.length-1));

    function setNotification(logoSrc, msg) { }
    function showNotification(state) { }
    function closeNotification() { }
  
    elements.btnClose.addEventListener("click", closeNotification, false);
  
    return {
        notify: function(state, options) {
            if(typeof options === "object") {
                setNotification(options.logo || "", options.msg);
            } else {
                throw new Error("Second arguments should be an object having keys as 'options' (e.g.- logo)");
        }
        if(typeof state === "string") {
            showNotification(state);
            stateClass= state;
        } else {
            throw new Error("First argument should be a string representing state of notificaion");
        }
    }
}
})();

Now let’s break each block of code one by one

function createAndAppendNotification() {
    //create required elements

    var notification= document.createElement("div"),
    logo= document.createElement("logo"),
    logoImg= document.createElement("img"),
    notificationMsg= document.createElement("h3"),
  
    btnClose= document.createElement("button");

    //add classes
    notification.classList.add("notification");
    notification.classList.add("gen");
    logo.classList.add("logo");
    notificationMsg.classList.add("notification-message");
    btnClose.classList.add("btn-close");
  
    btnClose.textContent= "x";

    //append elements
    logo.appendChild(logoImg);
    notification.appendChild(logo);
    notification.appendChild(notificationMsg);
    notification.appendChild(btnClose);
    
    document.body.insertBefore(notification, document.body.children[0]);

    return {
      notification: notification,
      logo: logoImg,
      msg: notificationMsg,
      btnClose: btnClose
    }
}

createAndAppendNotification function creates elements and appends them as per the markup structure discussed earlier in this post. The main part of this function is

return {
    notification: notification,
    logo: logoImg,
    msg: notificationMsg,
    btnClose: btnClose
}

var elements= createAndAppendNotification();

Along with creating and appending our markup, we return all the elements we’ll need later in an object. So, later we can access them like elements.notification, elements.btnClose.

not= elements.notification,
stateClass= "gen";

We’ll need to use elements.notification frequently, so we assign it to a separate variable not. stateClass stores current state class (which defines notification state e.g. success, warning).

function setNotification(logoSrc, msg) {
    elements.logo.setAttribute("src", logoSrc);
    elements.msg.textContent= msg;
}

setNotification sets src of .notification > .logo > img and updates text of .notification > .notification-message.

function showNotification(state) {
    if(stateClass) {
        not.classList.remove(stateClass);
    }
    
    switch(state) {
        case "success": not.classList.add("success");
                        break;
        case "error": not.classList.add("error");
                      break;
        case "gen": not.classList.add("gen");
                    break;
        case "warning": not.classList.add("warning");
                        break;
    }
    not.classList.add("is-open");
}

showNotification sets the passed state string as .notification’s second class which defines styles of that state. Also, it first removes the current state class (stateClass). At last it sets is-open class to .notification which makes .notification visible.

var trans= getComputedStyle(not).getPropertyValue("transition-duration"),
stateClass= "gen";

const delay=Number( trans.substring(0, trans.length-1));

function closeNotification() {
    not.classList.remove("is-open");
    setTimeout(()=> {
        if(!(not.classList.contains("is-open"))) {
            not.classList.remove(stateClass);
        }
    }, (delay*1000) || 1000);
}

elements.btnClose.addEventListener("click", closeNotification, false);

While notification is opened, clicking btnClose (.notification > .btn-close) closes the notification by removing it’s is-open class. After .notification goes off the viewport, state class is also removed to bring the .notification in order to prevent collision next time showNotification is called and new state class is set (if we don’t do so, .notification will be set two state classes and the one defined later will win which will give unexpected results).

Note: delay stores transition-duration value of .notification. We remove our state class only after .notification has transitioned back to -105% (which happens after delay seconds) to prevent the user from observing transitioning of the background color.

Now comes the core javascript section where we return our API to be accessed like Notifier.methodName()

var Notifier= (function() {
    return {
        notify: function(state, options) {
              if(typeof options === "object") {
                  setNotification(options.logo || "", options.msg);
              } else {
                  throw new Error("Second arguments should be an object having keys as 'options' (e.g.- logo)");
              }

              if(typeof state === "string") {
                  showNotification(state);
                  stateClass= state;
              } else {
                  throw new Error("First argument should be a string representing state of notificaion");
              }
        }
  }
})();

How would our library users expect to use it’s API? As our library serves as quick solution to a simple use case (displaying in-app notification), it would be good to give the users a handy way to display notification, something like window.alert(some-text). Let’s just think what data user needs to enter to display the notification.

The only things that are supposed to be changed in our new notification are state, Logo image (.notification > .logo > img) and the Text (.notification > .logo > .notification-message). Something like following would be cool

Notifier.notify("success", {
    logo: ".../logo.jpg",
    msg: "Request Sent!"
});

To achieve this, we return notify method in our Notifier function wrapper.

return {
    notify: function(state, options) {
    
        if(typeof options === "object") {
          setNotification(options.logo || "", options.msg);
        } else {
          throw new Error("Second arguments should be an object having keys as 'options' (e.g.- logo, msg)");
        }
    
        if(typeof state === "string") {
          showNotification(state);
          stateClass= state;
        } else {
            throw new Error("First argument should be a string representing state of notificaion");
        }
    }
}

When notify gets evoked (Notifier.notify()), it first checks if it’s second (passed) argument(options) is an object or not. If yes we call the setNotification which sets .notification > .logo >img’s src to first argument(options.logo) and .notification > .notification-message’s text to second argument(options.msg) of setNotification.

If the second argument doesn’t happen to be an object we throw a reasonable error. Similarly, we check if first argument(state) of the notifiy method is string or not. If it is then we call showNotification which adds is-open and pass state class to the notification element and also updates stateClass variable with the passed state argument (so that we can use it later e.g. in closeNotification function). If it’s not we again throw a reasonable error.

Note: Doing something when a condition goes true otherwise throwing an error is what called as ‘Error Handling’. From now on explanations regarding Error Handling will be skipped as I guess it is self-explanatory now. Also, note that everything is happening privately without exposing the functions like setNotification, showNotification to public API Notifier.

The only method you can access through Notifier API is notify. This method then makes use of setNotification and showNotification methods among other private data members wherever needed.

And the Beta version of our library is complete! We can use it as following

Notifier.notify("warning", {
    logo: ".../warning.jpg",
    msg: "Save your pen before logging out"
});

Now we can implement it any way we want. Below is the demo pen which utilises our newly built library.

See the Pen A Notification Library Demo by Devstreak (@devstreak) on CodePen.

Further Reading

Javascript Modules: A Beginner’s Guide – Medium

Mastering Module Pattern – Todd Motto

How do You Structure JavaScript- The Module Pattern Edition – CSS Tricks

Conclusion

Coding your first Javascript library was not that tough, huh? Unless you are not aiming for advanced features, you are ready to use your newly built library in your next projects. If you are Javascript lover, why don’t you add some more functionality to your library?

Head up for Part 2: Extending your first Javascript library

So which idea are you going to turn into your next library? Do share with us or ask questions, if any, in the comments below.

Share on Facebook Tweet on Twitter Post on Google+ Buffer