---
# System prepended metadata

title: JS

---

# JS-T
[toc]

---
[Angular socket with nodejs](/WuSVWB89ReagAkK4ocOKsA)
[Node.js](/Uawfp81ARnueCeZhm_sTEw)

---

1. 通常 <script> 會寫在 <head> 標籤裡
因為如果要一開始載入就執行, 通常會寫
-> window.onload:w = funtion() {}
這是全部跑完才會跑得 function, 所以就不會因為寫在 id 上面而讀取不到

2. 如果 <script> 放在最上面, 會讀不到下面的 id

3. 讀取 id, 並放入值
-> document.getElementById("test").innerHTML = '123';

4. 從 js 寫入 html
-> document.write`("<h1>hi</h1>")`;
-> 雖然可以從 script 直接寫進去, 但還是把值放入進標籤(ex. h1 的結構會比較好)

5. console.log(), console. table()
- 可以在網站按 f12(console), 可以看到 console 結果
- console.table 裡若放 array, console 的結果會用 table 呈現 index 和 value(比較清楚)

6. 在 JS 讀取 CSS 數值
- ex. 把 div1 的字體大小改成 div 的字體大小的 1.5倍, (parseFloat("1.2em") == "1.2")
div1.style.fontSize = (parseFloat(div.style.fontSize)) * 1.5 + "em"

6. 變數宣告
-> 也有分 local, global
-> 函數裡宣告就是 local

7. let
block scope
-> 只會在 { } 裡存在
-> ex. for {let i = 0;i < 10;i++)}
-> 出了迴圈就消失

8. const
-> 會一直持續存在

9. var
-> var 也可以當作數字

10. boolean
-> var a = true;
-> var b = fasle;

11. parseInt
在字串裡把數字找出來, 前提是非數字的都在數字後面(ex. 123tommy)
-> parseInt("123tommy") = 123

12. 宣告陣列, 五種都可以,
- js 允許不同 type 的變數放在相同陣列
1. var a = new Array()
2. var a = new Array(3);
3. var a = [];
4. var a = new Array(1.5, '2009', true);
5. var a = [1.5, '2009', true]
- 類似 dictionary 的陣列
var a = {"t" : 1, "o" : 2}
12-1. 加入陣列
- ex.
var a = []
a.push(1);

13. js 函式寫法
var a = function() { } 會等於 function a () { )

var $1 = (id) => { } 等於 var a = function(id) { }

14. JS 不同型態變數運算
var a = "1"
var b = 2
- 加, 減
a + b = "12";
- 乘, 除
a * b = 2;

15. == vs ===
- ==
值一樣即可, ex. 1 == "1" 為 true
- ===
值、型態都要一樣, ex. 1 === "1" 為 false

16. and, or, not in JS
-- and
&&
-- or
-- not
!

17. if 的另一種寫法 in JS
- x = (condition) ? true : false;
- ex.
c = (1!=1) ? 1 : 2; 為 2

18. switch
- 類似 if, 帶入一個參數會等於 case x
- 每個（除了最後一個) case 完要加 break, 不加會往下跑直到遇到 break or 最後一個
- ex.
switch(a=Today()) {
case 1 :
console.log(1);
break; /* 不加 break 會執行 case 2*/
case 2 :
console.log(2);
}

19. for in
- 把 array or string 的 index 值拿出來, 不是 value
- ex.
var ForIn = function() {
str = "123";
b = [1,2,3];
a = {t : 1, o : 2, m : 3};
for (var i in a)
console.log(i);
}
- in a 會印出 t, o, m
- in b 會印出 0, 1, 2
- in c 會印出 0, 1, 2

20. for of
- 把 array or string 的 value 拿出來
- ex.
var ForOf = function() { /* 拿出 value */
b = [1, 2, 3];
c = "123";
for (var i of c)
console.log(i);
}
- of b, c 都會印出 123

21. arguments
所有帶進去函式的參數會在一個陣列(arguments)裡面
- ex. 會印出 2
Test(1, 2, 3);
function Test() { /* 可以不用帶變數去對應 */
console.log(arguments[1]);
}

22. Class
- JS 的 function 也能當作 class
- ex.
function A() {
this.a = 1; # this 代表這個物件(A)底下的a是1, A.a = 1
this.print = function() {
console.log(1);
}
}
var b = new A();
console.log(b.a); # 印出 1
b.print() # 印出 1

23. JSON
- Java Script Object notation(敘述)
- stringify
把陣列變成字串
- ex.
var a = JSON.stringify({a:1, b:'2'})
-> a = "{'a':1,b:'2'}" # 注意, 逗號後的空格不會被讀入字串中
- 可以直接用 key 把 value 拿出來 : 
    ```javascript=
    a = {name : 123}
    console.log(a['name']);
    ```

24. 常見的 Global function, 屬性
- isNaN
判斷是不是數字
- ex.
isNaN("1") or isNaN(1), true
isNaN("t") , false
- indexOf()
- ex.
"123".indexOf("2"), 會是 3
- length
- ex.
"123".length , 會是 3
- substring
如果是負數就是 0, 和 slice 不同
- ex.
"12345".substring(-2,2), 為 12

25. split
- split 可依照資料在 html 中的換行進行切割
- ex.
function GetId(id) {
return document.getElementById(id);
}

window onload = function() {
var a = GetId("2").innerHTML.split("\n"); # 遇到換行就 split
}
<div id = "2">1, a
2, b
3, c
</div>

26. replace
- 替換字串, 換掉第一個要換的
- ex.
"123".replace("1", "a").replace("2", "b"); # 等於 ab3

27. join
- 加入到每個陣列之間
- ex.
var a = [1, 2, 3];
a.join(+) # 等於 1+2+3

28. push
- 直接取代
- ex.
var a = [1, 2, 3];
a.push(4); # 等於 4

