Backstory

I had scribbled this blog last year. Somehow I forgot to publish. During this quarantine time with lot of retrospection, I knew what I was missing.

Emphasizing On Writing Tests

I am big fan of tests. Tests not only test your code but also tell others how your code works. (Disclaimer: Provided enough tests)

There is the famous TDD in agile. Writing failing tests before writing the code, which gives lot of clarity for implementation. It gives you confidence. Then there is BDD which let’s tech and non-tech users to write tests using common framework. “Given-when-then” formula is most common for BDD.Writing tests gives you confidence. You should write units tests, integration tests, test with mocks and all other useful tests.

Win situtation

Tests helps in code reviews, tests help in refactoring code. Tests helps to speed up new dependent features. Stable code base must have tests (IMHO). As architecture evolves we need to find ways on how we can improve writing tests. Tests for UI, tests for micro-services, monitoring tests, snapshot tests, selenium tests.

back to tests

Till next time.

Your own personal ngrok.

Introduction

There are tons of service which provide you the way to expose your localhost on internet. We need it for testing 3rd party APIs or show casing current development server. For example the most popular ones are:

  1. ngrok
  2. localtunnel
  3. pagekite

So why reinvent the wheel? Because you gotta get to the roots and understand the simple complexity. It’s no big deal.

Prerequisites

So what all do we need?

  1. A Server (I will using $5 DO server)
  2. A Domain
  3. Curiosity and Patience

What are the benefits of this setup? You may ask.

  • Total Custom Domain and as many domains you want.
  • No limits on usages
  • More than other services’ paid plans

Let’s get started, shall we?

It’s just a 4 steps setup

  1. Setup the listening port using nginx on the server associating it with the subdomain.
  2. Add subdomain in DNS(We will be using awesome Digital Ocean DNS)
  3. Add SSL certificate provided by our favorite Let’s Encrypt
  4. Forward the traffic using amazing SSH command.

Step 1 - Configuring server with nginx to serve traffic for the subdomain

