DEV Community

Cover image for Yet Another Notification Library
Volker Schukai for schukai GmbH

Posted on • Updated on

Yet Another Notification Library

Motivation

We want to have something like this

wonted

There are a number of ready-made components out there, so what was our motivation for this control?

As always, there are several reasons to develop your own components and not use existing ones. When you write code yourself, you not only have full control, but you also always learn something new.

The learning effect cannot be overemphasized.

Here we would like to pass on a few learnings.

Maybe it helps you!

Technical conditions

But now let's get to our latest achievement, a notify control.

The component itself is not rocket science. Showing and hiding is done via CSS properties display and the setTimeout functionality. Quite simple.

An exciting question when creating the component was the positioning. How can the messages be arranged?

Naive implementation

One possibility is absolute positioning.

You simply define the top value for each message and thus have a first solution.

<style>
.message {
  position: absolute;
  right: 20px;
}

:nth-child(0) {
 top: 0;
}

:nth-child(1) {
   top: calc(1 * 40px);
}

:nth-child(2) {
   top: calc(2 * 40px);
}
</style>

<div class="message">
  this is message 1
</div>

<div class="message">
  this is message 2
</div>

<div class="message">
  this is message 3
</div>
Enter fullscreen mode Exit fullscreen mode

Looks quite nice already. Unfortunately, the solution has several problems. For example, what happens when the text of the notification is longer. Then the positions of the underlying are no longer correct.

An improvement would be to calculate the height via Javascript

This is done by calculating the height and top of the messages and placing the next message below them accordingly.

const element = document.querySelector('.message')
var domRect= element.getBoundingClientRect();

const margin = 20;
const nextPosition = domRect.bottom+margin;

console.log(nextPosition);
Enter fullscreen mode Exit fullscreen mode

This can be cast into a nice function and used. This works quite well and is also done by some components in the wild.

CSS should do the job

However, Javascript has the disadvantage that it is Javascript. Any calculation that has to run in Javascript is slower than native code.

So what to do?

The further consideration was, whether there is not a simpler system. Among other things, you can set up a div as a container and display the messages below each other.

This has the advantage, you don't need to calculate distances and you can adjust the positioning via flex.

<style>
.container {
  position: absolute;
  top: 0;
  right: 0;
  display:flex;
  flex-direction: column;
}

.message {
  margin-top: 25px
}
</style>


<div class="container">

  <div class="message">
    this is message 1
  </div>

  <div class="message">
    this is message 2<br>
    second line
  </div>


  <div class="message">
    this is message 3
  </div>

</div>
Enter fullscreen mode Exit fullscreen mode

However, a absolute positioned div has a disadvantage. The content behind it is not clickable or selectable.

This is where the CSS property user-select comes into play.

This allows a div to be expanded and pretend it's not there.

That's really all we need.

The final component

We have integrated all of this knowledge into a component.
You can examine the code in detail on our gitlab or in npm.

This is simply included via the tag monster-notify.

<monster-notify id="notify"
                data-monster-options='{
        "orientation": "right top"
        }'>
</monster-notify>
Enter fullscreen mode Exit fullscreen mode

The parameters, such as the positioning or the length of the overlay can be specified via a JSON data-monster-options.

New messages can be included via push method.

import {Notify} from "@schukai/component-notify/notify.js"

// create new message
const message=document.createElement('monster-notify-message');

// simple HTML content
message.innerHTML='this is a message!';

// change standard timeout
message.setAttribute('data-monster-timeout', 1000)

// notify container
const notifyElement = document.getElementById('notify');

// show message
notifyElement.push(message);

Enter fullscreen mode Exit fullscreen mode

The component itself has only one dependency to our own Monster library and uses only a few objects here.

hope you enjoy it!

References

Top comments (0)