CMK

Let's create something!


  • Home

  • Categories

  • Tags

  • Archives

  • About

Common DOM Manipulations

Posted on 2018-10-15 | In Full stack | Comments:

Window VS document

  1. window: a global object, and everything runs under it, including document
    • global variables,
    • global functions,
    • location
    • history
    • setTimeout
    • ajax call (XMLHttpRequest),
    • console
    • localStorage
  2. documnet: represents the DOM and DOM is the object oriented representation of the html markup you have written;
    • All the nodes are part of document. Hence you can use getElementById or addEventListener on document.

window.onload vs document.onload

  1. window.onload: When EVERYTHING is loaded. DOM is ready and all the contents including images, css, scripts, sub-frames, etc. finished loaded.
  2. document.onload: once the DOM is loaded, regardless of the css, scripts,…

attribute vs property

  1. attribute: belongs to HTML tag, is static after being initialized

  2. property: belongs to DOM node, is dynamic while interact with its element

  3. 1
    2
    3
    4
    5
    6
    <input id="my-input" type="text" value="Name:">
    <script>
    var myInput = document.getElementById('my-input');
    myInput.getAttribute('value'); //"Name:"
    myInput.value; //'my dude'
    </script>

Add class to element: classList

1
2
3
4
5
6
function addClass(selector, className){
var elm = document.querySelector(selector);
if (elm){
elm.classList.add(className);
}
}

Check Descendant

1
2
3
4
5
6
7
8
9
function isDescendant(parent, child){ 
while(child.parentNode ){
if(child.parentNode == parent)
return true;
else
child = child.parentNode;
}
return false;
}

Create DOM element: innerHTML vs appendChild

  1. innerHTML: browser removes all the current children of the elements. Parse the string and assign the parsed string to the element as children. So, Slow!

    1. 1
      2
      3
      var ul = document.getElementById('myList');

      el.innerHTML = '<li>Only one item</li>';
  2. appendChild: you create a new Element. Since you are creating it, browser doesnt have to parse string and there is no invalid html. so, Fast!

    1. 1
      2
      3
      4
      5
      var li = document.createElement("li");
      var text = document.createTextNode('Only one Item');

      li.appendChild(text);
      ul.appendChild(li);

CrateDocumentFragment: improve performance

  1. documentFragment a lightweight or minimal part of a DOM or a subtree of a DOM tree.

  2. It is very helpful when you are manipulating a part of DOM for multiple times.

  3. It becomes expensive to hit a certain portion of DOM for hundreds time. You might cause reflow for hundred times. Stay tuned for reflow.

    • BAD practice:

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        //bad practice. you are hitting the DOM every single time
        var list = ['foo', 'bar', 'baz', ... ],
        el, text;
        for (var i = 0; i < list.length; i++) {
        el = document.createElement('li');
        text = document.createTextNode(list[i]);
        el.appendChild(text);
        document.body.appendChild(el);
        }
    • GOOD practice:

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12

        //good practice. you causing reflow one time
        var fragment = document.createDocumentFragment(),
        list = ['foo', 'bar', 'baz', ...],
        el, text;
        for (var i = 0; i < list.length; i++) {
        el = document.createElement('li');
        text = document.createTextNode(list[i]);
        el.appendChild(text);
        fragment.appendChild(el);
        }
        document.body.appendChild(fragment);

reflow

  1. [reflow] : flow of the elements in the page is changed due to change size or position
    • When you change size or position of an element in the page, all the elements after it has to change their position according to the changes you made.
    • For example, if you change height on an element, all the elements under it has to move down in the page to accomodate a change in height.
  2. Reasons:
    • change layout (geometry of the page)
    • resize the window
    • change height/width of any element
    • changing font, or font size
    • move DOM element (animation)
    • adding or removing stylesheet
    • calculating offset height or offset width
    • display: none;

repaint

  1. It happens when you change the look of an element without changing the size and shape. This doesn’t cause reflow as geometry of the element didn’t changed.
  2. Reasons:
    • change background color
    • change text color
    • visibility hidden

destroy button

Create a button that is destroyed by clicking on it but two new buttons are created in it’s place.

​

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<div id="wrapper">
<button id="doubleHolder" class="double">double</button>
</div>
<script>
document.getElementById('doubleHolder').addEventListener('click', function (e) {
var btn = document.createElement('button');
btn.setAttribute('class', 'double');
btn.innerHTML = 'double1';

var btn2 = document.createElement('button');
btn2.setAttribute('class', 'double');
btn2.innerHTML = 'double2';
let wrapper = document.getElementById('wrapper')
wrapper.appendChild(btn);
wrapper.appendChild(btn2);
wrapper.removeChild(e.target);
});
</script>

Get text of a Node: innerHTML textContent innerText

  1. innerHTML parses content as HTML and takes longer. Also vulnerable to XSS attacks

  2. textContent uses straight text, does not parse HTML, and is faster.

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // Given the following HTML fragment:
      // <div id="divA">This is <span>some</span> text</div>

      // Get the text content:
      var text = document.getElementById("divA").textContent;
      // |text| is set to "This is some text".

      // Set the text content:
      document.getElementById("divA").textContent = "This is some text";
      // The HTML for divA is now:
      // <div id="divA">This is some text</div>
      Polyfill
  3. innerText Takes styles into consideration. It won’t get hidden text for instance.: Bad, causing reflow !

defer vs async

  1. defer & async are only effective in <script> tag. HTML parser will ignore defer and async keyword for inline script (script that does not have a src attribute).
  2. normal: When you have a plain script tag (no defer or async keyword), parser will pause parsing, script would be downloaded and exectuted. After that parsing resume
  3. async: downloading script won’t affect HTML paring, but executing will pause HTML parsing. Once downloaded, script begin to executed, will pause HTML parsing
  4. defer: downloading script won’t affect HTML paring, also executing won’t affect HTML parsing. The script with defer will executed util HTML finish parsing

Create a Calculator - [Vanilla JS]

Posted on 2018-10-15 | In Full stack | Comments:

Problem

Build a web calculator, that supports the basic operations of add, remove, multiply and divide.

In addition, support resetting the current state, as well as a decimal point button.

Implementation

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

<html>
<head>
<style>
.wrapper{
width: 400px;
height: 400px;
margin: 15px auto;
border: 1px solid black;
}
.display-panel{
margin: 10px auto;
width: 95%;
height: 60px;
line-height: 60px;
font-size: 30px;
border: 1px solid black;
text-align: right;
overflow-y: hidden;
overflow-x: scroll;
white-space: nowrap;
}

.ctl-panel{
margin: 5px auto;
width: 95%;
height: 300px;
border: 1px solid black;
font-size: 30px;
display: flex;
flex-flow: row wrap;
justify-content: space-around;
}

.left-part{
border: 1px solid red;
flex: 1 1 70%;
display: grid;
justify-content: space-around;
align-content: space-around;
grid-template-columns: auto auto auto;
grid-template-rows: auto auto auto auto;
}


.right-part{
flex: 1 1 20%;
border: 1px solid red;
display: grid;
justify-content: space-around;
align-content: space-around;
grid-template-columns: auto;
grid-template-rows: 30% 60%;
}

#equal{
height: 100%;
}

