Tag Archives: java script

Unique-paths

Task

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).

How many possible unique paths are there?

Note: m and n will be at most 100.

Example 1:

Input:

 m = 3, n = 2

Output:

 3

Explanation:

From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right

Example 2:

Input:

 m = 7, n = 3

Output:

 28

This problem was taken from Leetcode unique paths and Leetcode_unique_paths_part_II

Solution

Since we can move only right or down on every cell in the first row we will have only one place from where we can come and this is the cell before. And same for the first vertical row.

Unique Paths

Then after we figured out that there is only one way to reach each cell in the first row and the first column (which is from the cell before) we could start calculating possible lays to go to the next cells.
Let’s look at the cell in the second row and second column.There are actually two possible ways to go there: from the cell above, and the cell before, so 2 possible ways. (figure below).
The cell in the third column on the second row: same 1 way from the cell above, and from the cell before. But since there are already 2 ways to reach the cell before the total ways to reach this cell is: 1 + 2 = 3.

 

The solution:

/**
 * @param {number} m
 * @param {number} n
 * @return {number}
 */
var uniquePaths = function(m, n) {
    
    var memo = [];

    for(var i=0;i < n; i ++) {
        for(var j = 0; j < m; j ++) {
            var index = (i * m) + j; 
            if(i == 0) {
                memo[index] = 1;
            }
            else if(j == 0) {
                memo[index] = 1;
            }
            else {
                var up = index - m;
                var left = index - 1;
                memo[index] = memo[up] + memo[left];
            }
        }
    }
    return memo[memo.length - 1];
}

console.log(uniquePaths(7,3));

 

Unique paths with obstacles.

 

/**
 * @param {number[][]} obstacleGrid
 * @return {number}
 */
var uniquePathsWithObstacles = function(obstacleGrid) {
    var m = obstacleGrid[0].length;
    var n = obstacleGrid.length;
    var row = 0;
    if(obstacleGrid[0][0] == 1)
        return 0;

    var memo = [];

    for(var i=0;i < n; i ++) {
        for(var j = 0; j < m; j ++) {
            var index = (i * m) + j; 
            if(i == 0) {
                if(obstacleGrid[i][j] == 1 || (j > 0 && memo[index -1] == 0))
                    memo[index] = 0;
                else                
                    memo[index] = 1;
            }
            else if(j == 0) {
                if(obstacleGrid[i][j] == 1 || (i > 0 && memo[index - m] == 0))
                    memo[index] = 0;
                else                
                    memo[index] = 1;
            }
            else {
                var up = index - m;
                var left = index - 1;
                if(obstacleGrid[i][j] == 1)
                    memo[index] = 0;
                else
                    memo[index] = memo[up] + memo[left];
            }
            row += memo[index] ? 0 : 1;
        }
        if(row == m)
            return 0;            
        row = 0;
    }
    return memo[memo.length - 1];
};


var grid = [
  [0,0,0],
  [0,1,0],
  [0,0,0]
];

console.log(uniquePathsWithObstacles(grid));

 

Intersection of Two Linked Lists

Task

Write a program to find the node at which the intersection of two singly linked lists begins.

For example, the following two linked lists:

begin to intersect at node c1.

 

Example 1:

This problem was taken from Leetcode

Solution

We are not asked to compare the values inside the linked lists but the list node objects, so we could ignore the values of the list.

Approach 1: Brute Force

For each node ai in list A, traverse the entire list B and check if any node in list B coincides with ai.

Complexity Analysis

  • Time complexity : O(mn).
  • Space complexity : O(1).

Approach 2: Calculating the length of the two linked lists and compare the elements that could potentially intersect.

  • Time complexity : O(m+n).
  • Space complexity : O(m) or O(n).
 function ListNode(val) {
      this.val = val;
      this.next = null;
 }

headA = new ListNode(4);
headA.next = new ListNode(1);


headB = new ListNode(5);
headB.next = new ListNode(0);
headB.next.next = new ListNode(1);

headA.next.next = headB.next.next.next = new ListNode(8);
headB.next.next.next.next = headA.next.next.next = new ListNode(4);
headB.next.next.next.next.next = headA.next.next.next.next = new ListNode(5);


/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */


var getIntersectionNode = function(headA, headB) {
      let node = headA;
      let countA = 0;
      while(node != null ) {            
            node = node.next;           
            countA ++;       
      }    

      node = headB;
      let countB = 0;
      while(node != null ) {            
            node = node.next;           
            countB ++;       
      }    


      let longList, shortList, diff, iteratorLongLength,iteratorShortLength;
      if(countA > countB) 
        longList = headA, shortList = headB,  diff = countA - countB;
      else
        longList = headB, shortList = headA, diff =  countB - countA;


      let i = 0;
      while(shortList != null) {
        if(i < diff ) {
              longList = longList.next; 
        }
        else {
              console.log("long list, short list", longList.val, shortList.val);
              if(longList == shortList) 
                  return longList.val;
              longList = longList.next; 
              shortList = shortList.next;     
        }        

      i ++;      
      }
};


console.log (getIntersectionNode(headA, headB) );

what we just did:
– we calculated the length of the first list to be 5 and the second 6 (first and the second loop)
– the third loop is doing two things:
– first since the difference between the shorter and the longer list is 1 we move the cursor to the second element of the longer list (lines 59-61)
– after we position the longer list cursor at the second element we could start comparing (line 64)

If we execute the function we will see this result:

long list, short list 0 4
long list, short list 1 1
long list, short list 8 8

And the third element is exactly where the intersection is.

Approach 3: Traverse both lists and when reaching the end of each one, move the pointer to the opposite list and traverse again till intersection is found.

  • Time complexity : O(m+n).
  • Space complexity : O(m) or O(n).

This basically is the same concept as in the example above, just written in a bit more elegant way.

 function ListNode(val) {
      this.val = val;
      this.next = null;
 }

headA = new ListNode(4);
headA.next = new ListNode(1);