Please install nginx on the server first. You need to add one config file for nginx in sites-available folder of /etc/nginx assuming you are using Ubutnu. The nginx config file looks like below.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    server_name 42.igauravsehrawat.com;

    location / {
        proxy_pass http://localhost:4242;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        # Enables WS support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

server {
    server_name www.42.igauravsehrawat.com;

    location / {
        proxy_pass http://localhost:4242;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        # Enables WS support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

Let’s take a look at config.

  1. There is server_name directive which tell what domain to serve the request, listen directive for which port to listen on the domain.
  2. The location directive is important here, here we are just proxing request from localhost with port 4242. This port is important since we will be directing our localhost traffic to it. Then there is setting header on the request.
  3. Rest of directive is enabling websocket support on this setup by upgrading the connection. That’s all.

Note: You need to change server_name as per your required sub-domain instead of 42.igauravsehrawat.com.

Step 2 - Configuring Subdomain on DNS

Using Digital Ocean DNS Service, we will add a subdomain of our choice, it be 42.igauravsehrawat.com as done in step 1

For adding a subdomain, we will create 3 records(A Record, AAAA record, CNAME record) A and AAAA records will redirect to our digital ocean server as shown below. A & AAAA records

CNAME is just an alias of our A/AAAA record. All three will appear as shown below All three

So that’s all here, you should be able to access your sub-domain over internet, showing you 502 Bad Gateway page, which is expected since nothing is being forwarded to port 4242.

Step 3 - Enabling SSL

Enabling SSL is piece of cake, more easy than that.Follow the certbot installation sets from here

Then run the magic command for nginx sudo certbot --nginx

You will see something like below certbot-run

Run it twice, one for sub-domain and one for CNAME(alias).

Now you should see this we you go to your sub-domain www.42.igauravsehrawat.com expected-bad-gateway This is expected since nothing is running on port 4242 of localhost at the server.

Woof, we are just one step away from the happy dance.

Step 4 - SSH Magic

Just one command to rule them all

ssh -N -R localhost:4242:localhost:3000 root@42.igauravsehrawat.com

-N tells “Do not execute a remote command. This is useful for just forwarding ports.” -R tells “Connections to the given TCP port or Unix socket on the remote (server) host are to be forwarded to the local side.”

First localhost is of the server and second is of local machine, then the address of your server. If you set up the SSH keys(Recommended) it would be seamless, no password, nothing.

That’s it.

Happy Dance as promised.

happy-dance

Wait, wait, we haven’t tested yet.

Testing

For testing I will just create a react app using create-react-app and do npm start to run it on port 3000(default). And then run the magic SSH command ssh -N -R localhost:4242:localhost:3000 root@42.igauravsehrawat.com

See it in action localhost-on-internet

Conclusion

With power of just one server, it is not that hard to have your own setup. You just have your own customized, SSL enable sub domain with no restrictions.

This puzzle is from my mentor and I like how it is very pragmatic and not just a brain-teaser. So the official language of the puzzle is as below

Matter of Life or Death

An evil wizard imprisoned 64 mathematicians. The wizard announces: “Tomorrow I will have you all stand in a line, one behind the other, and put a hat on each of your heads. The hat will be either black or white. You can see the hats of everyone in front of you but you can’t see your own and you can’t turn behind. Starting from the last person in the line, I will ask the color of the hat that is on your head. You are allowed to say aloud single word answer: “white” or “black”. If you get it right, you will be released. Else you will be shot dead”. The geeks have a night to discuss their strategy. What strategy should the geeks develop so that largest number of them can survive!

Some clarifications:

  1. One don’t know what happened to previous person, all you can do is see in-front of you and “hear the response of all other people”.
  2. You can’t see the shadow of person behind you.
  3. Any other social hack, you might think of.

Let’s focus on the question again.

What strategy should the geeks develop so that largest number of them can survive!

The obvious possibility of surviving is 50% without doubt, how??. Well a group of 2 can save one of the person for sure i.e one person from the group has to tell right color to other person.

Now first the hint for better solution:

You can save significantly high number of people.


Try to solve the puzzle yourself first.



Ok fine, see the solution now.


Rang a bell??

Let’s draw the scenario

|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|

|-64-|-63-|-62-|-61-|-60-|-59-|-58-|-57-|-56-|-55-|-54-|-53-|-52-|-51-|-50-|-49-|-48-|-47-|-46-|-45-|-44-|-43-|-42-|-41-|-40-|-39-|-38-|-37-|-36-|-35-|-34-|-33-|-32-|-31-|-30-|-29-|-28-|-27-|-26-|-25-|-24-|-23-|-22-|-21-|-20-|-19-|-18-|-17-|-16-|-15-|-14-|-13-|-12-|-11-|-10-|-9-|-8-|-7-|
|-6-|-5-|-4-|-3-|-2-|-1-|

64 people, think of it as an array.

We have signal other people, what’s better than unity and forming a group? So we group people and try to signal more than one people at once.

Another hint:

Black and white! Two variables, can do anything with bits, binary?

2 people signal group

A group of two people can signal 4 four people using 2 bits. 00, 01, 10, 11.

  1. So they can signal if all people’s hat have same color using 00
  2. They can count no. of color of either pre-decided color, let’s white is the color.
  3. If the signal is black-black i.e 00, it means everyone is white so the all four people will say white.
  4. If the signal is white-black i.e 10, it means the first person can count no of white and black infront of him, if there are 2 white hats then he will say black, if there is one white hat in front of him, he way say white.
  5. Next person will account the choice of previous and do the calculation similarly.

Whoaa! never thought of it before, thought whatever is in destiny will happen. LOL.

So out of 64, 6 people can form the signal group and can give 64 kinds of signals i.e counting/numbers.

6 people signal group

Can save at-least 58 people.

So now that’s a life saving puzzle, should put enough interest to solve more puzzles.

Humor

There are 10 types of people in the world, one who understands binary and the rest who doesn’t.

Till next time.

CSS is amazing(Human err).

If it doesn’t work, you know that it is confused and won’t give an error. This confusion could have been solved by giving error/warning but CSS choose not too. That’s why all this hate for CSS on internet.

Let’s come to drawing a horizontal tree with a root in center, because WHY not. The tree structure should be like

Centered horizontal tree

A node can have multiple children, in simple terms and each tree will be centered around root. Don’t bother about responsive-ness for now, it will require some javascript and SVG which we aren’t going to do for now.

Concept(s) needed

CSS pseudo elements

What is a pseudo element?

You might have used pseudo classes abundantly, pseudo elements are similar to pseudo classes but they are used to style specific part of the selected item. For eg. :before/::before, :after/::after, :placeholder, :grammar-error etc.

That’s it, all we need is pseudo elements(as the heading states) to make the branches out of the nodes, either horizontal or vertical. Also knowledge of relative positioning and absolute positioning will come handy.

We will make this tree in parts, right part has root included itself. First lets draw the tree in crude way.

Tree skeleton

The tree skeleton is like something below:

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
    <body>
        <div id="wrapper">
          <div id="root-left">
              <div class="branch-inverse l1">
                <div class="entry-inverse">
                  <div class="branch-inverse l2">
                    <div class="entry-inverse">
                      <div class="branch-inverse l3">
                        <div class="entry-inverse sole"></div>
                      </div>
                    </div>
                    <div class="entry-inverse"></div>
                  </div>
                </div>
                <div class="entry-inverse">
                  <div class="branch-inverse l2">
                    <div class="entry-inverse sole"></div>
                  </div>
                </div>
              </div>
          </div>
          <div id="main-root"><span class="label">Root</span></div>
          <div id="root-right">
            <!-- <span class="label">Root</span> -->
              <div class="branch l1">
                <div class="entry">
                  <div class="branch l2">
                    <div class="entry"></div>
                    <div class="entry"></div>
                  </div>
                </div>
                <div class="entry">
                  <div class="branch l2">
                    <div class="entry"></div>
                    <div class="entry">
                      <div class="branch l3">
                        <div class="entry sole"></div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
        </div>
    </body>

which will look like

Tree skeleton

So we have branches, which have entry and entry can other branches and entries as its child(ren). Let’s first understand how CSS will be applied, CSS will be applied from inner most tag to other most tag, so the height of branches will add up and each branch’s height will be cumulative from previous height.

We have fixed the node’s width to 150 px here also incoming connection and outgoing connection are 50 px each, therefore a branch will be 250 px wide.

Let’s come to drawing connections, we have entry as node which is our reference for connections, we will create incoming connection on the right tree using :after pseudo element. For branch’s height we will use :before pseudo element on entry element. It’s possible to swap :before and :after but need to some alignment. Branch has the outgoing connection.

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
    .entry:before {
      content: "";
      height: 100%;
      position: absolute;
      border-left: 2px solid black;
      width: 50px;
      left: -50px;
    }

    .entry:after {
      content: "";
      width: 50px;
      border-top: 2px solid aqua;
      position: absolute;
      left: -50px;
      top: 50%;
      margin-top: 1px;
    }

    .branch:before {
  content: "";
  width: 50px;
  border-top: 2px solid magenta;
  position: absolute;
  left: -100px;
  top: 50%;
  margin-top: 1px;
}

Think pseudo elements as box and then choose their side, that’s what we have done, for incoming connection we chose border-top, for branch height we chose border-left. See the image how boxes looks like.

Box border

Notice how positioning is done using top and left.

Tree skeleton demo is here..

Nodes on the tree

Now we can add label on the right and tree will look Neat like this.

Label will be like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    .label {
      display: block;
      min-width: 150px;
      padding: 5px 10px;
      line-height: 20px;
      text-align: center;
      border: 2px solid silver;
      border-radius: 5px;
      position: absolute;
      left: 0;
      top: 50%;
      margin-top: -15px;
      overflow-wrap: break-word;
      background-color: lightblue;
    }

Notice the top and margin-top to make this label centered in the div using position absolute.

Whew, that’s all.

The left sub tree is easy and left as an exercise to the reader.(Just kidding.)

Left sub tree

For the left part construct the tree same as right tree but some CSS needs to be reversed. The quickest way to understand this is see the diff of two tree’s 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
    -.branch {
    +.branch-inverse {
       position: relative;
    -  margin-left: 250px;
    +  margin-right: 250px;
     }
     
    -
    -.branch:before {
    +.branch-inverse:before {
       content: "";
       width: 50px;
       border-top: 2px solid magenta;
       position: absolute;
    -  left: -100px;
    +  right: -100px;
       top: 50%;
       margin-top: 1px;
     }
     
    -.entry:before {
    +.entry-inverse:before {
       content: "";
       height: 100%;
    +  border-right: 2px solid black;
       position: absolute;
    -  border-left: 2px solid black;
    -  left: -50px;
    +  right: -50px;
     }
     
    -.entry:after {
    +.entry-inverse:after {
       content: "";
       width: 50px;
       border-top: 2px solid aqua;
       position: absolute;
    -  left: -50px;
    +  right: -50px;
       top: 50%;
       margin-top: 1px;
     }
     
    -.label {
    +.label-inverse {
       display: block;
       min-width: 150px;
       padding: 5px 10px;
    @@ -48,7 +46,7 @@
       border: 2px solid silver;
       border-radius: 5px;
       position: absolute;
    -  left: 0;
    +  right: 0;
       top: 50%;
       margin-top: -15px;
       overflow-wrap: break-word;

Now if you notice branch is place with margin of 250 px from right in its position, the magenta color connection is placed at 100 px to right from nodes which is opposite of what is on right side tree. .entry-inverse is now positioned with respect to right and .label-inverse is also position with respect to right.

CSS polishing

Now comes the task of providing finish to the product, we want round curves instead of bland straight lines in the tree. It’s important to note on :first-child and :last-child need to have rounded corners. Like the pic given below.

Round border

Demo code is present here..

For this we have provided round border to straight line and incoming connection to the node for their :first-child and :last-child with :before and :after property individually. Also we will truncate the height to 50% and position it 50% from top.

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
.entry-inverse:first-child:before {
  width: 10px;
  height: 50%;
  top: 50%;
  margin-top: 2px;
  border: 1px dashed red;
  border-radius: 0 10px 0 0;
}


.entry-inverse:first-child:after {
  height: 10px;
  border-radius: 0 10px 0 0;
  border: 1px dashed blue;
}

.entry-inverse:last-child:before {
  width: 10px;
  height: 50%;
  border-radius: 0 0 10px 0;
  border: 1px dashed violet;
}

.entry-inverse:last-child:after {
  height: 10px;
  border-top: none;
  border-bottom: 2px solid black;
  border-radius: 0 0 10px 0;
  margin-top: -10px;
  border: 1px dashed orange;
}

If now you remove the dashed lines, you will get the straight lines except for the sole children, for that we don’t want round corner and not want to display straight line. So the below CSS will help that.

1
2
3
4
5
6
7
8
9
10
.entry-inverse.sole:after {
  width: 50px;
  height: 0px;
  margin-top: 1px;
  border-radius: 0px
}

.entry-inverse.sole:before {
  display: none;
}

Right side rounded corners

Similarly the right side tree rounded corners can be handled. See the diff below for instant salvation.

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
--- .entry-inverse:first-child:before { (Selection)
+++ (clipboard)
@@ -1,42 +1,43 @@
-.entry-inverse:first-child:before {
+.entry:first-child:before {
   width: 10px;
   height: 50%;
   top: 50%;
   margin-top: 2px;
   border: 1px dashed red;
-  border-radius: 0 10px 0 0;
+  border-radius: 10px 0 0 0;
 }
 
 
-.entry-inverse:first-child:after {
+.entry:first-child:after {
   height: 10px;
-  border-radius: 0 10px 0 0;
+  border-radius: 10px 0 0 0;
   border: 1px dashed blue;
 }
 
-.entry-inverse:last-child:before {
+.entry:last-child:before {
   width: 10px;
   height: 50%;
-  border-radius: 0 0 10px 0;
+  border-radius: 0 0 0 10px;
   border: 1px dashed violet;
 }
 
-.entry-inverse:last-child:after {
+.entry:last-child:after {
   height: 10px;
   border-top: none;
   border-bottom: 2px solid black;
-  border-radius: 0 0 10px 0;
+  border-radius: 0 0 0 10px;
   margin-top: -10px;
   border: 1px dashed orange;
 }
 
-.entry-inverse.sole:after {
+.entry.sole:after {
   width: 50px;
   height: 0px;
   margin-top: 1px;
   border-radius: 0px
 }
 
-.entry-inverse.sole:before {
+.entry.sole:before {
   display: none;
-}
+}
+

As you can see only border radius has been changed.

Conclusion

That’s concludes our tree and sweet CSS lesson. Neat polished demo is here.

Remember you need patience with CSS else just go for SCSS/Sass. :)

And Of course it was there on the web but only the half part. Till next time.

So first technical blog of the year. It’s simple blog as it seems from the title. UX is important here too, that will also be the focus apart from code. This blog is from a task given to me by my mentor.

So let’s see the flow.

  1. We want to have two lists and item can be added and deleted in each list.
  2. An item can also be moved to other list by dragging it over to other list’s place.
  3. We want to create a placeholder where the item is going to be placed.
  4. We want to slightly tilt the item being dragged to show distinction.

So we need to know drag and drop API’s from MDN(a good documentation place). There are multiple API’s but we just need 4 of those. Those 4 are ondrag, ‘ondragover’, ‘ondragleave’, ‘ondragend’. We don’t need ondrop since we are using placeholder to mark the position to place the elements.

So let’s define the behavior.

  1. What happens when your start dragging?

Visual clue from rest of items present there(like tilted).

  1. How to show the place when an item is going to be dropped.

Show a visual clue like placeholder.

  1. What happens if drag fails(i.e it goes out of browser)?

It should come to the last visual clue it is supposed to come.

So our drag-able element/item looks like:

1
2
3
4
5
6
7
8
        <div id="item-template-container" class="item-template-container" draggable="true" ondragstart="listItemDragStartHandler(event);" ondragover="dragoverHandler(event);" ondragend="dragEndHandler(event);" ondragleave="dragLeaveHandler(event);" ondrop="onDropHandler(event);">
            <div class="item-list-element">
                <div class="item-text"></div>
                <div class="delete-item-div">
                    <button class="delete-item" onclick="delete_item(event);">Delete</button>
                </div>
            </div>
        </div>

And the 4 drag function looks like below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function removePlaceholder() {
    if(document.getElementById('grayed-empty-node') !== null) {
        document.getElementById('grayed-empty-node').remove();
    }
}

function insertPlaceholder(currentElement, which) {
    var isGrayEmptyNodeThere = document.getElementById('grayed-empty-node');
    if(isGrayEmptyNodeThere === null) {
        var listContainer = currentElement.parentNode;
        var emptyNode = document.getElementById('empty-node').cloneNode(true);
        emptyNode.removeAttribute('id');
        emptyNode.setAttribute('id', 'grayed-empty-node');
        emptyNode.setAttribute('class', 'fade-in');
        emptyNode.style.height = String(heightWidth[0]) + 'px';
        emptyNode.style.width = String(heightWidth[1]) + 'px';
        if (which == 'over')
            listContainer.insertBefore(emptyNode, currentElement);
        else if (which == 'leave')
            listContainer.insertBefore(emptyNode, null);
    }
}

The above ones are 2 helper functions for removing and inserting placeholder as the name suggests. Also you will notice there is event.preventDefault used because some OS browsers have other behaviors for dragging an object. So we will avoid that.

listItemDragStartHandler (funny name) is an ondrag method, here we want to save the dragged item in global var source and height & width of dragged element for placeholder. Then we attach a custom image for dragging which is bit tilted to show some relevance which is also stored in a global var dragImageSource.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function listItemDragStartHandler(event) {
    source = event.currentTarget;
    heightWidth = [];
    heightWidth.push(source.offsetHeight);
    heightWidth.push(source.offsetWidth);
    event.dataTransfer.setData('text/html', event.currentTarget.innerHTML);
    event.dataTransfer.effectAllowed = 'move';
    // Drag image logic
    dragImageSource = source.cloneNode(true);
    dragImageSource.style.position = 'absolute';
    // Don't show the element
    dragImageSource.style.top = '0px';
    dragImageSource.style.left = '-' + String(window.innerWidth) + 'px';
    // dragImageSource.style.left = '-100px';
    var toTiltElement = dragImageSource.getElementsByClassName('item-list-element')[0];
    toTiltElement.style.transform = 'rotate(5deg)';
    document.body.append(dragImageSource);
    event.dataTransfer.setDragImage(dragImageSource, heightWidth[1]/2, heightWidth[0]/2);
}

We have ondragover and ondragleave methods below which basically insert placeholder according to latest movement. In dragoverHandler we hide the existing item from it’s original place and check if we are dragging over a new element using lastDragOverElementId, only then we remove existing placeholder and insert new placeholder.

1
2
3
4
5
6
7
8
9
10
11
12
function dragoverHandler(event) {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
    source.style.display = 'none';
    var currentElement = event.currentTarget;
    var listContainer = currentElement.parentNode;
    if (lastDragOverElementId != currentElement.getAttribute('id')) {
        lastDragOverElementId = currentElement.getAttribute('id');
        removePlaceholder();
        insertPlaceholder(currentElement, 'over');
    }
}

In dragLeaveHandler, this is specifically needed to have a placeholder at the end of the list because we are always appending above existing drag over element. But when element exit the list it’s place should be at end. So here we check if it has left the last visible element(remember we still have one hidden original element in the list).

Also you will notice we have a timeout here for disabling ondragleave for few moments, this is for stop a chain reaction between ondragover and ondragleave since a list element passes through the dragged element when placing the placeholder. Then at-last we set lastDragOverElementId to null since a new dragged element will hover over other element now or re-enter.

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
function dragLeaveHandler(event) {
    event.preventDefault();
    var currentElement = event.currentTarget;
    var listContainer = currentElement.parentNode;
    var elementList = listContainer.getElementsByClassName('item-template-container');
    var visibleElementList = [];
    var i = 0;
    for (i=0; i<elementList.length; i++) {
        if (elementList[i].style.display !== 'none') {
            visibleElementList.push(elementList[i]);
        }
    }
    // Last element ondragleave handler should be delayed, otherwise it
    // would spin off chain reaction.
    var lastVisibleElement = visibleElementList[visibleElementList.length - 1];
    // Basically last element
    if (currentElement.id === lastVisibleElement.getAttribute('id')) {
        lastVisibleElement.removeAttribute('ondragleave');
        removePlaceholder();
        insertPlaceholder(currentElement, 'leave');
        // This delayed the chain reaction
        setTimeout(function() {
            visibleElementList[visibleElementList.length - 1].setAttribute('ondragleave', 'dragLeaveHandler(event);');
        }, 500);
    // When event is on last element set the lastDragOverElementId to null
    // then dragover to lastVisibleElement(second last element) can be handled
    // otherwise dragover to second last element won't show placeholder.
    lastDragOverElementId = null;
    }
}

This handler is needed to place the dragged element to placeholder place irrespective of drop, so drop is mouse release operation here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function dragEndHandler(event) {
    event.preventDefault();
    // Check the dropEffect
    dragImageSource.remove();
    var listElement = document.getElementById('grayed-empty-node')
    var listContainer = listElement.parentNode;
    if (event.dataTransfer.dropEffect === 'none' ||
        event.dataTransfer.dropEffect === 'move') {
        var grayEmptyNode = document.getElementById('grayed-empty-node');
        // When grayEmptyNode is null, that will append at the end.
        listContainer.insertBefore(source, grayEmptyNode);
        source.style.display = '';
        removePlaceholder();
    }
}

Ahoy!! I can’t understand without a demo. I have a demo here. There are few things I haven’t mentioned like fading of placeholder and using livereload while developing this.

That’s all for today.