button{
width: 50px;
height: 50px;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="display-panel" id="display">
display placeholdersdsfsdfsdfsdffsdfsdffsdf
</div>
<div class="ctl-panel" id="panel">
<div class='left-part'>
<div><button>+</button></div>
<div><button>-</button></div>
<div><button>&times;</button></div>
<div><button>7</button></div>
<div><button>8</button></div>
<div><button>9</button></div>
<div><button>4</button></div>
<div><button>5</button></div>
<div><button>6</button></div>
<div><button>1</button></div>
<div><button>2</button></div>
<div><button>3</button></div>
<div><button>0</button></div>
<div><button>.</button></div>
<div><button>C</button></div>
</div>
<div class='right-part'>
<div><button>&divide;</button></div>
<div><button id="equal">&equals;</button></div>
</div>
</div>
</div>

<script>
function handleInput(e){
let val = e.target.textContent
let display = document.getElementById("display")
if(isNum(val)){
handleNum(val, display);
}else if(isDot(val)){
handleDot(val, display);
}else if(isOperator(val)){
handleOperator(val, display)
}else if(isEqual(val)){
handleCalc(display)
}else if(isClear(val)){
handleClear(display)
}else{
return
}
}

function handleClear(display){
display.textContent = ""
}

function handleNum(v, display){
let oldText = display.textContent
display.textContent += v
}

function handleOperator(v, display){
let oldText = display.textContent
if( isOperator(oldText[oldText.length-1]) )
oldText=oldText.substr(0,oldText.length-1)
display.textContent = oldText+v
}

function handleDot(v, display){
let oldText = display.textContent
if( isDot(oldText[oldText.length-1]) ) return
display.textContent += v
}


function handleCalc(display){
let oldText = display.textContent
if( !isNum(oldText[oldText.length-1]) ) {
display.textContent = 0
return
}
display.textContent = calc(oldText)
}

function calc( text ){
let stack = []
let numbers = text.split(/\+|\-|\×|\÷/)
let operators = text.replace(/[0-9]|\./g, "").split("")
console.log(numbers)
console.log(operators)

stack.push(new Number(numbers[0]) )
for( let i = 0; i < operators.length; i++ ){
let num = new Number( numbers[i+1] )
if(operators[i] === "+"){
stack.push(num)
}else if(operators[i] === "-"){
stack.push(-num)
}else if(operators[i] === "×"){
let top = stack.pop()
stack.push(top*num)
}else if(operators[i] === "÷"){
let top = stack.pop()
stack.push(top/num)
}
}
console.log(stack)
return stack.reduce((item,accum)=>{
return accum + item
},0)
}


function isNum(v){ return v >= "0" && v <= "9" }
function isClear(v){ return v === 'C' }
function isEqual(v){ return v === '=' }
function isDot(v){ return v === '.' }
function isOperator(v){
return v === '+' || v === '-' || v === '÷' || v === '×'
}

document.getElementById("panel").addEventListener( "click", handleInput )
</script>
</body>
</html>

Reposition an Array

Posted on 2018-10-14 | In Algorithm | Comments:

Problem

Given an original array, and it new position to be. Reposition it according the given indices array. For example:

1
2
3
4
let arr  =    ["a", "b", "c", "d", "e", "f"]
let indices = [ 1, 2, 3, 5, 0, 4 ]

// the new array should be ["e", "a", "b", "c", "f", "d"]

Follow Up

Try to do it in-place, i.e. : Space Complexity O(1)

Solution 1: Space complexity O(n)

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
function rePosition(arr, indices){
if( arr.length !== indices.length )
throw new Error("they are not the same length")

let newArr = indices.map((item, index)=>{
return arr[indices.indexOf(index)]
})
return newArr
}

// Tests
let newArr = []
let arr = ["a", "b", "c", "d", "e", "f"]

// test 1
let indices1 = [ 1, 2, 3, 5, 0, 4 ]
newArr = rePosition(arr, indices1)
console.log(newArr) // ["e", "a", "b", "c", "f", "d"]

// test 2
let indices2 = [ 0, 1, 2, 3, 4, 5 ]
newArr = rePosition(arr, indices2)
console.log(newArr) // ["a", "b", "c", "d", "e", "f"]

// test 3
let indices3 = [ 2, 1, 0, 4, 3, 5 ]
newArr = rePosition(arr, indices3)
console.log(newArr) // ["c", "b", "a", "e", "d", "f"]

Solution 2: Space complexity O(1)

We need two variables: kickedItem and kickedIndex, to denote the kicked item in arr and kicked index in indices

  1. Every time we update an item in its new position, we put item in its new position, and swap the kicked item and index to their counterpart in arr & indices

  2. Since the kicked item & its new position are just swapped to previous position, we can keep staring the same position to deal with the chaining update, until there is an end to the chain

  3. NOTE: there are also cases (test case 3) that the new indices are not depended upon one another, but instead they are partially dependent. That’s why we need to maintain two variable for both arr & indices, rather than just for arr

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

function rePosition(arr, indices){
if( arr.length !== indices.length )
throw new Error("they are not the same length")

for( let i = 0; i < arr.length; i++ ){
while( i !== indices[i] ){
let kickedItem = arr[indices[i]]
let kickedIndex = indices[indices[i]]
arr[indices[i]] = arr[i]
indices[indices[i]] = indices[i]
arr[i] = kickedItem
indices[i] = kickedIndex
}
}
}

// Tests

// test 1
let arr = ["a", "b", "c", "d", "e", "f"]
let indices1 = [ 1, 2, 3, 5, 0, 4 ]
rePosition(arr, indices1)
console.log(arr) // ["e", "a", "b", "c", "f", "d"]

// test 2
arr = ["a", "b", "c", "d", "e", "f"]
let indices2 = [ 0, 1, 2, 3, 4, 5 ]
rePosition(arr, indices2)
console.log(arr) // ["a", "b", "c", "d", "e", "f"]

// test 3
arr = ["a", "b", "c", "d", "e", "f"]
let indices3 = [ 2, 1, 0, 4, 3, 5 ]
rePosition(arr, indices3)
console.log(arr) // ["c", "b", "a", "e", "d", "f"]

How to Flatten an Array

Posted on 2018-10-14 | In Algorithm | Comments:

Problem

Given an nested array, flatten it to 1-dimension array.

For example:

1
2
3
[1, [2, [ [3, 4], 5], 6], [7, 8], 9]
=>
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Solution 1: iterative version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. iterative [stack]
function flatten1( arr ){
let res = []
let stack = []
stack.push(arr)
while( stack.length > 0 ){
let item = stack.pop()
if( !Array.isArray(item) ){
res.push(item)
}else{
for( let i = item.length-1; i >=0; i-- ){
stack.push(item[i])
}
}
}
return res
}

Solution 2: recursive version

1
2
3
4
5
6
function flatten2(arr){
if( !Array.isArray(arr) ){
return [arr]
}
return [].concat(...arr.map(flatten2))
}

Solution 3: recursive version [shorter]

1
2
3
function flatten(a) {
return Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
}

Testing

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
let test1 = [1, [2, 3, [4, 5]], 6, [7, [8, 9], 10], 11]
let test2 = []
let test3 = [1]
let test4 = 1
let test5 = [[[1,2,3]]]
let test6 = [[[]],[],[],[],[[[]]]]


// let arrayFlattened1 = flatten1(test1).toString()
// let arrayFlattened2 = flatten1(test2).toString()
// let arrayFlattened3 = flatten1(test3).toString()
// let arrayFlattened4 = flatten1(test4).toString()
// let arrayFlattened5 = flatten1(test5).toString()
// let arrayFlattened6 = flatten1(test6).toString()
let arrayFlattened1 = flatten2(test1).toString()
let arrayFlattened2 = flatten2(test2).toString()
let arrayFlattened3 = flatten2(test3).toString()
let arrayFlattened4 = flatten2(test4).toString()
let arrayFlattened5 = flatten2(test5).toString()
let arrayFlattened6 = flatten2(test6).toString()
console.log(arrayFlattened1)
console.log(arrayFlattened2)
console.log(arrayFlattened3)
console.log(arrayFlattened4)
console.log(arrayFlattened5)
console.log(arrayFlattened6)

Common Algorithm Problems - JS

Posted on 2018-10-14 | In Algorithm | Comments:
  1. All credits to ThatJSDude

  2. Verify a prime number?

    1. version 1: not too good

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        function isPrime( num ){
        let i = 2
        while( i <= num/2 ){
        if( num % 2 === 0 ) return false
        i += 1
        }
        return true
        }

        // Test
        console.log(isPrime(1)) // true
        console.log(isPrime(2)) // true
        console.log(isPrime(4)) // false
        console.log(isPrime(7)) // true
        console.log(isPrime(101)) // true
    2. Improved Version

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        function isPrime(n)
        {
        let divisor = 3,
        let limit = Math.sqrt(n);

        //check simple cases
        if (n == 2 || n == 3) return true;
        if (n % 2 == 0) return false;

        while (divisor <= limit)
        {
        if (n % divisor == 0) return false;
        else divisor += 2;
        }
        return true;
        }

        > isPrime(137);
        = true
        > isPrime(237);
        = false
  3. Find all prime factors of a number?

    1. Find all prime factors

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        function findAllPrimeFactors( num ){
        let factors = []
        let i = 2
        while( num > 2 ){
        if( num % i === 0 ){
        factors.push(i)
        num /= i
        }else{
        i += 1
        }
        }
        return factors
        }

        // test
        console.log(findAllPrimeFactors(420)) // 2 2 3 5 7
    2. Find distinct prime factors

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        function findDistinctPrimeFactors( num ){
        let factors = []
        let i = 2
        let temp = num
        while( i <= num/2 ){
        if( temp % i === 0 ) {
        factors.push(i)
        while(temp % i === 0) temp /= i
        }
        i += 1
        }
        return factors
        }

        // test
        console.log(findDistinctPrimeFactors(420)) // 2 3 5 7
  4. Get nth Fibonacci number?

    1. f(n) = f(n-1) + f(n-2)

    2. Give n, print fibonacci sequence from 0 ~ n

      1. 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
        // 1. Iterative
        function f(n){
        let fb = []
        fb[0] = 1
        fb[1] = 1

        for( let i = 2; i <= n; i++ ){
        fb[i] = fb[i-1] + fb[i-2]
        }
        return fb;
        }

        // 2. recursive
        function wrapperFb(n){
        let memo = new Array(n+1).fill(0)
        memo[0] = 1
        memo[1] = 1
        function f(n){
        if( n===0 || n===1 ) return 1
        if( memo[n] !== 0 )
        return memo[n]
        else
        memo[n] = f(n-1) + f(n-2)
        return memo[n]
        }
        f(n)
        return memo
        }

        console.log(wrapperFb(5)) // 1 1 2 3 5 8
    3. Give n, print the nth fibonacci number

      1. 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
        // 1. iterative
        function fth(n){
        let fb = []
        fb[0] = 1
        fb[1] = 1

        for( let i = 2; i <= n; i++ ){
        fb[i] = fb[i-1] + fb[i-2]
        }
        return fb[n];
        }

        console.log(fth(5)) // 8


        // 2. recursive
        function wrapperFb(n){
        let memo = new Array(n+1).fill(0)
        memo[0] = 1
        memo[1] = 1
        function f(n){
        if( n===0 || n===1 ) return 1
        if( memo[n] !== 0 )
        return memo[n]
        else
        memo[n] = f(n-1) + f(n-2)
        return memo[n]
        }
        return f(n)
        }

        console.log(wrapperFb(5)) // 8
  5. Find the greatest common divisor of two numbers?

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      function greatCommonDevisor ( n1, n2 ){
      let res = 1
      let i = 2
      while( i <= n1 || i <= n2 ){
      while( n1%i === 0 && n2%i === 0 ){
      res *= i
      n1 /= i
      n2 /= i
      }
      i += 1
      }
      return res
      }
      console.log(greatCommonDevisor(36,48)) // 12
  6. Remove duplicate members from an array?

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      function removeDup(arr){
      let set = new Set()
      return arr.filter( item =>{
      if( !set.has(item) ){
      set.add(item)
      return item
      }
      })
      }
      let arr = [3,2,3,3,1,2,2,4,4,3,5]
      console.log(removeDup(arr)) // [3,2,1,4,5]
  7. Merge two sorted array?

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      function merge( arr1, arr2 ){
      let arr = []
      let i = 0, j = 0
      while( i < arr1.length && j < arr2.length ){
      if( arr1[i] < arr2[j] ){
      arr.push(arr1[i])
      i += 1
      }else{
      arr.push(arr2[j])
      j += 1
      }
      }
      if( i < arr1.length ) arr.push(...arr1.slice(i))
      if( j < arr2.length ) arr.push(...arr2.slice(j))
      return arr
      }

      let arr1 = [ 1, 3, 5, 5, 6, 8 ]
      let arr2 = [ 1, 2, 2, 4, 7, 8, 9 ]
      console.log(merge(arr1, arr2)) // [1,1,2,2,3,4,5,5,6,7,8,8,9]
  8. Swap two numbers without using a temp variable?

    1. using diff

      1. 1
        2
        3
        4
        5
        function swap(a, b){
        a = a - b
        b = a + b
        a = b - a
        }
    2. using XOR

      1. 1
        2
        3
        4
        5
        function swap(a, b){
        a = a ^ b
        b = a ^ b
        a = a ^ b
        }
  9. Reverse a string in JavaScript?

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      // 1. recursive version
      function reverseStr( str ){
      if( str === "" ) return ""
      return reverseStr(str.substring(1)) + str.charAt(0)
      }
      console.log(reverseStr("abcdef")) // "fedcba"

      // 2. built-in method
      function reverse(str){
      if(!str || str.length <2) return str;
      return str.split('').reverse().join('');
      }
      console.log(reverseStr("abcdef")) // "fedcba"

      // 3. string util method
      String.prototype.reverse = function(){
      if(!this || this.length <2) return this;
      return this.split('').reverse().join('');
      }
      let str = "abcdef"
      console.log(str.reverse()) // "fedcba"
  10. Find the first non repeating char in a string?

    1. Using Map instead of object to collect the frequency for each char

    2. Since we want to iterate the map in-order, while object cannot guanrentee this

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        // In the end, we need to iterate the key in-order, so we cannot count on object, but instead of Map, which give in-order traverse

        function firstNonRepeat(str){
        let map = new Map()
        for( let i = 0; i < str.length; i++ ){
        let char = str[i]
        if( map.has(char) ){
        map.set( char, map.get(char)+1 )
        }else{
        map.set( char, 1 )
        }
        }
        let arr = Array.from(map)
        for( let i = 0; i < arr.length; i++ ){
        if( arr[i][1] === 1 ) return arr[i][0]
        }
        return -1;
        }

        let str = "the quick brown fox jumps then quickly blow air"
        console.log(firstNonRepeat(str))
  11. Match substring of a sting?

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      // aaabc
      // aab
      function subStringFinder(str, subStr){
      let index = 0
      while( index <= str.length-subStr.length ){
      let sub = str.substring(index, index+subStr.length)
      if( sub === subStr )
      return index
      else
      index += 1
      }
      return -1
      }

      // test
      console.log(subStringFinder("aaabc", "aab")) // 1
      console.log(subStringFinder('abbcdabbbbbck', 'bbbck') ) // 8
  12. Create all permutation of a string?

    1. 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
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      /*  
      0 1 2 3
      1 2 3
      2 3
      3 2

      */

      // [version 1.1 recursive]: String with no-dup character
      function permutations(str){
      let perm = []
      let prefix = ""
      function dfs(str){
      if( prefix.length === str.length ){
      perm.push(new String(prefix))
      return
      }

      for( let i = 0; i < str.length; i++ ){
      if( prefix.indexOf(str[i]) === -1 ){
      prefix += str[i]
      dfs(str)
      prefix = prefix.substring(0, prefix.length-1)
      }
      }
      }
      dfs(str)
      return perm
      }
      console.log(permutations("abc"))

      // [version 1.2 iterative]: String with no-dup character
      function permutations(str){
      let queue = []
      queue.push("")
      let level = 0
      while( level < str.length ){
      let size = queue.length
      for( let k = 0; k < size; k++ ){
      let prefix = queue.shift()
      for( let i = 0; i < str.length; i++ ){
      if( prefix.indexOf(str[i]) === -1 ){
      queue.push(prefix+str[i])
      }
      }
      }
      level += 1
      }
      return queue
      }
      console.log(permutations("abc"))



      // [version 2.1-recursive]: string with duplicate character
      function permutations(str){
      let map = new Map()
      let perm = []
      let prefix = ""

      for( let i = 0; i < str.length; i++ ){
      if( map.has(str[i]) ){
      map.set( str[i], map.get(str[i])+1 )
      }else{
      map.set( str[i], 1 )
      }
      }

      function dfs(str){
      if( prefix.length == str.length ){
      perm.push(prefix)
      return
      }
      let freqArr = Array.from(map)
      console.log(freqArr)
      for( let i = 0; i < freqArr.length; i++ ){
      let char = freqArr[i][0]
      let freq = freqArr[i][1]
      if( map.get(char) > 0){
      prefix += char
      map.set(char, freq-1 )
      dfs(str)
      map.set(char, freq )
      prefix = prefix.substring(0, prefix.length-1)
      }
      }
      }
      dfs(str)
      return perm
      }

      console.log(permutations("abc"))
      console.log(permutations("aac"))

Stack & Queue Implementation using JS

Posted on 2018-10-08 | In Algorithm | Comments:

Stack

  1. interfaces:

    • stack.size
    • stack.peek()
    • stack.push
    • stack.pop()
    • stack.isEmpty()
    • stack.clear()
  2. implementation:

    1. 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
      /* 
      * Note: this version use `null` as Error symbol,
      * so you cannot push null into the stack
      */
      class Stack{
      constructor(){ this.data = [] }

      get size(){ return this.data.length }

      isEmpty(){ return this.data.length === 0 }

      peek(){ return this.data[this.data.length-1] }

      push(item){
      if( typeof item ==="undefined" || item === null ) return false
      this.data.push(item)
      return true
      }

      pop(item){
      if(this.data.length === 0) return null
      return this.data.pop();
      }

      clear(){ this.data = [] }
      }

      /* ---- Testing ---- */
      let s = new Stack()
      console.log("size: ",s.size) // 0
      console.log(s.pop()) // null
      console.log(s.push()) // false
      console.log(s.push(null)) // false
      console.log(s.push(1)) // true
      console.log(s.push(1)) // true
      console.log("size: ",s.size) // 2
      console.log(s.push(2)) // true
      console.log("size: ",s.size) // 3
      console.log(s.pop()) // 2
      console.log("size: ", s.size) // 2
      console.log(s.pop()) // 1
      console.log(s.pop()) // 1
      console.log("size: ", s.size) // 0
      console.log(s.pop()) // null
      console.log("size: ", s.size) // 0
      console.log(s.isEmpty()) // true

Queue

  1. interfaces:

    • queue.size
    • queue.peek()
    • queue.push
    • queue.pop()
    • queue.isEmpty()
    • queue.clear()
  2. implementation:

    1. 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
      /* 
      * Note: this version can add any object into Queue
      * But need to call `isEmpty()` when necessary
      */
      class Queue{
      constructor(){ this.items = [] }

      get size(){ return this.items.length; }

      isEmpty(){ return this.items.length === 0 }

      offer(item){ this.items.push(item) }

      poll(){ return this.items.shift() }

      peek(){ return this.items[0] }

      clear(){ this.items = [] }
      }

      /* ---- Testing ---- */
      let q = new Queue()
      console.log("size: ", q.size)// 0
      console.log(q.offer(null))
      console.log(q.offer(null))
      console.log(q.items) // [null, null]
      console.log(q.offer(1))
      console.log(q.items) // [null, null, 1]
      console.log(q.poll()) // null
      console.log(q.offer(2)) // [null, 1, 2]
      console.log("size: ",q.size)// 3
      console.log(q.poll())
      console.log(q.items) // [1, 2]
      console.log(q.poll()) // 1
      console.log(q.items) // [2]
      console.log("size: ", q.size)// 1
      console.log(q.poll()) // 2
      console.log("size: ", q.size)// 0
      console.log(q.isEmpty()) // true

How to implement auto-complete?

Posted on 2018-10-08 | In Full stack | Comments:

I. Logic

  1. For each input, wait 500ms to make API call, to load 40 items
  2. scroll down close to bottom ( less than 20px left ), make API call to load another 40 items
  3. could add local cache, to speed up response

II. code

  1. 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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    <!DOCTYPE html>
    <html>
    <head>
    <style>
    #wrapper {
    margin-top: 10px;
    overflow: auto;
    width: 250px;
    display: inline-block;
    border: 1px solid red;
    overflow: hidden;
    }

    .listWrapper{
    border: 1px solid blue;
    height: 200px;
    width: 140px;
    overflow: auto;
    scroll: auto;
    }

    </style>
    </head>
    <body>
    <div id="wrapper">
    <div>
    <input id="inputText" />
    <button>submit</button>
    </div>
    </div>

    <script>

    let body = document.getElementsByTagName("BODY")[0]
    let data = Array.from({length: 40}, () => "recommdation"+Math.floor(Math.random() * 40));

    const API = "https://www.demo.com/potentialList"
    let isFetching = false // used for scroll
    let isEnd = false // used for scroll


    // create new element below <input> inside div wrapper. For both 'input' & 'scroll'
    function appendList(elementCollection, wrapper, isScroll){
    if(isScroll){
    let listWrapper = document.getElementsByClassName("listWrapper")[0]
    data.forEach( ele=>{
    let itemNode = document.createElement("DIV")
    let textNode = document.createTextNode(ele)
    itemNode.appendChild(textNode)
    listWrapper.appendChild(itemNode)
    })
    return
    }

    let oldlistWrapper = document.getElementsByClassName("listWrapper")
    if(oldlistWrapper.length > 0){
    wrapper.removeChild(oldlistWrapper[0])
    }
    let listWrapper = document.createElement("DIV")
    listWrapper.setAttribute("class", "listWrapper")
    let list = Array.prototype.slice.call(elementCollection)
    data.forEach( ele=>{
    let itemNode = document.createElement("DIV")
    let textNode = document.createTextNode(ele)
    itemNode.appendChild(textNode)
    listWrapper.appendChild(itemNode)
    })
    listWrapper.addEventListener("click", putToInput)
    listWrapper.addEventListener("scroll", scrollHandler)
    wrapper.appendChild(listWrapper)

    }


    // onclick callback for items provided
    function putToInput(e){
    let input = document.getElementById("inputText")
    let wrapper = document.getElementById("wrapper")
    let listWrapper = document.getElementsByClassName("listWrapper")[0]
    input.value = e.target.innerHTML
    console.log(e.target)
    wrapper.removeChild(listWrapper)
    }


    // make API call
    function makeAPICall(API, val, isScroll){
    console.log("val: ", val)
    // 1. first check local cache

    // 2. hit fail, call API
    let ajax = new XMLHttpRequest()
    ajax.open("GET", "https://www.w3schools.com/js/ajax_info.txt", true)
    ajax.onreadystatechange = function(){
    if(this.readyState === 4 && this.status === 200 ){
    console.log(this.responseText)
    let parser = new DOMParser()
    let a = parser.parseFromString(this.responseText, "text/html")
    console.log(a)
    let body = a.getElementsByTagName("BODY")[0]
    let wrapper = document.getElementById("wrapper")
    if(val.localeCompare("scroll made request")===0){
    isFetching = false
    }
    appendList(data, wrapper, isScroll)
    }
    }
    ajax.send()
    }

    // scroll handler for listWrapper
    function scrollHandler(e){
    let listWrapper = e.target
    let boxHeight = listWrapper.clientHeight
    let scrollTop = listWrapper.scrollTop
    let scrollHeight = listWrapper.scrollHeight

    let remainder = scrollHeight - scrollTop
    let diff = remainder - boxHeight
    console.log("box height: ", boxHeight)
    console.log("scrollTop: ", scrollTop)
    console.log("scrollHeight: ", scrollHeight)
    console.log("diff: ", diff)

    if( diff < 20 && !isFetching && !isEnd ){
    isFetching = true
    makeAPICall(API, "scroll made request", true)
    }
    }

    // debounce 500ms to make API call
    let timeHandler = null
    function inputHandler(e){
    clearTimeout(timeHandler)
    let val = e.target.value
    timeHandler = setTimeout( function(){makeAPICall(API, "keypress made request", false)} ,500 )
    }

    let input = document.getElementById("inputText")
    addEventListener("input", inputHandler)
    </script>

    </body>
    </html>