headB = new ListNode(5);
headB.next = new ListNode(0);
headB.next.next = new ListNode(1);

headA.next.next = headB.next.next.next = new ListNode(8);
headB.next.next.next.next = headA.next.next.next = new ListNode(4);
headB.next.next.next.next.next = headA.next.next.next.next = new ListNode(5);


/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */


var getIntersectionNode = function(headA, headB) {
      let nodeA = headA;
      let nodeB = headB;
      let swapA = false;
      let swapB = false;
      var i = 0;
      while(nodeA != null && nodeB!=null ) {
            // node A            
            if(!swapA && nodeA.next == null) {
                  nodeA = headB;
                  swapA = true;
            }
            else {
                  nodeA = nodeA.next;
            }
            // node B
            if(!swapB && nodeB.next == null) {
                  nodeB = headA;
                  swapB = true;
            }
            else {
                  nodeB = nodeB.next;
            }            


            if(nodeA === nodeB)
                  return nodeA.val;

      }    
};


console.log (getIntersectionNode(headA, headB) );

what we just did:
– traverse listA and listB till we reach the end of each one (lines 47 and 55) .
– once we reach the end of each list we point the cursor to the opposite list (lines 43 and 51)

Approach 4: Hash Table

Traverse list A and store the address / reference to each node in a hash set. Then check every node bi in list B: if bi appears in the hash set, then bi is the intersection node.

Complexity Analysis

  • Time complexity : O(m+n).
  • Space complexity : O(m) or O(n).
 function ListNode(val) {
      this.val = val;
      this.next = null;
 }

// link-list A: [4,1,8,4,5]
// link-list B: [5,0,1,8,4,5]

headA = new ListNode(4);
headA.next = new ListNode(1);


headB = new ListNode(5);
headB.next = new ListNode(0);
headB.next.next = new ListNode(1);

headA.next.next = headB.next.next.next = new ListNode(8);
headB.next.next.next.next = headA.next.next.next = new ListNode(4);
headB.next.next.next.next.next = headA.next.next.next.next = new ListNode(5);


/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */


var getIntersectionNode = function(headA, headB) {
      let hashMap = {}; 
      let node = headA;
      while(node != null ) {
            
            hashMap[node.val] = node;
            node = node.next;                  
      }    

      node = headB;
      while(node != null) {
            let val = node.val;
            if(hashMap[val] == node) {
                  return val;
            }
            node = node.next;
      }
};

console.log ("result: ", getIntersectionNode(headA, headB) );

 

Check if string has all unique characters

Task

Implement an algorithm to determine if a string (of characters from ‘a’ to ‘z’) has all unique characters or not.

Example 1:

var s = "abcde";
returns true;

Example 2:

var s = "abcade";
returns false;

Solution

Solution 1: The brute force solution will be to iterate through all characters and compare with all other characters.

function areCharactersUnique(s) {
    for(let i=0; i < s.length; i++) {
        for(let j=0; j < s.length; j++) {
            if(i == j)
                continue;
            if(s[i] == s[j]) {
                return false;
            }
        }

    }
    return true;
}

var s = "abcade";

console.log(areCharactersUnique(s));

Solution 2: Using an array (or hashMap table) with key equal to the ASCII character code.

function areCharactersUnique(s) {
    var checker = new Array(26);
    for(let i=0; i < s.length; i++) {
        var pos = s[i].charCodeAt(0) - 'a'.charCodeAt(0);
        if(typeof checker[pos] != 'undefined') {
            return false;
        }
        checker[pos] = 1;
    }
    return true;
}

var s = "abcde";

console.log(areCharactersUnique(s));

Let’s make it more challenging and prohibit the use of additional data structures like count array, hash, etc.

Solution 3: Using bitwise operations to store into 32 bit if one of all 26 characters is presented or not.
We have 26 letters (from a to z). Let’s imagine that we could have 26 empty slots that we could set up to true if the character exists, pretty much as if we have a hashTable.
for simplicity I will use only 6 slots (from a to G) instead of all 26 that represent the whole alphabet.
In addition we have to mention that the slots are actually 32 (this is usually the length of an integer in JavaScript but we need only 26)

6 5 4 3 2 1 0
G F E D C B A
false false false false false false false

Given the string: ‘ABCG’ for example we will end up with this matrix.

6 5 4 3 2 1 0
G F E D C B A
true false false false true true true

But this could be stored into 32bit value using bitwise operations. The binary representation of the matrix above will be:  1000111

The solution:

function areCharactersUnique(s) {
    var checker = 0;

    for(let i=0; i < s.length; i++) {
        // Charcode of a is 97 but we want to start with 0
        var val = s[i].charCodeAt(0) - 'a'.charCodeAt(0);
        // & - Sets each bit to 1 if both bits are 1
        // examples: 
        // 1 & 10 = 0
        // 1 & 101 = 1
        if(checker & ( 1 << val)) {
            return false;
        }

        // | - Sets each bit to 1 if one of two bits is 1
        // examples:
        // 1 | 10 = 11
        // 1 | 1 = 1
        checker = checker | ( 1 << val);
    }
    return true;
}

what we just did:
– we started with creating a loop to go through all characters
– we set up an empty value checker to store if the character is used or not (this is the binary representation of the matrix above)
– (line 6) grabbing the value for each letter in the string but removing ‘a’ = 97 so ‘a’ character will be equal to 0 and z = 26
– (line 19) we are setting the position of the character into the checker to true using bitwise shift left (1 << a) and preserving other already set positions using bitwise | ‘or’
– (line 11) using the same technique but with bitwise & ‘and’ we check if the character position is set to true or not.

Here is a step by  step example for ‘ABCG’ character:
– initially checker = 0 // or the binary representation is '00000000...0'
– we are going to insert a using Zero fill left shift. This is basically going to add ‘1’ followed by as many ‘0’ to the right of the checker as the value of ‘a’ is. In this case 0, so schecker = 1 // binary '00000000...1