29. onsubmit, onresetd

30. 類似 dictionary 的東西
-ex.
var arrArea=[];
arrArea["台灣"]=["台北", "台中", "台南", "高雄"];
arrArea["日本"]=["東京", "大阪", "京都", "福岡", "札幌"];

31. 在 JS 插入 selection 表單
- html 的
<select id = "area">
- js
function insert() {
for (let area in arrArea) {
let opt = new Option();
opt.text = area;
$("area").add(opt);
}
}

32. function 加 () 與 不加
加了(), 就是馬上呼叫, 不加就是當某某條件就會觸發
-ex.
window.onload = function() {
$("ncnu").onclick = putMark; // 當點了就會觸發這個函式
}

33. function(e)
e = window.event

34. NaN
- 意義
not an number

35. js get option value
var num = $("sel_num").selectedIndex;
console.log($("sel_num")[num].text);
        
36. 日期

- 前一日
        date = new Date();
        date.setDate(date.getDate() - 1);
- 設置小時和分鐘
    ```javascript=
    var today = new Date();
    today.setHours(0,0,0,0); // 0 點 0 分 0 秒
    ```
        
37. 把 td 的 value 置中
td {
    text-align : center // 不能用 align :center
}

38. 把 json 的 key 用 變數
var a = 'abc';
{[a] : 1};

39. 自動按下按鍵
const event = new KeyboardEvent('action', {'keyCode' : num}) 
// action = keyup, keypress, keydown, num = 按鍵數字
document.dispatchEvent(event);

- input checkbox default checked
```javascript=
<input type = 'checkbox' name = 'paid' id = 'paid' checked/>
```

        
- JSON.stringify() doesn't know how to serialize a BigInt
    值加上 .toString()

## function
- copy function
    ```javascript=
	<button type = 'button' id = 'copy_bt'>複製</button>

    function cpContent() { copy the content to clipboard
            // select class name = copy
            var all_need_copy = getClass('copy');
            var total_str = '';
            // add the value needed to be copied
            for (let i = 0;i < all_need_copy.length;i++) {
                total_str += all_need_copy[i].value;
                total_str += "<new_element>";
            }
             // create a temporary textarea to place the value of copy
            var el = document.createElement('textarea');
            el.value += total_str; // add the value
            // make textarea unseen and unused
            el.setAttribute('readonly', '');
            el.style = {position: 'absolute', left: '‑9999px'};
            document.body.appendChild(el);
            el.select();
            var copy_suc = document.execCommand('copy');
            alert(copy_suc ? 'copy successfully'  : 'copy failed'); //alert successful or failed
            document.body.removeChild(el); // remove copt textarea
        }
    ```

- paste function 
    ```javascript=
    <button type = 'button' id = 'paste_bt'>貼上</button>
    
    getId("paste_bt").addEventListener('click', pasteContent); // paste the clipboard content
        
    async function pasteContent() { // paste the clipboard content
        var paste_text = await navigator.clipboard.readText();
        paste_text = paste_text.split("<new_element>");
        // the element need to put in the content of copied 
        var all_need_copy = getClass('copy');
        // put clickboard content in 
        for (let i = 0;i < all_need_copy.length;i++) {
            all_need_copy[i].value = paste_text[i];
        }
    }
    ```

- isNum
    ```javascript=
    function isNum(n) { // 是不是數字
	    return !isNaN(parseFloat(n)) && isFinite(n);
	}
    ```
- output html to pdf
    - javascript
    ```javascript=
    const btn = document.getElementById("button");  
    btn.addEventListener("click", function(){
        // confirm the user
        if (!confirm('確定輸出 PDF 檔？'))
        return;
        // disable button
        btn.style.display = 'none';
        window.jsPDF = window.jspdf.jsPDF;
        var element = document.getElementById('body');
        let imgsData = html2pdf().from(element).set( { 
            margin: -1, filename: 'text', 
        html2canvas: { scale:4, useCORS: true }, 
                jsPDF: {orientation: 'portrait', unit: 'pc', format: 'a3', compressPDF: true} 
            }).output('img',{},'img'); setTimeout(()=>{ 
                if(imgsData && imgsData._result){ 
            let width = imgsData._result.width; 
            let height = imgsData._result.height; 
            let pdf2 = new jsPDF('', 'pt', [width, height]); 
            pdf2.addImage(imgsData._result, 'jpeg', 0, 0, width, height); 
            pdf2.save(getId('stu_year').innerHTML + '_' + getId('name').innerHTML + '_畢業學分檢核表'); 
        } 
         },800) 
        // show button again
        setInterval(function(){
            btn.style.display = 'block';
        },1000);
    });
    ```
    - html
        ```html=
        <button id="button">輸出PDF檔</button>
        ```