Search for a Symmetric Node

Posted on 2018-10-07 | In Full stack | Comments:

Problem

Given two DOM trees with the same structure, and one node in tree1, try to find its symmetric node in tree2

Solution 1

If we are not so familiar with DOM API, we might forget there is an attribute for each DOM element: parentNode. Then we might just traverse both trees at the same time, when we encounter the node in tree1, the node in tree2 is what we want.

1
2
3
4
5
6
7
8
9
10
11
function getSymmetricNode(target, root1, root2){
if( target === root1 ) return root2

for( let i = 0; i < root1.childNodes.length; i++ ){
let node1 = root1.childNodes[i]
let node2 = root2.childNodes[i]
if( node1 === target ) return node2
getSymmetricNode(target, node1, node2 )
}
return null
}
  1. Time Complexity: O(b^d) [d: depth, b: branches], which is BAD!

Solution 2 [Improved ]

Actually there is an convenient attribute we could utilize, that is parentNode

  1. First, we could go up to find that path from root1 to target
    • The path is consist of indices of node on that path
  2. Using that path of indices, we could go downwards from root2 to find the desired node
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let getSymmetricNode = (target, root1, root2) => {
let path = getIndexPath( target, root1 )
return getNode( path, root2 )
}

let getIndexPath = (target, root1)=>{
let path = []
let curNode = target
while( curNode && curNode.parentNode ){
let index = curNode.parentNode.indexOf(curNode)
path.push(index)
curNode = curNode.parentNode
}
return path
}

