Hi, this is Sesame, today we together to make a “pop-up message alert” plug-in.

1. The analysis

  1. When a message is triggered, there is a top-down fade process.
  2. It will disappear automatically after a period of time, or the user needs to manually click the close button.
  3. As messages disappear, there is a bottom-up fading process.
  4. Messages are superimposed, with the most recent messages at the bottom of the message list.
  5. After the previous message disappears, subsequent messages have an upward slide effect.

Then the message itself is made up of three parts

  1. Message icon to distinguish between different types of messages.
  2. Message text.
  3. Close button. Not all messages require a close button.

2. Implementation style

So, whether we use native JS or VUE, first of all, we need to write the basic style of the message, and then use JS to control the pop-up and close of the message. So, let’s start with HTML and CSS.

<! -- message.html --> <! -- This CSS is some font ICONS I quote from Ali, please stamp: https://www.iconfont.cn/ --> <link rel="stylesheet" href="http://at.alicdn.com/t/font_1117508_wxidm5ry7od.css">
<link rel="stylesheet" href="./message.css">
<script src="./message.js"></script> <! --> <div id= --> <div id= --> <div id="message-container">
    <div class="message"> <! Font class --> <div class="type icon icon-success"></div> <! -- Message text --> <div class="text"< div> <! Close button --> <div class="close icon icon-close"></div>
    </div>
    <div class="message">
        <div class="type icon icon-error"></div>
        <div class="text"</div> </div> </div>Copy the code
/* message.css */

#message-container {position: fixed; left: 0; top: 0; right: 0; Display: flex; display: flex; display: flex; flex-direction: column; align-items: center; }#message-container .message {
    background: #fff;
    margin: 10px 0;
    padding: 0 10px;
    height: 40px;
    box-shadow: 0 0 10px 0 #eee;font-size: 14px; border-radius: 3px; /* Center the three elements inside the message vertically and horizontally */ display: flex; align-items: center; }#message-container .message .text {
    color: # 333;
    padding: 0 20px 0 5px;
}
#message-container .message .close {
    cursor: pointer;
    color: # 999;} /* Give each icon a different color to distinguish between different types of messages */#message-container .message .icon-info {
    color: #0482f8;
}
#message-container .message .icon-error {
    color: #f83504;
}
#message-container .message .icon-success {
    color: #06a35a;
}
#message-container .message .icon-warning {
    color: #ceca07;
}
#message-container .message .icon-loading {
    color: #0482f8;
}
Copy the code

Something like this

3. Animate

The next thing to do is animate the message to pop and disappear, again using CSS.

To implement custom animations in CSS, you first need to define an animation rule with @keyFrames, and then apply the animation to an element using the animation property.

So-called rules of animation is an animation sequence, or can be understood as one of key frames, and keys inside is what you want to change the CSS properties, you can write in the key frames and almost any CSS properties, when the animation is applied, the CSS properties will be according to the key frames to make the corresponding transformation.

So let’s start with @keyframes to write an animation rule

/* message.css */ /* This animation rule will be called message-move-in, and then we will use the animation property to apply the animation rule to an element. */ @keyframes message-move-in {0% {/* The animation is a top down fade-in process */ /* The opacity of the element should be set to 0 at the beginning of the animation and 1 at the end of the animation. This will implement a fade animation */ opacity: 0; TranslateY (-100%) represents the initial state of the animation, the position of the element "at its own height" above the actual position. */ transform: translateY(-100%); } 100% { opacity: 1; */ transform: translateY(0); }}Copy the code

Then we define a move-in class that is the same level as the message element, and apply the message-move-in animation rule to the move-in class. In this way, we only need to add a move-in to the message class.

/* message.css */

#message-container .message.move-in {/ * animation attributes are used to load a certain rules of animation Please refer to https://developer.mozilla.org/zh-CN/docs/Web/CSS/animation * / animation: Message - a move - in 0.3 s ease - in-out; }Copy the code

Let’s see how to use move-in:

As you can see, all you need to do is append a move-in to a message to create a pop-up animation. Then, the vanishing animation is also a routine, but with the pop-up animation in reverse.

/* message.css */ @keyframes message-move-out { 0% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(-100%); }}#message-container .message.move-out {Animation: message - a move - out 0.3 s ease - in-out; /* Animation-fence-mode: forward; /* Animation-fence-mode: forward; }Copy the code