- search 
    ```javascript=
    function getId(id) {
        return document.getElementById(id);
    }

    var all_no = []; // 全部快捷的編號
    var all_real = []; // 全部的真實名稱
    var all_rela = []; // 全部的對應快捷
    async function putKey() { // 把目前的快捷對應放進 table
        const {data : hot_key} = await axios.get('/hot_key');
        // 清空
        all_no = []; 
        all_real = [];
        all_rela = []; 
        for (let i = 0;i < hot_key.length;i++) {
            // 放入 table
            tab.innerHTML +=  
            "<tr/><td/>"+ hot_key[i].no  + 
             "<td>" + hot_key[i].real_name + "</td>" +
             "<td>" + hot_key[i].relative + "</td></tr>";
            // 放入 array
            all_no.push(hot_key[i].no);
            all_real.push(hot_key[i].real_name);
            all_rela.push(hot_key[i].relative);
        }
        getId('search').addEventListener('change', checkSearch);
    };
    putKey();

    function checkSearch() {
        getId('search').addEventListener('change', checkSearch);
        // 搜尋條件是否為空的
        if (getId('search').value == "" || getId('search').value == " ") {
            // 先清空 table
            tab.innerHTML = 
            "<tr><td class = 'col_title' colspan = '3'><b>目前快捷</b>" + 
            "<span class = 'position-absolute top-0 end-0' id = 'search_pos'>" + 
            "搜尋 : <input type = 'text' id = 'search'/></span></td></tr>" +
            "<tr><td>編號</td><td>真實名稱</td><td>對應快捷</td></tr>";
            putKey();
            return; // 沒有輸入條件
        }
        else {
            var search_value = getId('search').value;
            tab.innerHTML = 
            "<tr><td class = 'col_title' colspan = '3'><b>目前快捷</b>" + 
            "<span class = 'position-absolute top-0 end-0' id = 'search_pos'>" + 
            "搜尋 : <input type = 'text' id = 'search'/></span></td></tr>" +
            "<tr><td>編號</td><td>真實名稱</td><td>對應快捷</td></tr>";
        }
        var is_put = []; // 已經放進去的
        // 條件一 : 真實名稱
        for (i in all_real) {
            // 有符合的字串
            if (all_real[i].includes(search_value)) { 
                is_put.push(i);
                putEachKey(i);
            }
        }
        // 條件二 : 對應快捷
        for (i in all_rela) {
            // 還沒放過且有符合的字串
            if (!(is_put.includes(i)) && all_rela[i].includes(search_value)) { 
                is_put.push(i);
                putEachKey(i);
            }
        }
        getId('search').addEventListener('change', checkSearch);
        getId('search').value = search_value;
    }

    function putEachKey(i) { // 把目前的快捷對應放進 table
        tab.innerHTML +=  
        "<tr/><td/>"+ all_no[i]  + 
         "<td>" + all_real[i] + "</td>" +
         "<td>" + all_rela[i] + "</td></tr>";
    };
    ```

- is collide 偵測碰撞
```javascript=
function isCollide(a, b) { // 偵測左邊球門是不是進球
    var aRect = a.getBoundingClientRect();
    var bRect = b.getBoundingClientRect();
    return !(
        ((aRect.top + aRect.height) < (bRect.top)) ||
        (aRect.top > (bRect.top + bRect.height)) ||
        ((aRect.left + aRect.width-100) < bRect.left) ||
        (aRect.left > (bRect.left + bRect.width))
    );
}
```

- get random, 隨機數
```javascript=
function getRandomInt(max) { // max = 範圍最大值
  return Math.floor(Math.random() * max);
}
```

- 設置等待後，自動結束，不會一直重複
```javacript=
var myInterval = setInterval(function(){
     clearInterval(myInterval);
},50);
```

- 設定 table 的 row
```javascript=
function mkAddTableRow() { // 製作新增帳務 table 的欄位
    var add_tab = getId('add_tab'); // 要新增的 table
    for (let i = 0;i < total_row;i++) { // 總共幾個 row
        var tr = document.createElement('tr'); // row
        // 編號
        var td_id = document.createElement('td'); // column
        td_id.innerHTML = i+1; // 編號
        tr.appendChild(td_id); // row append column
        // reason column
        var td_reason = document.createElement('td'); 
        var td_reason_input = document.createElement('input');
        td_reason_input.id = 'add_reason' + i;
        td_reason.appendChild(td_reason_input);
        tr.appendChild(td_reason); // row append column
        // money column
        var td_money = document.createElement('td'); 
        var td_money_input = document.createElement('input');
        td_money_input.id = 'add_money' + i;
        td_money.appendChild(td_money_input);
        tr.appendChild(td_money); // row append column
        // mark column
        var td_mark = document.createElement('td'); 
        var td_mark_input = document.createElement('input');
        td_mark_input.id = 'add_mark' + i;
        td_mark.appendChild(td_mark_input);
        tr.appendChild(td_mark); 
        // row append column
        add_tab.appendChild(tr); 
    }
    // submit button
    var td_submit = document.createElement('button'); 
    td_submit.id = 'submit_add';
    td_submit.innerHTML = '送出';
    add_tab.appendChild(td_submit);
}
```

- bubble sort time or other
```javascript=
function bSort(arr) { // bubble sort 排序購買時間和開藥時間
    for (let i = 0;i < arr.length-1;i++) {
        for (let j = 0;j < arr.length-1;j++) {
            var time = arr[j].purchase_date ? arr[j].purchase_date : arr[j].time;
            var times = arr[j+1].purchase_date ? arr[j+1].purchase_date : arr[j+1].time;
            if (time < times) {
                var temp = arr[j+1];
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}
```

- 找 element 的位置，並且基於原本位置更改 : 
    ```javascript=
    var top = getComputedStyle(getId('xxx')).top;
    getId('xxx').style.top = (parseInt(top) + num) + 'px';
    ```
    
- 今日日期 : 
    ```javascript=
    function todayDate() { // 今日日期
        var today = new Date();
        var dd = String(today.getDate()).padStart(2, '0');
        var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
        var yyyy = today.getFullYear();

        today = yyyy + '-' + mm + '-' + dd;
        return today;
    }
    ```
    
## note
- aysnc with out function name
    1. use in add event listener 
    ```getId('logout').addEventListener('click', async() => {int a;})```
    2. call directly
    ```javascript=
    (async() => {
        int a;
    })(); 
    ```

- eventlistener 的 target id
    console.log(event.target.id);

- beforeonload 關掉離開的 dialog : 
    ```jvascript=
    window.addEventListener("beforeunload", async function(e) { 
        e.returnValue = null;
        return null;
    })
    ```
- beforeonload 離開顯示 dialog : 
    ```javascript=
    window.onbeforeunload = async function(e) {
    }
    ```