let getNode =(path, root)=>{
let node
while( path.length > 0 ){
node = root.childNodes[path.pop()]
}
return node
}

Testing

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Testing
<!doctype html>

<html>
<head>
<meta charset="UTF-8" />
<title>Hello World</title>
</head>
<body>
<div id="root1">
<div>
<div></div>
</div>
<div>
<div id="node1"></div>
<div></div>
</div>
</div>

<div id="root2">
<div>
<div></div>
</div>
<div>
<div id="node2"></div>
<div></div>
</div>
</div>
<script>


// --------------- 1: O(b^d) --------------
function getSymmetricNode1(target, root1, root2){
if( target === root1 ) return root2
for( let i = 0; i < root1.childNodes.length; i++ ){
let node1 = root1.childNodes[i]
let node2 = root2.childNodes[i]
let res = getSymmetricNode1(target, node1, node2)
if( null !== res ) return res

}
return null
}




// -------------- 2: O(b*d) ---------------
function getSymmetricNode(target, root1, root2){
let path = getIndexPath( target, root1 )
return getNode( path, root2 )
}

function getChildNodesArray(node){
return Array.prototype.slice.call(node.childNodes)
}


function getIndexPath(target, root1){
let path = []
let curNode = target
while( curNode !==root1 && curNode && curNode.parentNode ){
let index = getChildNodesArray(curNode.parentNode).indexOf(curNode)
path.push(index)
curNode = curNode.parentNode
}
return path
}


function getNode(path, root){
let node = root
while( path.length > 0 ){
node = getChildNodesArray(node)[path.pop()]
}
return node
}

const root1 = document.getElementById('root1');
const root2 = document.getElementById('root2');
const node1 = document.getElementById('node1');
const node2 = document.getElementById('node2');

const nodeX = getSymmetricNode(node1, root1, root2 );
console.log(nodeX)
console.log(nodeX === node2); // true
</script>
</body>
</html>

Design Pattern - JS

Posted on 2018-10-07 | In Full stack | Comments:

All Credit to: Learning JavaScript Design Patterns

Constructor Pattern

  1. What: a constructor is a special method used to initialize a newly created object once memory has been allocated for it

  2. Why need it

    • e.g.: create different student with: name, age, eat method

    • without constructor : each time we create a new student again and again

      • 1
        2
        3
        let alice ={name: "alice", age: 12, eat: function(){ ... } }
        let bob = {name: "bob", age: 15, eat: function(){ ... } }
        let carol ={name: "carol", age: 11, eat: function(){ ... } }
    • with constructor [BAD] : each time we create a new student trivially

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        /*
        BAD: each object will take a copy of `eat()` from memory,
        which is in fact not necessary
        */
        function Student( name, age ){
        let student = {}
        student.name = name
        student.age = age
        student.eat = function(){ console.log(name, " is eating")}
        return student
        }
        let alice = new Student("alice", 12)
        let bob = new Student("bob", 15)
        let carol = new Student("carol", 11)
        alice.eat()
    • with constructor [GOOD] : see next section Prototype pattern

      • 1
         

Prototype Pattern

  1. what: view prototype pattern as being based on prototypal inheritance where we create objects which act as prototypes for other objects.

  2. improvement of Student class, using prototype.eat()

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      /*
      GOOD: each object will share `eat()` of Student prototype
      */
      function Student( name, age ){
      this.name = name
      this.age = age
      }
      Student.prototype.eat = function(){console.log(this.name, " is eating")}

      let alice = new Student("alice", 12)
      let bob = new Student("bob", 15)
      let carol = new Student("carol", 11)
      alice.eat()

Factory Pattern

  1. what : combine similar classes together into a factory, to make code more compact

  2. Implement one!

    1. 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
      58
      59
      60
      61
      62
      // Types.js - Constructors used behind the scenes

      // A constructor for defining new cars
      function Car( options ) {

      // some defaults
      this.doors = options.doors || 4;
      this.state = options.state || "brand new";
      this.color = options.color || "silver";

      }

      // A constructor for defining new trucks
      function Truck( options){

      this.state = options.state || "used";
      this.wheelSize = options.wheelSize || "large";
      this.color = options.color || "blue";
      }


      // FactoryExample.js

      // Define a skeleton vehicle factory
      function VehicleFactory() {}

      // Define the prototypes and utilities for this factory

      // Our default vehicleClass is Car
      VehicleFactory.prototype.vehicleClass = Car;

      // Our Factory method for creating new Vehicle instances
      VehicleFactory.prototype.createVehicle = function ( options ) {

      switch(options.vehicleType){
      case "car":
      this.vehicleClass = Car;
      break;
      case "truck":
      this.vehicleClass = Truck;
      break;
      //defaults to VehicleFactory.prototype.vehicleClass (Car)
      }

      return new this.vehicleClass( options );

      };

      // Create an instance of our factory that makes cars
      var carFactory = new VehicleFactory();
      var car = carFactory.createVehicle( {
      vehicleType: "car",
      color: "yellow",
      doors: 6 } );

      // Test to confirm our car was created using the vehicleClass/prototype Car

      // Outputs: true
      console.log( car instanceof Car );

      // Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
      console.log( car );