– next step inserting ‘b’ follows the same procedure: b = 1, (1 << b) = 2 '000000...10' but we also want to preserve whatever was already inserted so we use bitwise ‘|’ ‘or’ which sets each bit to 1 if one of two bits is 1.
so
checker = checker | (1 << b) = 2
or the binary representation will be:
checker = '00000000...1' | (1 << b) = '000000000...11'

and the same for the rest of the characters

var a = 0;
var b = 1;
var c = 2;
var d = 3;
var e = 4;
var f = 5;
var g = 6;

var s = "abcg";
var checker = 0; 

checker = checker | (1 << a);   // 1
checker = checker | (1 << b);   // 11
checker = checker | (1 << c);   // 111
checker = checker | (1 << g);   // 1000111

Let’s modify the problem, and ask to return the index of the first unique character in the string.
For example for string ‘abcac’ the return will be the index of b – ‘1’
This problem is asked in Leetcode

/**
 * @param {string} s
 * @return {number}
 */
var firstUniqChar = function(s) {
    
    let lastSingle = null;
    let hashMap = {};
    for(var i = 0;i < s.length; i ++) {
        var val = s[i];
        hashMap[val] =  hashMap[val] == undefined ? i: 'not-unique';        
    }

    for(let i=0; i < s.length; i++) {
        var key = s[i];
        if(hashMap[key] != 'not-unique') {
            return hashMap[key];
        }
    }
    return -1;

}

 

LRU Cache

Task

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) – Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) – Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

The cache is initialized with a positive capacity.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

This problem was taken from Leetcode

Solution

The brute force solution will be to use array, to push every new element at the top, and on ‘get’ to pop out the element and to put it at the top of the array.

A better solution will be to use hashmap where the element retrieval will be O(1) (constant) but the hashmap is not keeping track of the order of the elements. To solve this problem we are going to link elements in the hashnmap table with double linked list, where each element will point to its previous and next sibling.
On every ‘get’ operation we are going to re-link the element and it’s siblings.

When the cache reaches the capacity we are going to remove the least used node from the bottom.

put(1,1) put(2,2) get(1) put(3,3) get(2) put(4,4) get(1) get(3) get(4)
return 1 -1 -1 3 4
result 1 2,1 1,2 3,1 => (2) 3,1 4,3 => (1) 4,3 3,4 4,3

 

class LRUCache {

  constructor(capacity) {
    
        this.head = null;
        this.tail = null;
        this.capacity = capacity;
        this.count = 0;
    this.hashMap  = new Map();    
  }

 
  get(key) {
    var node = this.hashMap.get(key);
    if(node) {
      if(node == this.head) {
        // node is already at the head, just return the value
        return node.val;
      }			
      if(this.tail == node && this.tail.prev) {
        // if the node is at the tail,
        // set tail to the previous node if it exists.
        this.tail = this.tail.prev;
        this.tail.next = null;
      }
      // link neibouring nodes together
      if(node.prev)
        node.prev.next = node.next;
      if(node.next)
        node.next.prev = node.prev;			
      // add the new head node
      node.prev = null;
      node.next = this.head;
      this.head.prev = node;
      this.head = node;

      return node.val;
    }
    return -1;
  }

  put(key, val) {
    this.count ++;
    var newNode = { key, val, prev: null, next: null };

    if(this.head == null) {
      // this.hashMap is empty creating new node
      this.head =  newNode;
      this.tail = newNode;
    }
    else {
      var oldNode = this.hashMap.get(key);
      if(oldNode) {
        // if node with the same key exists, 
        // clear prev and next pointers before deleting the node.
        if(oldNode.next) {
          if(oldNode.prev)
            oldNode.next.prev = oldNode.prev;
          else
            this.head = oldNode.next;
        }
        if(oldNode.prev) {					
          oldNode.prev.next = oldNode.next;
          if(oldNode == this.tail)
            this.tail = oldNode.prev;
        }
        // removing the node
        this.hashMap.delete(key);
        this.count --;				
      }

      // adding the new node and set up the pointers to it's neibouring nodes			
      var currentHead = this.head;
      currentHead.prev = newNode;				
      newNode.next = currentHead;
      this.head = newNode;

      if(this.tail == null)
        this.tail = currentHead;

      if(this.count == this.capacity + 1) {
        // remove last nove if over capacity
        var lastNode = this.tail;
        this.tail = lastNode.prev;
        if(!this.tail) {
          //debugger;
        }
        this.tail.next = null;
        this.hashMap.delete(lastNode.key);
        this.count --;
      }

    }
    this.hashMap.set(key, newNode);
    return null;
  }
}

 

Trapping Rain Water

Task

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.


image was borrowed from leetcode

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

Example:

Input:

 [0,1,0,2,1,0,1,3,2,1,2,1]

Output:

 6

This problem was taken from Leetcode

Solution

 

The brute force approach: for each element we go to the right and find the maximum height of the bar, then we go to the left and do the same.

For any element the maximum amount of the water that could be trapped will be the minimum of left height and right height, minus the height of the bar.

So for the array [0,1,0,2,1,0,1,3,2,1,2,1] we go all the way to the right and calculate the max right value, starting from first element ‘0’ max right will be 0. ‘1’ – max right is ‘1’ and so on.
We repeat the same from last element ‘1’ to the first one.

Then the trapped water for the first column will be: theArrayElement[n] –  min(maxRight, maxLeft)

the array 0 1 0 2 1 0 1 3 2 1 2 1
max right 0 1 1 2 2 2 2 3 3 3 3 3
max left 3 3 3 3 3 3 3 3 2 2 2 1
collected
water
0 0 1 0 1 2 1 0 0 1 0 0