- try catch 的 break
    - 設節點 : 
    ```javascript=
    omgalabel: try {
        if( bar ) break omgalabel;
        console.log( bar );
        // code 
    }
    catch( e ){
        //  This code should not execute if !!bar 
    }
    finally {
        // Code that executes no matter what
        console.log( true );
    }
    ```

- disable input 的紀錄 : 不能用在 css
`<input type = 'text' autocomplete="nope"></input>`

- clear canvas
```javascript=
const context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
```

- auto press key 自動按鍵
```javascript=
var evt = new KeyboardEvent("keydown", {
    bubbles: true, cancelable: true, keyCode: 13
});
element.dispatchEvent(evt);
```

- 用字串當變數名稱
```javacscript=
window[variableName];
```

- transaction
```javascrip=
let conn = await pool.getConnection();
// Start Transaction
await conn.beginTransaction();
try {
    // Add Data in a batch，一定要把 value 放在後面，不能直接代入
    await conn.batch("insert into test(`a`) values(?)", [1]);
    await conn.batch("insert into test(`a`) values(?)", ['12t']);
    // commit
    await conn.commit();
}
catch(e) {
    console.log(e);
    // 還原
    await conn.rollback();
}
conn.release();
```
    
- event.path 並不是所有的瀏覽器都有提供
    - 解決方法 : `event.composedPath()`
    - 教學 : 
    