Singleton Pattern

  1. What : It restricts instantiation of a class to a single object. Across the whole app, there is only one object of it exists.

  2. Why :

    1. can be used for data source of truth
    2. anyone can call its methods to update the data
  3. Trick : we can store private variable as closure, as singleton used here

    1. the singleton is instance, its private property & methods are: privateMethod, privateVariable, privateRandomNumber

    2. but return privateRandomNumber as public varible can keep it alive

    3. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      function init() {
      // private methods and fileds
      let privateVariable = "I am a private var";
      let privateRandomNumber = Math.random();
      function privateMethod(){console.log("I am private method" )}

      return {
      // Public methods and fields
      publicMethod: privateMethod,
      getPrivateVar: function(){ return privateVariable },
      getRandomNumber: function(){ return privateRandomNumber }
      };
      }
  4. Implementation

    1. 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
      58
      59
      60
      61
      62
      var mySingleton = (function () {
      var instance; // Instance stores a reference to the Singleton
      function init() {
      // Singleton
      var privateRandomNumber = Math.random();
      return {
      // Public methods and variables
      getRandomNumber: function() {
      return privateRandomNumber;
      }
      };
      };

      return {
      // Get the Singleton instance if one exists
      // or create one if it doesn't
      getInstance: function () {
      if ( !instance ) { instance = init(); }
      return instance;
      }
      };
      })();



      var myBadSingleton = (function () {
      // Instance stores a reference to the Singleton
      var instance;
      function init() {
      // Singleton
      var privateRandomNumber = Math.random();
      return {
      getRandomNumber: function() {
      return privateRandomNumber;
      }
      };
      };

      return {
      // Always create a new Singleton instance
      getInstance: function () {
      instance = init();
      return instance;
      }
      };

      })();


      // Usage:
      var singleA = mySingleton.getInstance();
      var singleB = mySingleton.getInstance();
      console.log( singleA.getRandomNumber() === singleB.getRandomNumber() ); // true

      var badSingleA = myBadSingleton.getInstance();
      var badSingleB = myBadSingleton.getInstance();
      console.log( badSingleA.getRandomNumber() !== badSingleB.getRandomNumber() ); // true

      // Note: as we are working with random numbers, there is a
      // mathematical possibility both numbers will be the same,
      // however unlikely. The above example should otherwise still
      // be valid.

Observer Pattern

  1. Subject : an object maintains a list of objects depending on it (observers), automatically notifying them of any changes to state.

  2. Observers : objects who are interesting in the messages from the subject

  3. Compared to Observable

    1. Observer Pattern: actively notify all observers when events happen
    2. observable Pattern: passively / lazily start the events watching process
  4. advantage: focus on observers, not their actions/methods

  5. disadvantage: cannot flexible on the observers’ methods, must be the same

  6. Implementation

    1. 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
      58
      59
      60
      61
      62
      63
      64
      // Util class: ObserverList, used by `Subject`
      function ObserverList(){
      this.observerList = [];
      }

      ObserverList.prototype.add = function( obj ){
      return this.observerList.push( obj );
      };

      ObserverList.prototype.count = function(){
      return this.observerList.length;
      };

      ObserverList.prototype.get = function( index ){
      if( index > -1 && index < this.observerList.length ){
      return this.observerList[ index ];
      }
      };

      ObserverList.prototype.indexOf = function( obj, startIndex ){
      var i = startIndex;

      while( i < this.observerList.length ){
      if( this.observerList[i] === obj ){
      return i;
      }
      i++;
      }

      return -1;
      };

      ObserverList.prototype.removeAt = function( index ){
      this.observerList.splice( index, 1 );
      };



      // -----1----- Subject
      function Subject(){
      this.observers = new ObserverList();
      }

      Subject.prototype.addObserver = function( observer ){
      this.observers.add( observer );
      };

      Subject.prototype.removeObserver = function( observer ){
      this.observers.removeAt( this.observers.indexOf( observer, 0 ) );
      };

      Subject.prototype.notify = function( context ){
      var observerCount = this.observers.count();
      for(var i=0; i < observerCount; i++){
      this.observers.get(i).update( context );
      }
      };

      // -----2----- Observer
      function Observer(){
      this.update = function(){
      // ...
      };
      }

RxJS - Observable

  1. Observer pattern: subject (publisher) & observer (subscriber)

    • use case: add event to DOM

    • implementation

      • 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
        // 1. Subject
        class Subject {

        constructor() {
        this.observerCollection = [];
        }

        registerObserver(observer) {
        this.observerCollection.push(observer);
        }

        unregisterObserver(observer) {
        let index = this.observerCollection.indexOf(observer);
        if(index >= 0) this.observerCollection.splice(index, 1);
        }

        notifyObservers() {
        this.observerCollection.forEach((observer)=>observer.notify());
        }
        }

        // 2. Observer
        class Observer {

        constructor(name) {
        this.name = name;
        }

        notify() {
        console.log(`${this.name} has been notified.`);
        }
        }

        // use case
        let subject = new Subject(); // 创建主题对象

        let observer1 = new Observer('semlinker'); // 创建观察者A - 'semlinker'
        let observer2 = new Observer('lolo'); // 创建观察者B - 'lolo'

        subject.registerObserver(observer1); // 注册观察者A
        subject.registerObserver(observer2); // 注册观察者B

        subject.notifyObservers(); // 通知观察者

        subject.unregisterObserver(observer1); // 移除观察者A

        subject.notifyObservers(); // 验证是否成功移除
  2. Iterator pattern

    • Why use it: offer a way to visit interal elements without exposing internal implementing details

    • drawback: one-way & irreversible

    • data structure of the return value: next()

      • => { done: false, value: elementValue }
      • => { done: true, value: undefined }
    • Implementation of Array Iterator

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        // Array Iterator using closure
        function makeIterator(array){
        var nextIndex = 0;

        return {
        next: function(){
        return nextIndex < array.length ?
        {value: array[nextIndex++], done: false} :
        {done: true};
        }
        }
        }

        // use case
        var it = makeIterator(['yo', 'ya']);
        console.log(it.next().value); // 'yo'
        console.log(it.next().value); // 'ya'
        console.log(it.next().done); // true
    • ES6 Iterator: Symbol.iterator can create iterator for an iterable object

      • How to use?

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        let arr = ['a', 'b', 'c'];
        let iter = arr[Symbol.iterator]();

        > iter.next()
        { value: 'a', done: false }
        > iter.next()
        { value: 'b', done: false }
        > iter.next()
        { value: 'c', done: false }
        > iter.next()
        { value: undefined, done: true }
      • iterable object: Arrays, Strings, Maps, Sets, DOM

  3. Observable in RxJS

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      var observable = Rx.Observable.create(function (observer) {
      observer.next(1);
      observer.next(2);
      observer.next(3);
      setTimeout(() => {
      observer.next(4);
      observer.complete();
      }, 1000);
      });

      console.log('just before subscribe');
      observable.subscribe({
      next: x => console.log('got value ' + x),
      error: err => console.error('something wrong occurred: ' + err),
      complete: () => console.log('done'),
      });
      console.log('just after subscribe');
  4. Characters

    • observable will not automatically run, only triggered by subscribe(); which is opposed to promise.

    • observable is a function, which can be observed by observer

    • observer is a object, which has three callback: next, error, complete

    • how to subscribe the observable? observable.subsribe(observer)

    • how to manage the relationships between observable and observer? – subscription var subscription1 = observable.subscribe(observer1);

    • how to unsubscribe? — subscription1.unsubscribe();

    • Subscribing to an Observable is analogous to calling a Function.

    • two Observable subscribes trigger two separate side effects. As opposed to EventEmitters which share the side effects and have eager execution regardless of the existence of subscribers, Observables have no shared execution and are lazy.

Command Pattern

  1. What : commonly exists in many tool config.json file: run("fun", "arg1", arg2)

  2. How to Implement :

    1. 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

      var carManager = {
      // request information
      requestInfo: function( model, id ){
      return "The information for " + model + " with ID " + id + " is foobar";
      },

      // purchase the car
      buyVehicle: function( model, id ){
      return "You have successfully purchased Item " + id + ", a " + model;
      },

      // arrange a viewing
      arrangeViewing: function( model, id ){
      return "You have successfully booked a viewing of " + model + " ( " + id + " ) ";
      }
      };

      carManager.execute = function ( name ) {
      return carManager[name] && carManager[name].apply( carManager, [].slice.call(arguments, 1) );
      };


      // Use case
      carManager.execute( "arrangeViewing", "Ferrari", "14523" );
      carManager.execute( "requestInfo", "Ford Mondeo", "54323" );
      carManager.execute( "requestInfo", "Ford Escort", "34232" );
      carManager.execute( "buyVehicle", "Ford Escort", "34232" );

Mixin Pattern: _.extend()

  1. what: Mixins allow objects to borrow (or inherit) functionality from them with a minimal amount of complexity

  2. How to use: using _.extend() from Underscore.js

    1. 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
      var myMixins = {
      moveUp: function(){ console.log( "move up" ); },
      moveDown: function(){ console.log( "move down" ); },
      stop: function(){ console.log( "stop! in the name of love!" );}
      };

      // A skeleton carAnimator constructor
      function CarAnimator(){
      this.moveLeft = function(){
      console.log( "move left" );
      };
      }

      // A skeleton personAnimator constructor
      function PersonAnimator(){
      this.moveRandomly = function(){ /*..*/ };
      }

      // Extend both constructors with our Mixin
      _.extend( CarAnimator.prototype, myMixins );
      _.extend( PersonAnimator.prototype, myMixins );

      // Create a new instance of carAnimator
      var myAnimator = new CarAnimator();
      myAnimator.moveLeft();
      myAnimator.moveDown();
      myAnimator.stop();

      // Outputs:
      // move left
      // move down
      // stop! in the name of love!
  3. Write ourselves: _.extend() <=> augument()

    1. 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
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      // Define a simple Car constructor
      var Car = function ( settings ) {

      this.model = settings.model || "no model provided";
      this.color = settings.color || "no colour provided";

      };

      // Mixin
      var Mixin = function () {};

      Mixin.prototype = {

      driveForward: function () {
      console.log( "drive forward" );
      },

      driveBackward: function () {
      console.log( "drive backward" );
      },

      driveSideways: function () {
      console.log( "drive sideways" );
      }

      };


      // Extend an existing object with a method from another
      function augment( receivingClass, givingClass ) {

      // only provide certain methods
      if ( arguments[2] ) {
      for ( var i = 2, len = arguments.length; i < len; i++ ) {
      receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
      }
      }
      // provide all methods
      else {
      for ( var methodName in givingClass.prototype ) {

      // check to make sure the receiving class doesn't
      // have a method of the same name as the one currently
      // being processed
      if ( !Object.hasOwnProperty.call(receivingClass.prototype, methodName) ) {
      receivingClass.prototype[methodName] = givingClass.prototype[methodName];
      }
      }
      }
      }


      // Augment the Car constructor to include "driveForward" and "driveBackward"
      augment( Car, Mixin, "driveForward", "driveBackward" );

      // Create a new Car
      var myCar = new Car({
      model: "Ford Escort",
      color: "blue"
      });

      // Test to make sure we now have access to the methods
      myCar.driveForward();
      myCar.driveBackward();

      // Outputs:
      // drive forward
      // drive backward

      // We can also augment Car to include all functions from our mixin
      // by not explicitly listing a selection of them
      augment( Car, Mixin );

      var mySportsCar = new Car({
      model: "Porsche",
      color: "red"
      });

      mySportsCar.driveSideways();

      // Outputs:
      // drive sideways