The complexity will be O(n2)

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    if(height.length < 2)
        return 0;

    let findMaxLeft = function(idx, height) {
        let max = 0;
        for(let i =idx;i >= 0; i --) {
            max = Math.max(max, height[i]);
        }
        return max;
    }

    let findMaxRight = function(idx, height) {
        let max = 0;
        for(let i = idx;i < height.length; i ++) {
            max = Math.max(max, height[i]);
        }
        return max;
    }  

    let collectedWater = 0;
    for(let i = 0;i < height.length; i ++) {

        const maxLeft = findMaxLeft(i, height);
        const maxRight = findMaxRight(i, height);

        let min = Math.min(maxLeft, maxRight);
        collectedWater += (min - height[i]);
    }

    return collectedWater;
};

The better solution: find all max left and max right with one loop, then do a second loop for each element in the array, and calculate trapped water.

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    let maxLeftArray = [], maxRightArray = [];
    let maxLeft = 0, maxRight = 0;
    const ln = height.length;
    let trappedWater = 0;

    for(let i = 0;i < height.length; i ++) {
        maxLeftArray[i] = Math.max(height[i], maxLeft);
        maxLeft = maxLeftArray[i];

        maxRightArray[ln - i - 1] = Math.max(height[ln - i - 1], maxRight);
        maxRight = maxRightArray[ln - i - 1];
    }

    for(let i = 0;i < height.length; i ++) {
        trappedWater += Math.min(maxLeftArray[i], maxRightArray[i]) - height[i];
    }
    return trappedWater;

};
what we just did:

– With one loop find the max left and right bar on each side.
– for any element the maximum amount of the water that could be trapped will be the minimum of left height and right height, minus the height of the bar.

Web Components: create text field with autosuggest dropdown and dictionary

What are web components?

Web components are new reusable components that add new functionalities to a standard components (ie: img, input, table, h, p, div, etc.) or create new components using and combining the existing standard components. You could read more here: web components

How to create custom web component ?

The simplest idea of how to create a web component is this: (although this could be highly customised, and done in many different ways, for the purpose of this exercise to be as simple as possible we will explore the simplest approach):

  1. Create custom class that will handle the presentational logic of the component.
  2. Add the custom tag in the document.
  3. Add the styles and the HTML markup to the main DOM. This is usually done by adding the shadow DOM of the new component to the document’s DOM.

Let’s make a real useful web component, while going to the process

Create a Text Field component with auto suggest drop down drawer and dictionary of the suggested words.

Final product will look like this:

Day:
Month:

First We will

Create the HTML and the CSS layout.

Create a new file

./text-box-with-dropdown.js

var template = `
<style>
  .wrapper {
    display: inline-grid;
  }
  #drawer {
    cursor: pointer;
    border: 1px solid silver;
    background: #f4f4f4;
  }
  .selectedRow {
    color: white;
    background: #606062;
  }
  p {
    margin: 1px;
    border-bottom: 1px solid silver;
  }
  p:last-child {
    border-bottom: none;
  }  
</style>

<div class="wrapper">
  <input type="text" id='textfield'>
  <div id='drawer'>
  </div>
</div>
`;

We added the CSS, and the HTML markup which consists of a wrapper div, a text field, and a ‘drawer’ div, which will show the suggested words.

Add web component’s class.

Next, In the same file create a class to handle the events.

class TextboxWithDropdown extends HTMLElement {  

  constructor() {
    // Always call super first in constructor
    super();
    this._shadowRoot = this.attachShadow({ 'mode': 'open' });
  }

  connectedCallback() {  
    this._shadowRoot.innerHTML = template; 
  }
}

And now let’s attach the newly created web component to the document DOM

window.customElements.define('textbox-with-dropdown', TextboxWithDropdown);

Now we are going to create the actual HTML document to view the component.

./index.html

<!DOCTYPE html>
<html lang="en" prefix="og=https://ogp.me/ns#" itemType="https://schema.org/WebPage" data-reactroot=""></html>
  <head>
    <script src="./text-box-with-dropdown1.js"></script>
    <style>
      body {
            padding: 40px;
            padding-bottom: 40px;
            background-color: #f5f5f5;
      }    
    </style>
  </head>

  <body>
    Day: <textbox-with-dropdown id="txt1" dictionary="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" value='1'></textbox-with-dropdown>
    Month: <textbox-with-dropdown id="txt2" dictionary="January,February,March,April,May,June,July,August,September,October,November,December"></textbox-with-dropdown>
  </body>
</html>

 

What we just did:
– we created simple HTML document
– Added two instances of our new WEB component textbox-with-dropdown
– Passed a property with a list of all days of the month to the first component and set it’s value=’1′ which will be the default parameter.
– Passed months of the year as a property to the second component.

So far the component will be visible but not quite functional. Let’s add all that we need to make the component functional.

First let’s discuss connectedCallback hook. It is invoked each time the custom element is appended into a document-connected element, while attributeChangedCallback is invoked when one of the custom element’s attributes is added, removed, or changed. How we are going to use this? We are going to move all initialization logic to connectedCallback
Then we are going to use attributeChangedCallback to listen to attribute changes and update the component. For example if you use the dev tools and inspect the component and edit the dictionary attribute, our component will update accordingly.

Next we are going to add a function that will accept a prefix parameter, which will be whatever the user starts typing in the text field and will filter the dictionary and return only matching words (or numbers).

We will also add keyUp events, so the user will be able to use ‘up’ and ‘down’ keys to navigate through the words list, and in addition we will add mousemove events to highlight the words where the mouse is.

Go back to text-box-with-dropdown.js and add these functionalities.

./text-box-with-dropdown.js

var template = `
<style>
  .wrapper {
    display: inline-grid;
  }
  #drawer {
    cursor: pointer;
    border: 1px solid silver;
    background: #f4f4f4;
  }
  .selectedRow {
    color: white;
    background: #606062;
  }
  p {
    margin: 1px;
    border-bottom: 1px solid silver;
  }
  p:last-child {
    border-bottom: none;
  }  
</style>

<div class="wrapper">
  <input type="text" id='textfield'>
  <div id='drawer'>
  </div>
</div>
`;

class TextboxWithDropdown extends HTMLElement {  

  constructor() {
    // Always call super first in constructor
    super();
    // and attach the component
    this._shadowRoot = this.attachShadow({ 'mode': 'open' });
  }