- page : 分頁功能
    - html : 
    ```html=
    <!-- table page -->
    <ul class="nav nav-tabs" id="myTab" role="tablist">
    </ul>
    <div id = 'tab_pos_title' class = 'position-relative'></div>
    <div id = 'tab_pos' class = 'position-relative'></div>
    <nav aria-label="Page navigation example" class = 'position-absolute bottom-0' id = "page_top">
    </nav>
    ```
    - css : 
    ```css=
    <style>
    a {
        color : #f33;
        -webkit-user-select:none;
        -moz-user-select:none;
        user-select:none;
        text-align : center;
    }

    #page_last {
        width : 105px;
    }

    #page_next {
        width : 105px;
    }

    #last_page {
        width : 80px;
    }

    #first_page {
        width : 80px;
    }

    #page_top {
        left : 30%
    }
    </style>
    ```
    - js : 主要改 putRecord、getPageNum
    ```javascript=
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>

    <script>
        function lessTime(times) { // 把時間弄得好看一點
            if (times == null) // 如果還沒有時間
                return null;
            var new_time = '';
            for (let i = 0;i < times.length-5;i++) { // 不要後面五個字元
                if (times[i] == 'T') { // 把 T 換掉
                    new_time += '\n';
                    continue;
                }
                new_time += times[i];
            }
            return new_time;
        }

        function getId(id) {
            return document.getElementById(id);
        }

        // make page
        let now_page = 0; // 現在的頁數

        async function putRecord(page) {
            const {data : user} = await axios.get('/viewPa/nId');
            const {data : med_inventory_each} = await axios.get('/med_inventory_each', {params : {aId : user.nId}});
            const {data : medicines} = await axios.get('/medicines_normal');
            med_inventory_each.sort(function(a,b){ // 從最後看的開始排序
                return b.no - a.no;
            });
            var count_records = 0; // 總共有幾個可以放的紀錄, 不包含看完的
            var count_added_records = 0; // 真正放了幾個紀錄進 table
            total_len = 0; // when reset table, reset the total_len too
            getId("tab").innerHTML = ""; // 每次重新點, 都清空 table
            await tabTitle(); // 製作 tab title
            // 把各個批次的藥分開
            for (let i = 0;i < med_inventory_each.length;i++) {
                    //console.log(med_inventory_each[i]);
                count_records++; // 計算總共有加了幾筆紀錄
                if (count_records <= (page-1) * limit_records) // 還沒到可以放進去的 index
                    continue; // 重找
                if (count_added_records >= limit_records) // 真正放進去的紀錄不能超過限制的次數
                    break; // 停止
                count_added_records++; // 真正放進去的次數
                for (let k = 0;k < medicines.length;k++) {
                    if (med_inventory_each[i].code == medicines[k].code) { // 藥碼相同，有此筆藥的資訊  
                        tab.innerHTML += "<tr id = 'each_row'" + i + "/><td/>" + med_inventory_each[i].no + 
                        "<td>" + med_inventory_each[i].code + "</td>" +
                        "<td>" + medicines[k].medi_eng + "</td>" +
                        "<td>" + parseFloat(med_inventory_each[i].now_quantity) + "</td>" + 
                        "<td>" + med_inventory_each[i].expire + "</td>" +
                        "<td>" + med_inventory_each[i].aId + "</td></tr>";
                        break;
                    }
                }
            }
        }

        var total_len = 0;
        let limit_records = 14; // 頁面能顯示最大的列數量

        async function tabTitle() { // 製作 table title
            const {data : user} = await axios.get('/viewPa/nId');
            getId('tab').innerHTML = 
            "<tr><td colspan = '7'>帳號：" + user.nId + "。單次批貨藥品紀錄</td>" + 
            "</tr><tr><td>編號</td><td>藥碼</td><td>藥品名稱</td><td>數量</td><td>過期日期</td><td>負責人員</td>";
        }

        function mkPage(page_num) { // make page with page_num
            if (getId('page_list')) // if page_list exist, renew one
                getId("page_list").remove(); // remove
            // 如果總頁數 < 一行限制最大頁數，一行限制最大頁數 = 總頁數
            var temp_max_col_page = total_page_num < max_col_page ? total_page_num : max_col_page; 
            // create and add the needed elements
            var page_list_element = document.createElement("ul");
            page_list_element.className = "pagination";
            page_list_element.setAttribute("id", "page_list");
            var page_top = getId("page_top");
            page_top.appendChild(page_list_element);
            var page_list = getId('page_list');
            var page_list = getId('page_list');
            if (page_num >= temp_max_col_page) { // 一行最大只能 10 頁
                page_num = temp_max_col_page + parseInt(now_page);
            }
            if (page_num >= total_page_num) { // 不能超過最大頁數
                page_num = total_page_num;
            }
            if (now_page > total_page_num-temp_max_col_page) {
                // 總共的頁數不能低於 10 ，所以當現在頁數 < 總頁數-10，開始的頁數還是要保持在總頁數 -10
                start_page = total_page_num-temp_max_col_page;
            }
            else { // 開始頁數 = 現在頁數
                start_page = now_page;
            }
            // 製作元素

            // 上一頁的元素
            var page_li= document.createElement("li");
            page_li.className = "page-item";
            var page_a = document.createElement("a");
            page_a.className = "page-link";
            page_a.setAttribute("id", "page_last");
            page_a.innerHTML = '上一頁'; // start from page 1
            page_li.appendChild(page_a);
            page_list.appendChild(page_li);

            // 首頁的元素
            var page_li= document.createElement("li");
            page_li.className = "page-item";
            var page_a = document.createElement("a");
            page_a.className = "page-link";
            page_a.setAttribute("id", "first_page");
            page_a.innerHTML = '首頁'; // start from page 1
            page_li.appendChild(page_a);
            page_list.appendChild(page_li);

            for (let i = start_page;i < page_num;i++) { // make page with page num
                var page_li= document.createElement("li");
                page_li.className = "page-item";
                var page_a = document.createElement("a");
                page_a.className = "page-link";
                page_a.setAttribute("id", "page_"+(i+1));
                if (i == now_page) { // 目前的頁數要用黑色字體
                    page_a.style.color = 'black';
                }
                page_a.style.width = '50px';
                page_a.innerHTML = (parseInt(i)+1); // start from page 1
                page_li.appendChild(page_a);
                page_list.appendChild(page_li);
            }
            // 尾頁的元素
            var page_li= document.createElement("li");
            page_li.className = "page-item";
            var page_a = document.createElement("a");
            page_a.className = "page-link";
            page_a.setAttribute("id", "last_page");
            page_a.innerHTML = '尾頁'; // start from page 1
            page_li.appendChild(page_a);
            page_list.appendChild(page_li);

            // 下一頁
            var page_li= document.createElement("li");
            page_li.className = "page-item";
            var page_a = document.createElement("a");
            page_a.className = "page-link";
            page_a.setAttribute("id", "page_next");
            page_a.innerHTML = '下一頁'; // start from page 1
            page_li.appendChild(page_a);
            page_list.appendChild(page_li);
        }

        let start_page; // 開始的頁數
        let max_col_page = 10; // 一行最多可以有幾個頁數
        function mkPageListener(page_num) { // 依照 page 的數量監聽
            // 如果總頁數 < 一行限制最大頁數，一行限制最大頁數 = 總頁數
            var temp_max_col_page = total_page_num < max_col_page ? total_page_num : max_col_page; 
            mkPage(page_num); // 製作頁面按鈕
            if (page_num >= temp_max_col_page) { // 最大只能 15 頁
                page_num = temp_max_col_page + parseInt(now_page);;
            }
            if (page_num >= total_page_num) {
                page_num = total_page_num;
            }
            getId('page_last').addEventListener('click', turnPage);
            getId('page_next').addEventListener('click', turnPage);
            getId('last_page').addEventListener('click', turnPage);
            getId('first_page').addEventListener('click', turnPage);
            for (let i = parseInt(start_page)+1;i < page_num+1;i++) // 從第一頁開始監聽
                getId('page_'+i).addEventListener('click', turnPage); // put records in table depend on page num
        }

        function turnPage(e) { // 翻頁
            if (e.target.innerHTML == "上一頁") {
                // 不能讓上一頁到 -1
                if (now_page < 1) {
                    now_page = 0;
                }
                else {
                    now_page -= 1;
                }
            }
            else if (e.target.innerHTML == "下一頁") {
                if (now_page+1 < total_page_num) { // 下一頁不能超過最後一頁
                    now_page += 1;
                }
            }
            else if (e.target.innerHTML == "首頁") {
                now_page = 0;
            }
            else if (e.target.innerHTML == "尾頁") {
                now_page = total_page_num-1;
            }
            else { // 現在頁數 = 案的頁數-1
                now_page = parseInt(e.target.innerHTML)-1;
            }
            mkPageListener(total_page_num); // 重新製作頁面按鈕、監聽
            putRecord(now_page+1); // 把紀錄放進 table, 現在是第幾個 table, 第幾頁
        };

        let total_page_num;
        async function mkDoc(e) { // 製作醫生 table
            //now_page = 0;
            if (e == 'first') {
                var tab_id = 1;
            }
            else {
                var tab_id = getDid(e.target.id); // 取得 dId
            }
            now_table = tab_id;
            //mkTable(tab_id); // 製作該 id 的 table
            putRecord(tab_id);
            const {data : user} = await axios.get('/viewPa/nId');
            const page_num = axios.get('/getPageNum', {params : {all_med : true, aId : user.nId}});
            page_num.then(function(result) { // make page
                var page_num_result = {data : result}.data.data.page_num; // total records num
                total_page_num = countPageNum(page_num_result); // total_page_num = all records/limit_records
                mkPageListener(total_page_num); // make listener of page list 
            });
        }

        function countPageNum(page_num_result) { // count page num
            let page_num = Math.floor(page_num_result/limit_records); // without float
            let page_less = page_num_result%limit_records; 
            if (page_less != 0) // is float
                page_num += 1; // add one more page
            return page_num;
        }

        mkDoc('first');

    </script>
    ```
    
- 選取全部相同 id
    ```javascript=
    for (let i = 0;i < document.querySelectorAll("[id='show_details']").length;i++) {
        document.querySelectorAll("[id='show_details']")[i].addEventListener('click', showDetail)
    }
    ```