animation-fill-mode: forwards; What does this do? By default, the animation returns to the original state of the element, in this case disappearing and reappearing, as shown in the figure below:

So animation – fill – mode: recently; So that when the animation ends, it stays that way, it’s not displayed anymore.

4. Write JS plug-ins

So, before we write JS, let’s think about how you want to call this plug-in if you are a plug-in user.

Our plugin is very simple, is in need of pop up a Message, and assumes that the plug-in is he provided us a class, is called a Message, and he has an internal show method, so as long as the users to instantiate the class, calling his show method, and then introduced into different parameters can pop up a Message. And the objects we instantiate can be globally unique.

<! -- message.html --> <! -- ellipsis... --> <script> // Message can be defined as a global object that can be called directly in a project. const message = new Message(); message.show({type: 'success',
    text: 'Point a concern not to get lost ~'
});
</script>
Copy the code

So, we’ll write a Message class first, and we’ll have to implement a show method.

/* message.js */

class Message {
    constructor() {

    }

    show({ type = 'info', text = ' '{}}})Copy the code

Here I directly use the es6 class keyword, in fact, its internal is still the form of the prototype chain. Using class gives us a little bit more intuition about this class.

According to our analysis in Part 1, all Message elements need to be created in JS, so we don’t need users to write any HTML code, so we just need to create message-container when the object is instantiated new Message(). Later, when the show method is called, you simply insert the message inside the message-container.