  connectedCallback() {
    // and when the component is mounted, do the rest to make it work
    this.selectedIndex = 0;
    this.filteredWordsCount = 0;
    this.isDrawerOpen = false;

    this._shadowRoot.innerHTML = template;
    this.textfield = this._shadowRoot.getElementById("textfield");
    this.dictionary = this.getAttribute("dictionary").split(',');
    this.textfield.value = this.getAttribute("value");
    this.textfield.addEventListener("keyup", function(e) {
      this.keyUp(e);
    }.bind(this));    
  }

  static get observedAttributes() {
    // on attributes changed by the browser dev tools this will reflect the changes
    return ["dictionary", "value"];
  }  

  get value() {
   // return the value
    return this.textfield.value;
  }

  attributeChangedCallback(name, oldValue, newValue) {
    //Custom square element attributes changed.
    this.dictionary = newValue.split(',');
  }

  selectHighlight(i) {
    this._shadowRoot.getElementById('row-' + this.selectedIndex).classList.remove("selectedRow");
    this._shadowRoot.getElementById('row-' + i).classList.add("selectedRow");
    this.selectedIndex = i;
  }

  keyUp(e) {
    if(e.keyCode == 13) {
      this.rowSelected(e);
      return;
    }
    if(e.keyCode == '38' || e.keyCode == '40') {
      this.arrowUpDown(e);
      return;
    }
    var prefix = this._shadowRoot.getElementById('textfield').value;
    if(prefix == '') {
      this._shadowRoot.getElementById('drawer').innerHTML = '';
      this.selectedIndex = 0;
      this.filteredWordsCount = 0;
      return;
    }
    if(this.isDrawerOpen == true)
      return;
    var words = this.filterWords(prefix);
    this._shadowRoot.getElementById('drawer').innerHTML = words.join('');
    // attach the events
    var c = 0;
    words.map(function(row, i) {
      var row = this._shadowRoot.getElementById('row-' + i);
      row.addEventListener("mousemove", function() {
        this.selectHighlight(i);
      }.bind(this));
      row.addEventListener("click", function(e) {
        this.rowSelected(e);
      }.bind(this));
    }.bind(this));
    // select first row if any
    if(words.length > 0)
      this._shadowRoot.getElementById('row-0').classList.add("selectedRow");
  }  

  arrowUpDown(e) {
    if(this.selectedIndex > -1)
      this._shadowRoot.getElementById('row-' + this.selectedIndex).classList.remove("selectedRow");    
    if(e.keyCode == '38' && this.selectedIndex > 0) {
      // arrow up
      this.selectedIndex --;
    }
    else if(e.keyCode == '40' && this.selectedIndex < this.filteredWordsCount - 1) {
      // arrow down
      this.selectedIndex ++;
    }
    this._shadowRoot.getElementById('row-' + this.selectedIndex).classList.add("selectedRow");
    e.preventDefault();
    return false;
  }

  rowSelected(e) {
    if(this.filteredWordsCount == 0)
      return;
    this._shadowRoot.getElementById('textfield').value = this._shadowRoot.getElementById('row-' + this.selectedIndex).innerText;
    this._shadowRoot.getElementById('drawer').innerHTML = '';
    this.filteredWordsCount = 0;
    this.selectedIndex = 0;
    e.preventDefault();
    return false;    
  }

  //business logic comes here. Bad idea!. Separate this in a diffrent class. But for the simplicity of the example we will keep it here.
  filterWords(prefix) {
    prefix = prefix.toLowerCase();
    var result = [];
    for(var i=0; i < this.dictionary.length;i ++) {
      var wordArray = this.dictionary[i].toLowerCase();
      for(var j=0; j < prefix.length && j < wordArray.length; j ++) {
        if(prefix[j] != wordArray[j]) {
          break;
        }
      }
      if(prefix.length == j) {
        var wordRow = '<p id="row-' + result.length + '">' + this.dictionary[i] + '</p>';
        result.push(wordRow);
      }
    }
    this.filteredWordsCount = result.length;
    return result;
  }  

}



window.customElements.define('textbox-with-dropdown', TextboxWithDropdown);

What we just did:
– in connectedCallback function we set up:
this.selectedIndex which will point to the selected word in the drawer
this.filteredWordsCount storing the number of word matched
this.isDrawerOpen
this._shadowRoot.innerHTML is set up with the HTML layout that we created in the beginning of the file.
this.textfield is set up with the value of value attribute. this._shadowRoot is the way how we are referring to the root of the component.
this.dictionary stores an array of all dictionary words that we pass using the dictionary property.
– added event listener to the text field, which will respond on keyUp and keyDown and will manipulate the highlighted word.
– (line 56) observedAttributes function returns a list of the attributes that we need to observe. Ie on adding or removing words through browser’s dev tools.
– (line 61) this function is called every time when the value of value attribute is called.Ie when  document.getElementById('txt1').value is called.
– (line 66) attributeChangedCallback is called whenever the observed parameters that we set up in observedAttributes function. Once the dictionary parameter is changed we are updating this.dictionary
– (line 71) selectHighlight function is called on mousemove over the words in the drawer.
– (line 77) keyUp(e) function responds to key press and is responsible to call rowSelected() on enter key, or arrowUpDown() on button up or down. If any other key is pressed filterWords(prefix)is called, prefix is the whatever the user started to type in the textfield. Then when the new words list is returned, we attach mousemove event (line 101), and click event (line 104)
arrowUpDown() simply move the highlighter up or down.
rowSelected() is called when a word is selected either with mouse click or when enter key is pressed.

 

 https://github.com/ToniNichev/WebComponents-text-box-with-dropdown.git

Array VS Hash Table

Hash table tutorial

Find element in Array