Facebook Interview Preparation

Posted on 2018-10-06 | In Full stack | Comments:

HTML Accessibility Tag

  1. To improve accessiblity, we are encouraged to use semantic tag of HTML5

    • MDS: https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility
  2. Why to use it?

    • https://stackoverflow.com/questions/17272019/why-to-use-html5-semantic-tag-instead-of-div
  3. Common semantic tags

    • Layout: header footer nav main section article aside
      • <header role="banner">
      • <section role="search">
    • Form: label input button
      • <label for="search">
      • <input type="text" >
      • <input type="submit" />
    • Table: <caption> <table summury="caption"> th
    • Image:
      • <img alt="img.png" title="This is a beautiful girl" >
      • aria-labelledby
      • empty alt: necessary if the image is only for decoration use
    • text:
      • <em> vs <i>
      • <strong> vs <b>
      • <abbr title="Hypertext Markup Language"> HTML <abbr>
    • position:
      • absolution position is better than display: none or visibility: hidden: to overlap all div with different z-index
      • since display: none or visibility: hidden will hide content from screenreader
  4. How to improve accessibility?

    • use :focus & :hover pseudo-class to highlight the chosen tag

      • 1
        2
        3
        4
        a:hover, input:hover, button:hover, select:hover,
        a:focus, input:focus, button:focus, select:focus {
        font-weight: bold;
        }
  5. JavaScript in accessibility

    1. provide client-side form validating, rather than server-side
    2. <video> has control button for keyboard
    3. mouseover == onfocus & mouseout == onblur
  6. mobile accessibility

    • touch: onmousedown == ontouchstart && onmouseup == ontouchstop

    • side menu

    • @media

      • logical operator: and, not, only, ,(like 'or')

      • 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
        <!-- 1. media in <link>  -->
        <link href="mobile.css" rel="stylesheet" media="screen and (max-width: 600px)">

        <!-- 2. media in <style> -->
        <style media="all and (max-width: 500px)">
        p {
        color: blue;
        background-color: yellow;
        }
        </style>

        <!-- 3. media in <script> -->
        <script>
        if (window.matchMedia("(min-width: 400px)").matches) {
        // the viewport is at least 400 pixels wide
        } else {
        // the viewport is less than 400 pixels wide
        }
        </script>

        <!-- 4. media inside style -->
        <style>
        @media screen and (min-width: 30em) and (max-width: 992px) {
        body { background-color: blue; }
        }

        @media (min-height: 680px), screen and (orientation: portrait){
        body { background-color: blue; }
        }
        </style>
    • @import

      • syntax:

        1
        2
        @import 'custom.css';
        @import url('landscape.css') screen and (orientation:landscape);

Why use React?

Credit to: https://reactjs.org/blog/2013/06/05/why-react.html

  1. React is not MVC, is only “V”
  2. Support small, reusable component
  3. efficient DOM update, using diff algorithm
    • reconcilation process: the way React used to update real DOM, by only updating changed components node and its children
    • Every time props or state change leading to render() execution, which will produce either a String or virtual DOM node;
    • Either way, React will compare it with previous String or virtual DOM node, to determine which node in real DOM need to be updated
    • diff/reconcilation process only happens after render(), it will compare current tree with previous tree
  4. key in a list of nodes: make diff efficiently
    1. there are cases that a node is added in front to a list, without key, React just compare each pair of nodes from previous tree and current tree, and re-render all the nodes in real DOM in that list; Even thought, it only needs to insert a new node to real DOM tree
    2. Attaching a key to each node in a list, will help React know which node need to be re-render in real DOM tree.
    3. Nodes with the same key inside a list, will be stable across different re-renders

React Lifecyle

  1. Mounting phase

    • constructor()
    • getDerivedStateFromProps()
    • render()
    • componentDidMount()
  2. Updating phase

    • getDerivedStateFromProps()
    • shouldComponentUpdate()
    • render(): after either new props or new state
    • componentDidUpdate()
  3. Unmounting phase

    • componentWillUnmount()
  4. Other API: setState() & forceUpdate()

    1. setState(): can take both object and callback function as parameter

    2. setState( (prevState, props) => updatedState )

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        // BAD: updated state will only take one increment
        // i.e. in the end, counter=1
        componentDidMount(){
        this.setState({counter: this.state.counter+1})
        this.setState({counter: this.state.counter+1})
        this.setState({counter: this.state.counter+1})
        })

        // GOOD: updated state will take all increment
        // i.e. in the end, counter=3
        componentDidMount(){
        this.setState( counter=>{counter: state.counter + 1} )
        this.setState( counter=>{counter: state.counter + 1} )
        this.setState( counter=>{counter: state.counter + 1} )
        })

How to implement infinite scroll loading

  1. Credit to: https://segmentfault.com/a/1190000004974824

  2. Which event do we use?

    • We assume the user will scroll down the screen, in order to load more content
    • So we use the scroll event on window or block element with a height set already
  3. Three conditions for AJAX cal

    1. container bottom - window bottom < certain value ,i.e. the container don’t have too much content to show
    2. not in the middle of loading
    3. still have content to load, i.e. not the end of all the content
  4. code

  5. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    var isLoading = false;
    var isEnd = false;
    var triggerDistance = 200;

    function fetchData() {

    var distance = container.getBoundingClientRect().bottom - window.innerHeight;
    if ( !isLoading && !isEnd && distance < triggerDistance ) {

    isLoading = true;

    fetch(path).then(res => {
    isLoading = false;
    // if not end, will short-circut latter statement
    // if reach end, will still check the latter one, which will set isEnd to true
    res.data.length === 0 && isEnd = true;
    doSomething(res.data);
    });

    }

    }
    window.addEventListener('scroll', fetchData);

The rendering order: CSS, JS, HTML

  1. credit to:
    • https://bitsofco.de/async-vs-defer/
    • https://www.cnblogs.com/yingsong/p/6170780.html
  2. General order
    1. JS in <head> will block downloading of following resource
    2. Browser downloads and parses HTML to DOM
    3. when encounter <script>,
      • if not external, render engine hand control power to js engine
      • if is external, render engine keep rendering, while downloads external

CSS: profile & edit image link

  1. Implement the requirement

  2. consider accessibility

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
58
59
60
61
62
63
64
// 1. Mouse is not over the picture
+-------------+
| |
| |
| picture |
| |
| |
+-------------+

// 2. Mouse is anywhere over picture
+--------+----+
| |edit| <--- links to /edit.php
| +----+ icon url: /edit_icon.png
| picture |
| | <--- links to /profile.php
| | picture url: /profile_pic.png
+-------------+

<html>
<head>
<style>
.imgWrapper{
width: 100px;
height: 100px;
border: 1px solid red;
position: relative;
}

#image1{
width: 100%;
height: 100%;
}

#image2{
position: absolute;
top: 0;
right:0;
width: 50px;
height: 50px;
display: none;
}

.imgWrapper:hover #image2{
display: block;
}

</style>
</head>
<body>


<div class="imgWrapper">
<a href="https://stackoverflow.com/questions/1827965/is-putting-a-div-inside-an-anchor-ever-correct" target="_blank">
<img id="image1" src="https://www.w3schools.com/html/pic_trulli.jpg" />
</a>

<a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility" target="_blank">
<img id="image2" src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/UniversalEditButton3.png/220px-UniversalEditButton3.png" />
</a>
</div>
<script>
</script>
</body>
</html>

Flex Layout

  1. flex container:

    1. NOTE: float clear vertical-align for the items inside flex boxwill become invalid

    2. display: inline-flex vs display: flex:

      • only apply to flex container, to make it display as inline or block
      • won’t affect flex items inside
    3. flex-direction

    4. flex-wrap: break new line or not when exceeding it container

    5. flex-flow is combinatin of first 2 items:

      1. 1
        2
        3
        4
        flex-direction: row;
        flex-wrap: wrap;
        <==>
        flex-flow: row wrap;
    6. align-items: center;: where flex items sit on the cross axis

    7. justify-content: space-around;: where the flex items sit on the main axis

  2. flex items:

    1. flex: 1 1 20px: three attributes: flex-grow flex-shrink flex-basis
    2. order: 3: like the smaller will be put in front
    3. align-self: override its container’s align-items layout, update position of itself