/* message.js */ class message {/** * the constructor is automatically executed at instantiation time */constructor() {
        const containerId = 'message-container'; ContainerEl = document.getelementById (containerId);if(! This.containerel = document.createElement() {this.containerel = document.createElement() {this.containerel = document.createElement('div'); this.containerEl.id = containerId; / / the message - the container element in the HTML document at the end of the body. The body. The appendChild (enclosing containerEl); } } show({type = 'info', text = ' '{}}})Copy the code

Thus, calling const message = new Message() automatically inserts a message-container node into the DOM. So, the most important thing is our show method:

  1. Create a message nodeAnd append it tomessage-containerThe end of the container.
  2. Set a time at which messages are automatically removed.
  3. Listen for the “close button” click event to allow the user to manually remove the message.

Let’s take it one step at a time.

4.1 Create a message node and append it tomessage-containerThe end of the container.
Class Message {// omit... show({type = 'info', text = ' '}) {// Create an Element objectlet messageEl = document.createElement('div'); Messageel.classname = messageel.className = messageel.className = messageel.className = messageel.className = messageel.className = messageel.className ='message move-in'; // Messageel. innerHTML = '<span class="icon icon-${type}"></span>
            <div class="text">${text}</div>
            <div class="close icon icon-close"></div> `; / / appended to the message at the end of the container / / this. ContainerEl attributes are we in the constructor to create the message - the container vessel enclosing containerEl. The appendChild (messageEl); }Copy the code

Let’s try calling ~

<! -- message.html --> <! -- ellipsis... --> <button class="btn"</button> <script> // message can be defined as a global object that can be called directly in a project. const message = new Message(); document.querySelector('.btn').addEventListener('click', () => {
            message.show({
                type: 'success',
                text: 'Point a concern not to get lost ~'
            });
        });
        
    </script>
Copy the code

4.2 Set a time at which the message will be automatically removed.
// message.js class message {// omit... show({type = 'info', text = ' ', duration = 2000}) {// omit... / / usesetTimeout to make a timersetTimeout(() => {// The Element object has a remove method inside it that removes the Element from the DOM tree! messageEl.remove(); }, duration); }}Copy the code

move-out
message
message

// message.js class message {// omit... show({type = 'info', text = ' ', duration = 2000}) {// omit... / / usesetTimeout to make a timersetTimeout (() = > {/ / the first move - in the pop-up animation classes to remove, or we'll have a problem, can test our messageEl. Under the className = messageEl. ClassName. Replace ('move-in'.' '); // Add a move-out class messageel.className +='move-out'; // This is the place to listen for the end-of-animation event and remove the message from the DOM tree after the animation ends. / / if you are on the increase after the move - out direct call messageEl. Remove, then you won't see any animation messageEl. AddEventListener ('animationend'() => {// The Element object has a remove method that removes the Element from the DOM tree! messageEl.remove(); }); }, duration); }}Copy the code

Notice how the DOM tree changes:

4.3 Monitor “close button”clickEvent to allow the user to manually remove the message.

Sometimes, we want the message to remain displayed until the user manually closes it, so we first add a parameter that controls whether to display the close button.

// message.js class message {// omit... show({type = 'info', text = ' ', duration = 2000, closeable = false}) {// Create an Element objectlet messageEl = document.createElement('div'); Messageel.classname = messageel.className = messageel.className = messageel.className = messageel.className = messageel.className = messageel.className ='message move-in'; // Messageel. innerHTML = '<span class="icon icon-${type}"></span>
            <div class="text">${text}</div> `; // Whether to display the close buttonif(closeable) {// Create a close buttonlet closeEl = document.createElement('div');
            closeEl.className = 'close icon icon-close'; // Append the close button to the end of the message element messageel.appendChild (closeEl); Closeel.addeventlistener () {closeel.adDeventListener () {closeel.addeventListener ();'click', () => { this.close(messageEl) }); } // Append to message-container // this.containerEl property is the message-container we created in the constructor this.containerEl.appendChild(messageEl); // Set the timer only when duration is greater than 0, so our messages will always be displayedifDuration > 0) {// usesetTimeout to make a timersetTimeout(() => { this.close(messageEl); }, duration); }} /** * Close a message * Because the timer needs to remove the message, and then the user manually closes the event to remove the message, * @param {Element} messageEl */ close(messageEl) {// Remove the move-in animation class, otherwise there will be a problem. Can test the messageEl. ClassName = messageEl. ClassName. Replace ('move-in'.' '); // Add a move-out class messageel.className +='move-out'; // This is the place to listen for the end-of-animation event and remove the message from the DOM tree after the animation ends. / / if you are on the increase after the move - out direct call messageEl. Remove, then you won't see any animation messageEl. AddEventListener ('animationend'() => {// The Element object has a remove method that removes the Element from the DOM tree! messageEl.remove(); }); }}Copy the code

Let’s try calling ~

<! -- message.html --> <! -- ellipsis... --> <button class="btn"</button> <script> // message can be defined as a global object that can be called directly in a project. const message = new Message(); document.querySelector('.btn').addEventListener('click', () => {
            message.show({
                type: 'warning',
                text: 'Click the cross next to me.', duration: 0, // closeable:true, // Can be manually closed}); }); </script>Copy the code

In fact, it is almost written, but there are still some small problems, for example, when we pop up two or more messages, if the front message disappears, the following message will jump directly to the top position, very stiff, without any sliding, as shown in the picture:

We can use the transition property of the CSS to gradually reduce the height of the meesage, so that the following elements will gradually move up.

/* message.css */ /* omit... * /#message-container .message {
    background: #fff;
    margin: 10px 0;
    padding: 0 10px;
    height: 40px;
    box-shadow: 0 0 10px 0 #ccc;font-size: 14px; border-radius: 3px; /* Center the three elements inside the message vertically and horizontally */ display: flex; align-items: center; */ Transition: height 0.2s ease-in-out, margin 0.2s ease-in-out; } /* omit... * /Copy the code

Then we just need to make a change in the Message class’s close method:

Close (messageEl) {/ / the first move - in the pop-up animation classes to remove, or we'll have a problem, can test our messageEl. Under the className = messageEl. ClassName. Replace ('move-in'.' '); // Add a move-out class messageel.className +='move-out'; / / move - out after the animation to the height of the elements and margins are set to 0 / / since we set up the transition in the CSS properties, so there will be a transition animations messageEl. AddEventListener ('animationend', () => {
            messageEl.setAttribute('style'.'height: 0; margin: 0'); }); // This is the place to listen for the transition end-of-animation event and remove the message from the DOM tree after the animation. messageEl.addEventListener('transitionend'() => {// The Element object has a remove method that removes the Element from the DOM tree! messageEl.remove(); }); }Copy the code

See the effect:

At the end

Ok, basically already written, but for the compatibility of various browsers, I recommend you to use Babel transcoding, if you want to publish, you can use Webpack to pack JS and CSS together.

However, we still do not consider a scenario. The current close message is realized by calling the close method inside the object. What if we want the external control to close the message, for example, when I request the server to pop up a loading message, how can I close the message after the server returns data? Very simple, everyone to achieve it!

Can’t you take a look at this? ?