function fuindInArray() {
  var t0 = performance.now();

  for(var q = 0; q < data2.length; q++) {
      if( data2[q] == '106112407') {
          console.log(data2["106112407"]);
          break;
      }
  }

Find element in Hash Table

function findInHashtable() {
  var t0 = performance.now();

  console.log(data1["106112407"]);

  var t1 = performance.now();
  document.querySelector('#result1').value = "Call took " + (t1 - t0) + " milliseconds.";
}

 


Majority element in array

Task

Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.

You may assume that the array is non-empty and the majority element always exist in the array.

Example 1:

Input:

 [3,2,3]

Output:

 3

Example 2:

Input:

 [2,2,1,1,1,2,2]

Output:

 2

This problem was taken from Leetcode

Solution

The solution:

Create an object (or associative array, depends of the language), iterate through all elements in the array adding them to the newly created object using the element value as a key. If element exists increase the value +1.
Keep track of mostly repeated element and return it at the end.

var arr = [3, 3, 4, 2, 4, 2, 4, 4];


/**
 * @param {number[]} nums
 * @return {number}
 */
var majorityElement = function(nums) {
  var elements = {};
  var majorityElement = nums[0];
  var majorityElementCount = 1;
  for(var i in nums) {
  	var element = nums[i];
  	elements[element] = typeof elements[element] == 'undefined' ? 1 : elements[element] + 1;
  	if(elements[element] > majorityElementCount) {
  		majorityElement = element;
  		majorityElementCount = elements[element];
  	}
  }    
  return majorityElement;
};

console.log(">>", majorityElement(arr));

– Create an object (line 9)
– Iterate through each element in the array feeling up the object with the elements from the array where the element value would be the key of the element (line 14)
– Each time when the element exists, add + 1
– Keep track of mostly repeated element (Lines 16-17)

Could we optimize the code? Slightly. When we add + 1 we could check if the value is already greater than the length of the array / 2 and if se we know that this is the majority element because no other element could have greater value.

...
    if(elements[element] > nums.length / 2) {
        return element;
    }
...

 

Creating loader component and set up one CSS per brand.

branch-name:  
Click To Copy

 

Previous solution was great but not perfect. We still add a CSS of two brands in one CSS file and loading all CSS for all brands per each component.

Could we do it better and load only the necessary CSS for each component for the particular branch ? Yers we could. The idea about bundle splitting is that one CSS file will be created per each component.

So in order to have one CSS file per brand we have to

Create a wrapper component per each brand.

Let’s start with

The Home component.

We are going to move the component’s code from index.js to a new file called renderer.js and repurpose index.js to be a wrapper component, which will load brand specific sub component, that will pass the style object back to the renderer component in renderer.js. If it sounds a bit confusing don’t worry. Follow that tutorial and it will get clear.

./src/components/Home/renderer.js

import React from 'react';

const Renderer = ({styles, title}) => {
  return (
    <div>
      <div className={styles.wrapper}>{title}</div>
    </div>
  );
}

export default Renderer;

The code in renderer file is pretty similar to that we had in index.js with one exception: we are going to pass the css as a property to this component.
And just to demonstrate how we could render different layout per each brand we are going to pass the title property as well.

Now the index.js will become our Home component wrapper, which will dynamically load either ./brands/one or ./brands/two sub component, which on the other hand will load our ./renderer.js component, passing the appropriate CSS for the selected brand.

./src/components/Home/index.js

import React from 'react';
import Loadable from 'react-loadable';
import Loading from '../Loading';

const one = Loadable({
  loader: () => import ('./brands/one'),
  loading: Loading
});

const two = Loadable({
  loader: () => import ('./brands/two'),
  loading: Loading
});

const components = {
  one,
  two
}

const Home = ( {subDomain} ) => {
  const Component = components[subDomain];
  return (
      <Component />
  )
}
export default Home;

what we just did:
– we crated a wrapper component, that will conditionally load the helper component for the current brand (lines 5 and 10)
-we render the appropriate sub component, based on the brand name.

Let’s create the helper sub components that will load the brand specific CSS and pass it to the renderer component and render it.

These components will look quite similar:

./src/components/Home/brands/one/index.js

import React from 'react';
import styles from './styles.scss';
import Renderer from '../../renderer.js'

export default () => {
  return (
    <Renderer styles={styles} title="This is my home section rendered for One!" />
  )
}

./src/components/Home/brands/two/index.js

import React from 'react';
import styles from './styles.scss';
import Renderer from '../../renderer.js'

export default () => {
  return (
    <Renderer styles={styles} title="This is my home section rendered for Two!" />
  )
}

what we just did:
– we imported brand specific CSS in each of the components (line 2)
– imported the renderer component (line 3)
– rendered the renderer component, passing the CSS and the title property (line 7)

Open the ./dist folder and look at 1.css and 2.css contents:

./dist/1.css

.one-wrapper{background-image:url(/dist/images/b5c0108b6972494511e73ad626d1852f-home.png);height:500px}.one-wrapper h2{color:#000}

./dist/2.css

.two-wrapper{background-image:url(/dist/images/a005b97826d5568577273d214dd5f89a-home.png);height:800px}.two-wrapper h2{color:#00f;font-size:50px}

Webpack created two files with the corresponding CSS: one-wrapper and two-wrapper containing only the CSS needed for each brand.

Open the browser and give it a try. The result should be what we saw in the previous chapter, but this time only the brand specific CSS is loaded.

Nice! Now we have Webpack created these two CSS files, but the class names are one-wrapper and two-wrapper which comes from the lead folder name, which now instead of been ./Home is ./Home/one and /Home/two  What will happen if we want to make another component brand specific?

The Greetings component

Let’s do the same changes:

./src/components/Greetings/renderer.js

import React, { Component } from 'react';
import { connect } from 'react-redux';


const CHANGE_USERNAME = 'CHANGE_USERNAME';
const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE';

class Greetings extends Component {

  constructor(props) {
    super(props); 
  }
  

  doneEditUsername() {
    let newName = document.querySelector('#inputField').value;
    this.props.changeUserName(newName);
    this.props.toggleLogInPopup();
  }

  usernameChanged(el) {
    let newName = el.target.value;    
    this.props.changeUserName(newName);
  }

  onToggleEditMode() {
    this.props.toggleLogInPopup();
  }

  render() {
    let element = <h2 onClick={() =>{   this.onToggleEditMode()  }}>Hello:  {this.props.userName}</h2>;
    if(this.props.editMode)
      element = <h2>Type new name:<input type="text" id='inputField' value={this.props.userName} onChange={(el) => { this.usernameChanged(el);}} /><button onClick={() =>{ this.doneEditUsername() }}>done</button></h2>
    return (
      <div>
        <div className={this.props.styles.wrapper}>
          {element}
        </div>
      </div>);
  }
}

const mapStateToProps = ( storeState ) => {
  return {  
    userName: storeState.user.userName,
    editMode: storeState.user.editMode
  }
}

const mapDispatchToProps = dispatch => {
  return {
    toggleLogInPopup: () => {
      dispatch({type: TOGGLE_EDIT_MODE});
    },
    changeUserName: (userName) => {
      dispatch({type: CHANGE_USERNAME, data: userName});
    }
  }
};


export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Greetings);

what we just did:
– we just moved the code from ./index.js to ./renderer.js
– we removed the CSS import since we are passing CSS as a property
– we changed the div class name to come from the passed property (line 36)

The rest is the same like in Home component.

The index component will actually look exactly the same:

./src/components/Greetings/index.js

import React from 'react';
import Loadable from 'react-loadable';
import Loading from '../Loading';



const one = Loadable({
  loader: () => import ('./brands/one'),
  loading: Loading
});

const two = Loadable({
  loader: () => import ('./brands/two'),
  loading: Loading
});

const components = {
  one,
  two
}

const Home = ( {subDomain} ) => {

  const Component = components[subDomain];
  return (
      <Component />
  )
}
export default Home;

Let’s load different ‘home’ pictures for each brand.
Move the home picture in ./images/home.png to ./images/one/home.png and add another image for ./images/two/home.png (you could either download some png or use the one in this branch)

./src/components/Greetings/brands/one/styles.scss

.wrapper {
  background-image:url('../../../../images/one/home.png');
  height: 500px;
  h2 {
    color: black;
  }
}

./src/components/Greetings/brands/two/styles.scss

.wrapper {
  background-image:url('../../../../images/two/home.png');
  height: 800px;
  h2 {
    color: blue;
    font-size: 50px;
  }
}

Here we have to adjust the relative path to the images since this component goes two levels deeper and we moved the images into a brands folders (line 2)

And the helper sub components are the same like in Home component.

./src/components/Greetings/brands/one/index.js

import React from 'react';
import styles from './styles.scss';
import Renderer from '../../renderer.js'

const One = () => {
  return (
    <Renderer styles={styles} />
  )
}

export default One;

./src/components/Greetings/brands/two/index.js

import React from 'react';
import styles from './styles.scss';
import Renderer from '../../renderer.js'

const One = () => {
  return (
    <Renderer styles={styles} />
  )
}

export default One;

Start the server and go to http://one.localhost:3006/home and you will notice that the Home component height increased. Why this happened?

Let’s open http://one.localhost:3006/dist/1.css and look at the class names:

.one-wrapper{background-image:url(/dist/images/b5c0108b6972494511e73ad626d1852f-home.png);height:500px}.one-wrapper h2{color:#000}

Somehow the one-wrapper has background-image:url(/dist/images/b5c0108b6972494511e73ad626d1852f-home.png) and height:500px that belongs to the Greetings component.

Why this is happening? Because of the way how we set up class name structure in Css-Loader. If you look at webpack.base.config.js  you will see that the localIdentName which is the CSS className is constructed by adding the folder name, and the actual local identifier ‘[folder]-[local]’

 ./src/webpack.base.config.js

...
      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]',
              sourceMap: true
            }
          },