Grid Layout

  1. Grid Container

    • display: display: grid vs display: inline-grid: only apply on grid container
    • gap: grid-row-gap grid-column-gap grid-gap: set the gap between items
    • col num/width: grid-template-columns: define num of columns and their width
    • row num/width:grid-template-rows: define num of rows and their width
    • horizontally align: justify-content: center, start, end, space-evenly, space-around, space-between
    • vertically align: align-content: center, start, end, space-evenly, space-around, space-between
  2. Grid Items

    1. line: grid-row-start grid-column-end: specify the start & end of span it covers

    2. line shorthand 1 : grid-row grid-column

      • grid-row:1 / 3: range: [line1 line3]
      • grid-column: 1/span 3: from line1, take 3 units, range: [line1 line4]
    3. line shorthand 2 : grid-area = grid-row-start+ grid-column-start + grid-row-end + grid-column-end

      • grid-area: 1 / 2 / 5 / 6: start on row-line 1 and column-line 2, and end on row-line 5 and column line 6:

      • grid-area: 2 / 1 / span 2 / span 3:

      • NOTE: can assign names to grid items, cooperate with grid-template-areas. Used to assign space to the named item

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        https://www.w3schools.com/css/tryit.asp?filename=trycss_grid_grid-area_named1

        .item1 { grid-area: header; }
        .item2 { grid-area: menu; }
        .item3 { grid-area: main; }
        .item4 { grid-area: right; }
        .item5 { grid-area: footer; }

        .grid-container {
        grid-template-areas:
        'header header header header header header'
        'menu main main main right right'
        'menu footer footer footer footer footer';
        }
      • order: specified by starting line# and ending line#

      • 1
        2
        3
        4
        5
        6
        .item1 { grid-area: 1 / 3 / 2 / 4; }
        .item2 { grid-area: 2 / 3 / 3 / 4; }
        .item3 { grid-area: 1 / 1 / 2 / 2; }
        .item4 { grid-area: 1 / 2 / 2 / 3; }
        .item5 { grid-area: 2 / 1 / 3 / 2; }
        .item6 { grid-area: 2 / 2 / 3 / 3; }

CSS:

  1. float & position & box model

    • float
      • when container box has no height, and need to cover all it floating children: clear: both & overflow: hidden
  2. box-sizing=>content-box / border-box

    • box-sizing: content-box; actual width= width + padding + border
      The widthand height properties include the content, but does not include the padding, border, or margin.
    • box-sizing: border-box; actual width = width = content + padding + border
      The width and height properties include the content, padding, and border, but do not include the margin
  3. cursor: pointer cursor acts like <a> when hover

  4. px vs pt vs rem vs em
    https://kyleschaeffer.com/development/css-font-size-em-vs-px-vs-pt-vs/

    • pt: physical measurement, fixed when media once produced
    • px: calculated relative to pt; can be adjusted
    • rem: related to root font-size (default: 16px)
    • em: related to its parents font-size (default: 16px)
  5. resize: indicate how this block element can be resized

    • horizontal: can only resized horizontally
    • vertical: can only resized vertically
    • both: can be resized on both directions
    • none: none resizable
    • initial: == none resizable
  6. width & height

    • make the aspect-ratio stay the same: adjust one, set other one auto

      • tweak image while keep aspect-ratio the same

      • some time will overflow: can add overflow: hidden to its parent

        • 1
          2
          3
          4
          5
          6
          7
          8
          div{
          width: 100px;
          height: 100px;
          }
          .img{
          width: 100%;
          height: auto;
          }
  7. white-space: nowrap, pre, normal

    • how to handle text’s space inside a block to be displayed
    • nowrap: ignore all white-space in one line, like in <span>
    • normal: make text content displayed against the block, change line based on space
    • pre: take in all kind of space (include new line) displayed as they specified in html tag
  8. overflow: deal with inner box exceeding outer box: hidden or scroll

  9. text-overflow: deal with too many words inside box, clip or ellipsis

    • clip(default)
    • ellipsis(省略号)
  10. word-break: deal with long word, which may exceeds width of its container

    1. normal
    2. break-word;: the best clipping way to show the word
  11. max-width vs width

    • width: set the tentative value`

    • max-width set the rule, to restrict the value of width

    • some time can change the aspect-ratio

      • 1
        2
        3
        4
        .image{
        width: 100%;
        height: 100%;
        }

CSS: block vs inline vs inline-block

credit to: http://dustwell.com/div-span-inline-block.html

  1. <div>
    • A “block-level element”
    • can contain all other elements!
    • can only be inside other block-level elements
    • defines a rectangular region on the page
    • tries to be as wide as possible
    • begins on a “new line”, and has an “carriage return” at the end, like a <p>
  2. <span>
    • An “inline element”
    • cannot contain block-level elements!!
    • can be inside any other element
    • tries to be as narrow as possible
    • defines a “snake” on the page
    • doesn’t create any new lines.
  3. block vs inline vs inline-block
    1. block:
      • has width, height, margin, padding
      • force a new line after the block
    2. inline:
      • no width, no height; only respect left/right margin, left/right padding
      • allow other elements to sit on the same line
    3. inline-block:
      • has width, height, margin, padding
      • allow other elements to sit in a line, that’s why called inline

CSS: how to center an element?

  1. https://css-tricks.com/centering-css-complete-guide/
  2. Horizontally
    • center inline element inside block element:
      • text-align: center
    • center block element inside another block element:
      • outer block with specified width: margin: 0 auto
      • outer block with no width: inner width will spread its width to outer, so no need for margin: 0 auto
    • center multiple block element in a row inside another block element:
      • display: inline-block
      • display: flex
    • center multiple block element in different rows inside another block element:
      • margin: 0 auto
  3. Vertically for inline elements
    • center single inline element inside block element:
      • height==line-height
      • padding-top == padding-bottom
    • center multiple inline elements inside block element:
      • display: table& display: table-cell+ vertial-align: middle :
      • display: flex & align-items: center
      • ghost element:
        • full-height pseudo element is placed inside the container and the text is vertically aligned with that (both are inline-block)
        • trick: contain multiple lines into a inline-block to be a whole inline element
        • adjacent with another inline-block with height=100%
        • inline-block elements will align on baseline, if vertical-align doesn’t specify any other value
        • if vertical-align specify middle: browser will try to align all inline boxes in middle, rather than baseline
        • NOTE: vertical-align specify how will itself to be aligned vertically
  4. Vertically for block elements
    • if height of inner block is known: use position: abosulute & top & margin:0.5*height
    • if height of inner block is not known: use position: abosulute top & transform: translateY(-50%)
    • Again: display: flex
  5. Both Horizontally & Vertically
    • if height of inner block is known: use position: abosulute & top & margin:0.5*height
    • if height of inner block is not known: use position: abosulute top & transform: translateY(-50%)
    • Again: display: flex
    • display: grid

DOM structure

  1. difference between HTMLCollection & NodeList

    • HTMLCollection: a collection of element nodes

    • NodeList: a collection of all types of nodes: element node/ text node/ comment node

    • convert them to array, to extract info: Array.prototype.slice.call(NodeList)
  2. everything in an HTML document is a node:

    • The entire document is a document node
    • Every HTML element is an element node
    • The text inside HTML elements are text nodes
    • All comments are comment nodes
  3. navigate across the DOM tree using attibutes: [element node, text node, comment node]

    1. parentNode
    2. childNodes [nodenumber]
    3. firstChild
    4. lastChild
    5. nextSibling
    6. previousSibling
  4. A common error in DOM processing is to expect an element node to contain text.

    1. <title id="demo">DOM Tutorial</title>

    2. The element node <title> (in the example above) does not contain text.

    3. It contains a text node with the value “DOM Tutorial”.

    4. The value of the text node can be accessed by the node’s innerHTML property:

    5. 1
      2
      3
      4
      5
      6
      7
      var myTitle = document.getElementById("demo").innerHTML;

      // or
      var myTitle = document.getElementById("demo").firstChild.nodeValue;

      // or
      var myTitle = document.getElementById("demo").childNodes[0].nodeValue;

DOM API:

  1. get style & set style of an element

    • set style

      • directly use it as attribute elements.style.fontSize = '10px'
      • NOTE: the style name must be camelCase
    • get style:

      • if style is set using style or js, directly get the style

      • if style is set using css without element.style keyword, we will use getComputedStyle()

      • document.defaultView.getComputedStyle(ele,null).getPropertyValue("font-size")

      • 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
        <!DOCTYPE html>
        <html lang="en">
        <head>

        <script>
        function cStyles() {
        var RefDiv = document.getElementById("d1");
        var txtHeight = document.getElementById("t1");
        var h_style = document.defaultView.getComputedStyle(RefDiv, null).getPropertyValue("height");

        txtHeight.value = h_style;
        }
        </script>
        <style>
        #d1 {
        margin-left: 10px;
        background-color: rgb(173, 216, 230);
        height: 20px;
        max-width: 20px;
        }
        </style>
        </head>

        <body>

        <div id="d1">&nbsp;</div>
        <form action="">
        <p>
        <button type="button" onclick="cStyles();">getComputedStyle</button>
        height<input id="t1" type="text" value="1" />
        </p>
        </form>

        </body>
        </html>
  2. console.trace() can be used to trace the call stack of a certain function

  3. addEventListener & removeEventListener

    • 1
      2
      3
      4
      5
      // Attach an event handler to the document
      document.addEventListener("mousemove", myFunction);

      // Remove the event handler from the document
      document.removeEventListener("mousemove", myFunction);
  4. createAttribute() & setAttributeNode()

    • 1
      2
      3
      4
      5
      6
      function myFunction() {
      var h1 = document.getElementsByTagName("H1")[0];
      var att = document.createAttribute("class");
      att.value = "democlass";
      h1.setAttributeNode(att);
      }
  5. createElement() & intersetBefore() & appendChild()

    1. 1
      2
      3
      4
      var btn = document.createElement("BUTTON");        // Create a <button> element
      var t = document.createTextNode("CLICK ME"); // Create a text node
      btn.appendChild(t); // Append the text to <button>
      document.body.appendChild(btn); // Append <button> to <body>
  6. querySelector & querySelectorAll()

    1. https://www.w3schools.com/cssref/css_selectors.asp

    2. querySelector() returns the first element that matches a specified CSS selector(s)

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        // 1. select first p element whose parent is a div
        function myFunction() {
        var x = document.querySelector("div > p");
        x.style.backgroundColor = "red";
        }

        // 2. select all a element has target attribute
        function myFunction() {
        document.querySelector("a[target]").style.border = "10px solid red";
        }
    3. querySelectorAll() returns all elements in the document that matches a specified CSS selector(s), as a static NodeList object.

      1. 1
        2
        3
        4
        5
        6
        7
        function myFunction() {
        var x = document.querySelectorAll("a[target]");
        var i;
        for (i = 0; i < x.length; i++) {
        x[i].style.border = "10px solid red";
        }
        }
  7. element.childNodes vs element.children

    • element.childNodes : returns a NodeList object, including element nodes, text nodes, and comment nodes
    • element.children: return a HTMLCollection object
    • difference:
      • childNodes: contain all nodes, including text nodes and comment nodes
      • children: only contain element nodes.
  8. element.firstChild vs element.firstElementChild

    • difference:
      • firstChild returns the first child node as an element node, a text node or a comment node (depending on which one’s first),
      • firstElementChild returns the first child node as an element node (ignores text and comment nodes).
  9. element.parentNode vs element.parentElement

    • parentElement: returns null if the parent node is not an element node

    • parentNode: is probably the most popular

    • 1
      2
      3
      4
      5
      document.body.parentNode; // Returns the <html> element
      document.body.parentElement; // Returns the <html> element

      document.documentElement.parentNode; // Returns the Document node
      document.documentElement.parentElement; // Returns null (<html> does not have a parent ELEMENT node)
  10. element.previousSibling vs element.previousElementSibling

    • previousSibling: returns the previous sibling node as an element node, a text node or a comment node
    • previousElementSibling: returns the previous sibling node as an element node (ignores text and comment nodes).

DOM events

  1. [Trick]: bind data on element as attribute, then use e.target.getAttribute("data-info")

  2. common types:

    • onload

    • oninput

    • onchange

    • onfocus
    • onmouseover & onmouseout
    • onclick & onmousedown & onmouseup
    • onkeypress & onkeydown & onkeyup
    • onresize
    • onscroll
  3. event propagation: bubbling & capturing

    • Event propagation is a way of defining the element order when an event occurs.
    • bubbling: the inner most element’s event is handled first and then the outer: the <p> element’s click event is handled first, then the <div> element’s click event.
    • capturing: the outer most element’s event is handled first and then the inner: the <div> element’s click event will be handled first, then the<p> element’s click event.

Array-like object under the hood

  1. All credit to: http://2ality.com/2013/05/quirk-array-like-objects.html
  2. An array-like object
    • only has:
      • index to access to elements: a[0]
      • the property length that tells us how many elements the object has: a.length
    • does not have: array methods such as push, forEach and indexOf; have to borrow these methods
    • NOTE:
      • NodeList has the forEach attribute
      • HTMLCollection doesn’t have forEach attribute
    • “borrow” : that why we use:
      • [].slice(array-like)
      • [].indexOf(array-like)
      • [].forEach(array-like)
  3. Two examples of array-like objects
    • result of the DOM method document.getElementsByClassName() (many DOM methods return array-like objects)
    • the special variable arguments .
      • You can determine the number of arguments via arguments.length
      • you can access a single argument, e.g. read the first argument: arguments[0]
  4. Array methods, however, have to be borrowed. You can do that, because most of those methods are generic

  5. Three ways to convert Array-like objects to Array.

    1. Array.prototype.slice.call(arguments)`

    2. Array.from(arguments)

    3. use bind to create a new function slice() function on array-like object, so we can reuse it

      1
      2
      3
      4
      5
      6
      let = getIndexOfA = function(){ 
      let myIndexOf = [].indexOf.bind(arguments);
      return myIndexOf("a") // NOTE: mustn't use arguments.myIndexOf()
      }

      console.log( getIndexOfA("d", "d", "a","d") ) // 2