# Node.js
## 筆記
- 新增放圖片檔案
    教學 : https://www.geeksforgeeks.org/how-to-fetch-images-from-node-js-server/

- config
- 

1. 物件
var a = { firstName: 'James', lastName: 'Bond' }

2. npm
- 只會在單一資料夾裡安裝
npm install 套件
npm -i --save 套件 

- 會在全域中安裝
npm install -g 套件

- 解除安裝
npm uninstall 套件

3. 內建的 http module
- ex.
var http=require('http');
var server=http.createServer(function(req,res){
if(req.url=='/'){
res.writeHead(200,{'Content-Type':'text/html'});
res.write('<html><body>This is Home Page.</body></html>');
res.end();
}else if(req.url=='/student'){
res.writeHead(200,{'Content-Type':'text/html'});
res.write('<html><body>This is student Page.</body></html>');
res.end();
}else if(req.url=='/admin'){
res.writeHead(200,{'Content-Type':'text/html'});
res.write('<html><body>This is admin Page.</body></html>');
res.end();
}else
res.end('Invalid Request!');

});

server.listen(5000);

4. express
- 安裝
npm install express --save # -- save 會有 .json 檔存放套件資訊

- 路由
```javascript=
app.get('/', function (req, res) {
res.send("<html><body><h1>Hello World</h1></body></html>");
});

var server = app.listen(5000, function () {
console.log('Node server is running..');
});
```
5. express 呼叫寫好的 html
    - 安裝 body-parser
    npm install body-parser

    -ex.
    ```javascript=
    var express = require('express');
    var app = express();
    var bodyParser = require("body-parser");
    app.use(bodyParser.urlencoded({ extended: false }));
    app.get('/', function (req, res) {
    res.sendFile(__dirname+'/templates/test.html'); // ___
    });

    var server = app.listen(5000, function () {
    console.log('Node server is running..');
    });
    ```
    - `npm init`
    - `npm install`
    - 

6. 永遠執行 .js
- 安裝
sudo npm install -g forever # 要為全域的

- 執行
forever start test.js

- 查看執行中的檔案
forever list

7. 連線到 mariadb
- 安裝
sudo npm install mariadb

- ex.
const db = require("mariadb");
const pool = db.createPool({
host : 'localhost',
user : 'wang',
password : 'wang313',
database : 'clinic'
});

async function connect() {
let conn;
try {
let conn = await pool.getConnection();
let rows = await conn.query('select * from test');
console.log(rows);
}
catch(err) {
console.log(err);
}
}
connect()

8. jade-template engine
- 安裝
sudo npm install jade

- 建立資料夾放 .jade 檔，預設路徑為相同路徑下的 views 資料夾
vi ./views/test.jade

- ex.
app.set("view engine", "jade");
app.set("views", "jade"); // 設定資料夾名稱，預設為 views
app.get('/', function (req, res) {
res.render('test'); // path = ./jade/test.jade
});

9. 傳送資料
- vi .js
```javascript=
app.get('/', async function (req, res) {
var db = require('mariadb');
const pool = db.createPool({
host : 'localhost',
user : 'wang',
password : 'wang313',
database : 'clinic'
});
let conn = await pool.getConnection();
let rows = await conn.query('select * from test');
res.render('test',{test:rows});
});
```

- vi .jade
doctype html
html
head
title 新增病患資料
body
ul
each item in test
li=item.a

10. 接收資料
- 前端

- 後端
要記得加 index
- ex.
let order = await conn.query('select pa_num from doctors where dId = ?;', req.body.dId);
console.log(order[0].pa_num);

- 前端
用 JSON.stringify 把 [Object object] 的 value 拿出來

11. sort
- ex. rec 是 json, 用 rec 裡的 r_num 大小來排序
rec.sort(function(a,b){
return a.r_num - b.r_num;
});

- ex.
axios.get('/data').then(function(response) {
JSON.stringify(response.data[i].pId)
})

11. 時區問題
- 帶參數執行
env TZ='Taiwan/Taipei' node test.js

12. redirect 帶參數
var string = encodeURIComponent('1');
res.redirect('/?valid=' + string);

13. api 回傳 json 給 html
- api
return res.json({a : 1})

- html
<script>
(async() => {
const {data : rec} = await axios.get('/records');
})();
</script>

14. jwt cookie
- 設定 cookie, 到 api, 先回傳 cookie，res.cookie 完可以再回傳別的(res.json、sendfile)
const {data : rec} = await axios.get('/records');
const token = jwt.sign({ data, exp: Math.floor(Date.now() / 1000) + (60 * 15) }, 'my_secret_ key')
res.cookie('token', token, { httpOnly: false, secure: false, maxAge: 3600000 });

- 路由查看 cookie,
const user = jwt.verify(req.cookies.token, 'my_secret_key');

- html 接收 cookie
-> 先到路由設定回傳 cookie 值
router.get('/dId', function(req, res) {
const user = jwt.verify(req.cookies.token, 'my_secret_key');
return res.json({dId : user.data.aId});
});

-> 再到 html 接收
const {data : user} = await axios.get('/docMain/dId');

15. 轉到其他網址
- 第一種
```javascript=
res.statusCode = 302;
res.setHeader("Location", "http://localhost:8080/login");
res.end(); // setHeader 的 end 要 ()
```

- 第二種
```javascript=
res.redirect('/login')
res.end // 不用 ()
```

16. 好用的 js 套件網站
bestofjs

17. cookie
- 設置 cookie
```
const data = {update_rId : req.body.rId};
const update_rId = jwt.sign({data, exp: Math.floor(Date.now() / 1000) + (60 * 15) }, 'my_secret_key'); // 加密
res.cookie('update_rId', update_rId,  { httpOnly: false, secure: false, maxAge: 3600000 })
```
- 解 cookie
``const user = jwt.verify(req.cookies.token_name, 'my_secret_key');``