...

But the folder name now is ‘one’ or ‘two’ for both components since it takes only the leaf folder name. Let’s fix this by

Make brand component names unique.

Go to src/Home/brands/one and rename it to src/Home/brands/OneHome and src/Home/brands/two to be src/Home/brands/TwoHome
and do the same for the greetings component: src/Greetings/brands/one => src/Greetings/brands/OneGreetings and src/Greetings/brands/Two => src/Greetings/brands/TwoGreetings

Next let’s make the appropriate changes in both: Home and Greeting component:

./src/Home/index.js

import React from 'react';
import Loadable from 'react-loadable';
import Loading from '../Loading';

const one = Loadable({
  loader: () => import ('./brands/OneHome'),
  loading: Loading
});

const two = Loadable({
  loader: () => import ('./brands/TwoHome'),
  loading: Loading
});

const components = {
  one,
  two
}

const Home = ( {subDomain} ) => {

  const Component = components[subDomain];
  return (
      <Component />
  )
}
export default Home;

and

./src/Greetings/index.js

import React from 'react';
import Loadable from 'react-loadable';
import Loading from '../Loading';



const one = Loadable({
  loader: () => import ('./brands/OneGreetings'),
  loading: Loading
});

const two = Loadable({
  loader: () => import ('./brands/TwoGreetings'),
  loading: Loading
});

const components = {
  one,
  two
}

const Home = ( {subDomain} ) => {

  const Component = components[subDomain];
  return (
      <Component />
  )
}
export default Home;

Run the project and check /dist/3.css 