What can be put inside <head>?

https://github.com/xiaoyu2er/HEAD

JS coding: mutate array according new indices

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
Given an input array and another array that describes a new index for each element, mutate the input array so that each element ends up in their new index. 

// ---------------- in-place mutate array ------------
var arr = ["a","b","c","d","e","f"];
var indices = [2, 3, 4, 0, 5, 1];


// Solution1: real in-place solution!
function inPlaceChange(arr, indices){
for( let i = 0; i < arr.length; i++ ){
while( indices[i] !== i ){
let kickedNum = arr[indices[i]]
let kickedId = indices[indices[i]]

arr[indices[i]] = arr[i]
arr[i] = kickedNum

indices[indices[i]] = indices[i]
indices[i] = kickedId
}
}
}
inPlaceChange(arr, indices)
console.log(arr)

//---------------------------------------------------

var arr = ["a","b","c","d","e","f"];
var indices = [2, 3, 4, 0, 5, 1];
// Solution 2: not real in-place, cause map() use extra space
let arr = indices.map( (id, index)=>arr[indices.indexOf(index)] )
console.log("updated arr: ", arr)

<script async> & <script defer>

  1. normal <script src="script.js" >
    • The HTML parsing is paused for the script to be both fetched and executed
  2. <script async>

    • The HTML parsing is not paused for the script to be fetched,
    • but still be paused for js to be executed
  3. <script defer>

    • The HTML parsing is not paused for the script to be fetched,
    • oppositely, the execution of js is paused util the HTML finish parsing
    • same as <script> right before the </body> tag

How to check data types in JS

https://webbjocke.com/javascript-check-data-types/

write a promise

Promise

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<!DOCTYPE html>
<html>
<head>
<style>
#wrapper {
margin-top: 10px;
overflow: auto;
width: 250px;
display: inline-block;
border: 1px solid red;
overflow: hidden;
}


.listWrapper{
border: 1px solid blue;
height: 200px;
width: 140px;
overflow: auto;
scroll: auto;
}

</style>
</head>
<body>


<div id="wrapper">
<div>
<input id="inputText" />
<button>submit</button>
</div>

</div>

<script>

let data = new Array(45)
data.fill("recommendation word" )


const API = "https://www.demo.com/potentialList"

// create new element below <input>, inside div wrapper


function appendList(elementCollection, wrapper){
let oldlistWrapper = document.getElementsByClassName("listWrapper")[0];
wrapper.removeChild(oldlistWrapper)

let listWrapper = document.createElement("DIV")
listWrapper.setAttribute("class", "listWrapper")
wrapper.appendChild(listWrapper)
let list = Array.prototype.slice.call(elementCollection)
data.forEach( ele=>{
console.log("wrapper: ", wrapper)
let itemNode = document.createElement("DIV")
let textNode = document.createTextNode(ele)
itemNode.appendChild(textNode)
listWrapper.appendChild(itemNode)
})

}
// make API call
function makeAPICall(API, val){
let ajax = new XMLHttpRequest()
ajax.open("GET", "https://www.w3schools.com/js/ajax_info.txt", true)
ajax.onreadystatechange = function(){
if(this.readyState === 4 && this.status === 200 ){
console.log(this.responseText)
let parser = new DOMParser()
let a = parser.parseFromString(this.responseText, "text/html")
console.log(a)
let body = a.getElementsByTagName("BODY")[0]
let wrapper = document.getElementById("wrapper")
console.log(body.children)
// append return list to <input>
appendList(body.children, wrapper)
}
}
ajax.send()
console.log(val)
}


// debounce 500ms to make API call
let timeHandler = null
function inputHandler(e){
clearTimeout(timeHandler)
let val = e.target.value
timeHandler = setTimeout( function(){makeAPICall(API, val)} ,500 )
}

let input = document.getElementById("inputText")
addEventListener("input", inputHandler)
</script>

</body>
</html>

AJAX

  1. XMLHttpRequest is browser built-in object, used to pull data in background

  2. Methods:

    • open(method, url, async, user, psw): specify the request
      • method: the request type GET or POST
      • url: the file location
      • async: true (asynchronous) or false (synchronous)
      • user: optional user name
      • psw: optional password
    • setRequestHeader(): Adds a label/value pair to the header to be sent
    • send(str): Sends the request to the server (GET or POST)
      • if no str provided, send as GET
      • if str is provided, send as the parameters for POST
  3. properties:

    • onreadystatechange: function used to be called when state change
    • readyState: Holds the status of the XMLHttpRequest.
      • 0: request not initialized
      • 1: server connection established
      • 2: request received
      • 3: processing request
      • 4: request finished and response is ready
    • status: Returns the status-number of a request
      • 200: “OK”
      • 403: “Forbidden”
      • 404: “Not Found”
    • responseText: Returns the response data as a string
    • responseXML: Returns the response data as XML data
  4. steps

    1. An event occurs in a web page (the page is loaded, a button is clicked)
    2. An XMLHttpRequest object is created by JavaScript
    3. The XMLHttpRequest object sends a request to a web server
    4. The server processes the request
    5. The server sends a response back to the web page
    6. The response is read by JavaScript
    7. Proper action (like page update) is performed by JavaScript
  5. Code

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      /* --------------- 1. GET  --------------- */
      function loadDoc() {
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
      document.getElementById("demo").innerHTML = this.responseText;
      }
      };
      xhttp.open("GET", "demo_get.asp", true);
      xhttp.send();
      }

      /* --------------- 2. POST --------------- */
      function loadDoc() {
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
      if (this.readyState === 4 && this.status === 200) {
      document.getElementById("demo").innerHTML = this.responseText;
      }
      };
      xhttp.open("POST", "demo_post2.asp", true);
      xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      xhttp.send("fname=Henry&lname=Ford");
      }

Check if undeclared, undefined or null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. `undeclared` check
try{
undeclaredVar
}
catch(e) {
if(e.name === 'ReferenceError') {
console.log('var is undeclared')
}
}

// 2. `undefined` check
var undefinedVar

if (typeof undefinedVar === 'undefined') {
console.log('var is undefined')
}

// 3. `null` check
var nullVar = null

if (nullVar === null) {
console.log('var is null')
}
1…345…9
Mingkai Cao

Mingkai Cao

All-trade Jack, loves full-stack

85 posts
5 categories
136 tags
GitHub E-Mail
© 2020 Mingkai Cao
Powered by Hexo
|
Theme — NexT.Mist v6.0.6