- error : mariadb too many connections
close the pool
``pool.end();``

- show Promise { <pending> }
    - 加上 await : `await conn.query`
    - 要加上 then    
    let userToken = AuthUser(data) console.log(userToken) // Promise { <pending> } 
    userToken.then(function(result) { console.log(result) // "Some User token" })

- 直接呼叫函式
    document.addEventListener("keyup", async(event) => {int a = 0;}

- get url, 加上參數
    `window.location.href = "/ckPatients?view=true"`

- redis : client closed error
    - 改用 ioredis
        `npm install ioredis`

- `sendfile depreacate, change to sendFile`   
    - 相對路徑 : `res.sendFile('public/appversion.html', { root: '.' });`

## 新增專案
1. 複製 config、node_modules、config.js、package.json、test.js
2. 改 config/default.json 的 file path


## 樣板
- 創一個新的 router : 
    - app.js 
    ```javascript=
    app.use('/hot_key', require('./api/hot_key'));
    ```
    - 建 ./api/xxx.js
    ```javascript=
    const router = require('express').Router();
    var bodyParser = require("body-parser");
    var db = require('mariadb');
    var func = require('../module/func');
    var jwt = require('jsonwebtoken');
    const pool = db.createPool({
        host : 'localhost',
        user : 'wang',
        password : 'wang313',
        database : 'clinic'
    });
    var config = require("config"); // 設定檔
    var root = config.get('server.root'); // 根目錄位置

    router.get('/', async function(req, res) { // 回傳 json
        try {
            const user = jwt.verify(req.cookies.token, 'my_secret_key');
        }
        catch(e) {
            console.log(e);
            res.redirect('/login');
            res.end();
            return;
        };
        let conn = await pool.getConnection();
        let medicines_normal = await conn.query('select * from hot_key;');
        conn.release();
        res.json(medicines_normal);
        res.end;
    });

    router.get('/relation', async function(req, res) { // 回傳 html
        try {
            const user = jwt.verify(req.cookies.token, 'my_secret_key');
        }
        catch(e) {
            console.log(e);
            res.redirect('/login');
            res.end();
            return;
        };
        res.sendFile(root + 'templates/keyRelation.html');  //回應靜態文件
        res.end;
        return;
    });

    module.exports = router;
    ```
    - 建 ./templates/xxx.html
    ```html=
    <html>
        <script src = 'https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js' ></script>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
        <head>
            快捷鍵關係圖
        </head>
        <body>
           <table id = 'tab' border = '1'>
               <tr>
                   <td colspan = '3'>目前快捷鍵</td>
               </tr>
               <tr>
                   <td>編號</td>
                   <td>真實名稱</td>
                   <td>對應快捷</td>
               </tr>
           </table>
        </body>
        <style>
            td {
                text-align : center;
            }
        </style>
        <script>
            (async() => {
                const {data : hot_key} = await axios.get('/hot_key');
                for (let i = 0;i < hot_key.length;i++) {
                    tab.innerHTML +=  
                    "<tr/><td/>"+ hot_key[i].no  + 
                     "<td>" + hot_key[i].real_name + "</td>" +
                     "<td>" + hot_key[i].relative + "</td></tr>";
                }
            })(); 
        </script>
    </html>
    ```
    
## syntax sugar
- 教學
    https://sophiali.dev/syntactic-sugar-examples-javascript
- IF
    condition ? expressionIfTrue : expressionIfFalse
    ex. 
    ```
    int i = 2;
    i > 1 ? console.log('I am true') : console.log('I am false');
    ```
    ```javascript=
    `${med_inventory_each[i].expire ? med_inventory_each[i].expire : '無'}`
    ```
    output = I am true
- direct use variable in string
    ``console.log(`${variable}`);``
    
## socket.io-聊天室
- 教學  
https://single9.net/2017/12/node-js-%e8%88%87-socket-io-%e5%8d%b3%e6%99%82%e8%81%8a%e5%a4%a9%e5%ae%a4%e5%af%a6%e4%bd%9c/
    - 後端
    ```javascript=
    io.on('connection', (socket) => {
        // 斷線
	    socket.on('disconnect', () => {
		    //console.log('Bye~');  // 顯示 bye~
	    });
	
        // 前端呼叫這個函式
	    socket.on("docMsg", (msg) => {
            // 回傳 "msg", 前端要去 call msg
		    io.emit("msg", msg);
	    });
    })
    ```
    
    - 前端
    ```javascript=
    // 傳訊息給後端的 docMsg 
    socket.emit("docMsg", msg);
    
    // 接收後端 msg 回傳的訊息
    socket.on("msg", function (chat_msg) {
        console.log(chat_msg);
    })
    ```
- return promise pending
    - solution : use then to get value
    ex. 
    ```javascript=
    checkFinancial().then(function(remaining) {
        console.log(remaining);
    })
    ```
    
# CSS
- scroll (拉霸)
    - 說明 : 先訂好最大高、寬，如果超出範圍就變成 scroll，如果是 table，要先用 div 包起來，再去設定 div 的長、寬。先把 div 和 table 設 position absolute，table 之後可以再調回 relative，要先去設定 table 的 margin 才能把拉霸的距離設正確。
    - ex. 
    ```javascript=
    html -
    <div class = 'position-absolute' id = 'scroll_div'>
        <table class = "position-absolute" id = 'tab' border = '2'>
        </table>
    </div>
    #scroll_div { // div
        width : 250px;
        height: 600px; 
        overflow-x: auto; 
        overflow-y: auto; 
    }
    
    #now_tab { // 第二種 div，應該是這個才可
    position : absolute;
    height : 10%;
    width : 50%;
    overflow-y : auto;
    }
    
    #tab { // table，讓拉霸距離正確
        width : 100%;
    }
    ```
    - 警告 : 如果用 col-md-x，會讓內容和拉霸產生距離。
    
- 父層和子層的關係 : 
    - 教學 : https://ithelp.ithome.com.tw/articles/10243699
    - 主要關係 : 
    ![](https://i.imgur.com/X3fgQSt.png)
    
- 彈出畫面
    - 範例 : https://codepen.io/tommygood/pen/dyKQKNK
    
- 文字不能被複製
    ```css=
    div{
        -webkit-user-select:none;
        -moz-user-select:none;
        user-select:none;
    }
    ```

- 兩個物件，其中一個要較上層
```css=
#test {
    z-index:99;
}

#test1 {
    z-index:98;
}
```

- 旋轉角度
```css=
.rotate_image_right {
    transform: rotate(-60deg);
}
```
    
- 資料處理中的顯示
    https://codepen.io/tommygood/pen/YzjMYoY
        
# React.JS
- install : `npm install --save create-react-app`

- 建專案 : `create-react-app app_name`
        - error : 找不到此命令
            可能因為 node 版本太舊，改用
            `npx create-react-app app_name`
    
- 語法 : 
    - 值在 return 中要用 div 包起來

- 模板 : 
    - 建 class : 
        - Sid.js
            ```javascript=
            import React from 'react';

            class Sid extends React.Component {
                render() {
                    return (
                        <div>
                        Sid test
                        </div>
                    )
                }
            }

            export default Sid // 要 export class 才能用 ;
            ```
        - App.js
            ```javascript=
            import React from 'react';
            import Sid from './Sid';

            class App extends React.Component {
                render() {
                    return(
                        <div>
                            <Sid/>
                        </div>
                    )
                }
            }

            export default App;
            ```
    - 使用 function 
        - 外部
        ```javascript=
        function Test(props) {
            return <h2>hello {props.name}</h2>;
        }

        class App extends React.Component {

            render() {
                return(
                    <div>
                        <Test name = 'fuc'/>
                    </div>
                )
            }
        }
        ```
        - 內部
        ```javascript=
        insert_texts = (name) => {
            console.log(name + this.state.name);
        }
    
        render() {
            return (
                {this.insert_texts('a')}
            )
        }
        ```
    - props : property
        ```javascript=
        class App extends React.Component {
            constructor(props) { // 建構子
                super(props); // 繼承 props，一定要這行
                this.state = {
                    test : 'tommy'
                };
            }

            render() {
                return(
                    <div>
                        {this.state.test}
                    </div>
                )
            }
        }
        ```

    - 註解 : 
        ```javascript=
        {/* 多行註解 */}
        {
           // 單行註解
        }
        ```
    
    - export 多個 class
        - 要 export 的 js
        ```javascript=
        import React from 'react';

        class Test2 extends React.Component {
            render() {
                return (
                    <div>
                        Test :
                    </div>
                )
            }
        }

        class Test3 extends React.Component {
            render() {
                return (
                    <div>
                        Test :
                    </div>
                )
            }
        }

        export {
            Test2,
            Test3
        }
        ```
        - 要 import 的 js
        ```javascript=
        import {Test2, Test3} from './Test';
        ```
    - map (for) : 
        ```javascript=
        function MakeName(name) {
            var new_name = name.map(function (value, index) { // value = 值, index 從 0開始
                const new_value = value + 1;
                return <li id = {index} key = {index}>{new_value}</li> 
            }) // 一定要有 key，不然會有 error
            return <div>{new_name}</div>
        }
    
        class Test2 extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    num : 5,
                    name : ['a', 'b', 'c']
                };
            }
            const each_name = MakeName(this.state.name);
    
            render {
                return (
                    <div>
                       {each_name} 
                   </div> 
                )
            }
        }
        ```
    - onchange call function :
        ```javascript=
        function detectInput(e) {
            console.log(e);
        }
        <input type = 'text' onChange = {detectInput}/>
        ```
    
    - change state value : 
        ```javascript=
        constructor(props) {
            super(props);
            this.state = {
                name : ['a', 'b', 'c']
            };
        }
    
        this.setState({
          name: new_name //更新State
        })
        ```
## router
- install : 
    `npm install react-router-dom --save`
    
- 教學 : 
    - https://ithelp.ithome.com.tw/articles/10305953?sc=iThelpR
    - https://ithelp.ithome.com.tw/articles/10305940
    
- 設定 : 
    ```javascript=
    import logo from './logo.svg';
    import './App.css';
    import React from 'react';
    import Sid from './Sid';
    import {Test2, Test3} from './Test';
    import { BrowserRouter, Routes, Route } from "react-router-dom";



    function Test(props) {
        return <h2>hello {props.name}</h2>;
    }

    class App extends React.Component {
        render() {
            return(
                <BrowserRouter basename="/pig"> // 設定 app 的 route
                // 設定其他 route
                <Routes>
                  <Route path="/" element={<Sid />} />
                  <Route path="page1" element={<Test2 />} />
                  <Route path="*" element={<Test3 />} />
                </Routes>
                </BrowserRouter>
            )
        }
    }

    export default App;
    ```
    
## ES6
- 教學 : 
    - https://ithelp.ithome.com.tw/articles/10217085
- function 定義
    - 原本的 : 
        ```javascript=
        function test(a, b) {
            console.log(a);
        }
        ```
    - ES6 : 
        ```javascript=
        function test = (a,b) => {
            console.log(a);
        }

- 當變數名稱和要傳的物件名稱一樣
    - 原本的 :
    ```javascript=
    var a = 1;
    var b = 2;
    var c = {
    a : a,
    b : b
    }
    ```
    - 可改成 :
    ```javascript=
    var a = 1;
    var b = 2;
    var c = {
    a,
    b
    }