.OneHome-wrapper--t4U5b{background:#8d8dac;color:#fff;text-align:center;font-family:MyFont}

it contains only CSS for the Home component.

Adding a hash in CSS class names

As an extra step we could also add a hash for each class name. This will make class names unique per component, so if accidentally happened to have two components with the same names their CSS won’t colide.

This could be achieved by simply adding a hash directive in localIdentName in CSS-loader config [folder]-[local]–[hash:base64:5]

const getEnvironmentConstants = require('./getEnvironmentConstants');
const webpack =require('webpack');

module.exports = {
  mode: 'development',
  devtool: 'eval-source-map',
  entry: [
    '@babel/polyfill',    
    './src/index.js',
  ],
  output: {
    filename: '[name]-bundle.js',
    publicPath: '/dist/',
  },  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },

      // SCSS
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 2,
              localIdentName: '[folder]-[local]--[hash:base64:5]',
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              plugins: () => [require('autoprefixer')()],
              sourceMap: true
            },
          },
          {
            loader: 'sass-loader',
            options: {
              outputStyle: 'expanded',
              sourceMap: true
            }
          }
        ],
      },
      // images
      {
        test: /\.(png|jp(e*)g|svg)$/,  
        use: [{
            loader: 'url-loader',
            options: { 
                limit: 8000, // Convert images < 8kb to base64 strings
                name: 'images/[hash]-[name].[ext]'
            } 
        }]
      },
      //File loader used to load fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader']
      }                    
    ]
  },
  plugins: [
    new webpack.DefinePlugin({ 'process.env' : getEnvironmentConstants() } ),  
  ]
};

Run the project again and the home component should look like before. Open http://one.localhost:3006/dist/1.css and you will see how the has was appended to the class names.

branch-name:  
Click To Copy

 

Simple caching using browser’s service workers.

A service worker API is relatively new technology, allowing for running a special type of web worker (a JavaScript), that can be installed in the browser and could provide special features to the website like caching data and allowing for offline load.

Service workers are also heavily used in progressive apps since they provide functionalities for caching resources and push notifications.

Before we jump into building an example app, let’s see what are the main service worker properties.

Service worker properties:

– Service workers are plain JavaScript APIs.

– Service workers run on separate thread so they can’t access the elements of the main page like the DOM elements but this is not the intend of how they are used.

– Service workers can listen on an events emitted from the browser and execute an appropriate script. In example a service worker could listen on browser fetch event and fetch the data or return it from cache.

Service worker lifecycle.

On a first page visit the only thing that happens is that service worker got installed. No code inside the service worker got executed, unless the user hit another page from this website or reloads the current page. Then service worker kicks in and could provide the extra functionality to the site.

Let’s get started by creating a simple web page, that will make an AJAX request to this service: https://www.toni-develops.com/external-files/examples/service-workers/delayed-response.php that will return the current time, and they display it.

The service intentionally delays the response 5 seconds so this way we could observe when we open the page for the first time how the response is delayed, since service worker is installing and don’t have the response cached. Then if we reload the page the service worker will intercept the request and will serve last cached response right away and then  will make a request to the service and update the page.

Building a caching layer for the web resources using service worker.

Let’s create the page.

index.html

<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>title</title>
    <script
          src="https://code.jquery.com/jquery-3.3.1.min.js"
          integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
          crossorigin="anonymous"></script>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
  </head>
  <body>
    My fancy new site !
    <hr>
    <div id="container"></div>
  </body>
</html>

 

script.js

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('./sw.js').then(function(registration) {
      // Registration was successful
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}



$.ajax({url: "https://www.toni-develops.com/external-files/examples/service-workers/delayed-response.php", success: function(result){
  $("#container").html(result);
}});

 What’s happening here:
– line 1: we check if the browser supports service workers.
– line 2: added event listener, listening for load event and on load it installs the service worker. The rest is just error handling.
-line15:we make a request to the service using plain jQuery AJAX request.

style.css

body {
  background: silver
}

Now it’s time to write the actual service worker.

sw.js

var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
  './index.html',
  './style.css',
  './script.js',
  'https://www.toni-develops.com/external-files/examples/service-workers/delayed-response.php',
  'https://code.jquery.com/jquery-3.3.1.min.js'

];

self.addEventListener('install', event => {
  // Perform install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        console.log('Opened cache');
        cache.addAll(urlsToCache);
      })
  );
});


self.addEventListener('fetch', event => {
  console.log("(9)served from service worker: ",event.request.url);
  // serve as soon as possible from cache
  event.respondWith(fromCache(event.request));
  // update cache
  event.waitUntil(  
    update(event.request)
  );
});


/**
 * 
 * Helper methods
 */

function fromCache(request) {
  return caches.open(CACHE_NAME).then(cache => {
    return cache.match(request);
  });
}


function update(request) {
  caches.open(CACHE_NAME).then( cache => {
    fetch(request).then( response => {
      cache.put(request, response)
    });
  });
}

What we just did:
– line 1: we specified the cache key (we have to make sure that it is unique)
– line 2: we created an array urlsToCache having all URLs that we want to cache. If one of the URLs fail, service worker will fail to install so we have to keep this url list short.
– line 11: added event listener, that on install event (when the service worker is invoked for the first time) will open the cache (line 14), and will add all urls to the cache (line 17) and then return the contents.
– line13: will simply prevent service worker to be killed before finishing with caching the resources.
– line 23: added fetch event. This will fires wherever a web page request a resource (*.js, *.css, images or in this case AJAX request)
– line 26: will return the asset right away from cache
– line29: will fetch the asset from the ‘slow’ service to update the cache for the next request.
Now let’s look at the helper methods:
fromCache function is simply opens the cache and finds the request key.
update function is opening the cache, fetches a fresh data from the service (or stores a fresh copy of web resource) and puts it back into the cache using request as a key.

Testing the project.

Open an empty tab in Chrome (comand + T), make sure that the dev tools are also open on the net tab (alt + comand + i), and the console drawer is visible (escape toggles the drawer)

Copy the example URL example found here and paste it into Chrome’s tab.

You will see the message “My fancy new site !” and after around 5 seconds current date and time will appear (- 5 seconds that took for the service to return the data)

Now service worker is installed. Reload the page and you will see that the current time will appear instantly from the cache. Observe the net tab and you will notice that the service worker also made a request to the service to update the data (the one that is pending on the picture below) and also web resources are now coming from the service worker (the size column).

Additionally if you want to clear the cache you could do it by clicking on the Application tab in the dev tools “Clear Storage” and then “clear site data”.