---
# System prepended metadata

title: 'Final Project: To-do list 組內教學'

---

# Final Project: To-do list 組內教學
:::info
將Final Project的部分模組化
:::

# code

```xml=
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://getbootstrap.com/docs/5.3/assets/css/docs.css" rel="stylesheet">
    <script src="https://kit.fontawesome.com/d4981dc1b8.js" crossorigin="anonymous"></script>
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        body, html{
            height: 100%;
            background-color: black;
            /* opacity: 0.5; */
        }
        .banner{
            height: 100%;
            /*  https://source.unsplash.com/1280x720/?city,water,people*/
            /* background: 
                center / cover
                no-repeat
                fixed; */
            filter: blur(4px);
            opacity: 0.5;
        }
        .medium{
            height: 80%;
            width: 35%;
            position: absolute;
            margin: auto;
            color: white;
            top: 0px;
            left: 0px;
            right: 0px;
            bottom: 0px;
            /* filter: blur(10px); */
            backdrop-filter: blur(20px);
            -webkit-backdrop-filter: blur(20px);
            border-radius: 150px;
            /* opacity: 0.7; */
        }
        .container{
            height: 80%;
            position: absolute;
            margin: auto;
            color: white;
            top: 0px;
            left: 0px;
            right: 0px;
            bottom: 0px;
            /* backdrop-filter: blur(10px); */
            /* -webkit-backdrop-filter: blur(10px); */
            border-radius: 10px;
            /* opacity: 0.6; */
        }
        .to-do-list{
            overflow: hidden;
            max-height: 80%;
        }
        .to-do-list:hover{
            overflow-y: auto;
        }
        .to-do-list-block{
            max-height: 70%;
        }
        .to-do-list-block button{
            color: white;
        }
    </style>
</head>
<body>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
    <div class="banner">
    </div>
    <div class="medium">
    </div>
    <div class="container content text-center d-flex flex-column justify-content-center">
        <div class="row clock">
            <span class="clock fs-2" id="time">
            </span>
        </div>
        <div class="row name">
            <span class="fs-3"></span>
        </div>
        <div class="row to-do-list-block flex-column justify-content-start">
            <div class="to-do-list w-auto mx-auto">
            </div>
            <div class="list-item-template d-none">
                <div class="list-item d-flex justify-content-center">
                    <button class="btn btn-none p-0 m-0" type="button" onclick="changeItemState(-1)"> 
                    </button>
                    <p class="list-item-content p-0 m-0 ms-2">Lorem ipsum dolor sit amet.</p>
                </div>
            </div>
            <div class="btn-control text-center d-flex mt-1 justify-content-center">
                <div class="p-0 m-1" id="addList">
                    <button type="button" class="fa-solid fa-plus btn btn-none p-0 m-0" data-bs-toggle="modal" data-bs-target="#exampleModal">
                    </button>
                    <!-- Modal -->
                    <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
                        <div class="modal-dialog">
                            <div class="modal-content">
                                <div class="modal-header">
                                    <h1 class="modal-title fs-5 text-dark" id="exampleModalLabel">新增待辦事項</h1>
                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                </div>
                                <div class="modal-body">
                                    <div class="input-group mb-3">
                                        <span class="input-group-text" id="basic-addon1">項目內容</span>
                                        <input type="text" class="form-control" placeholder="" id="itemContent" aria-label="Username" aria-describedby="basic-addon1">
                                    </div>
                                </div>
                                <div class="modal-footer">
                                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                                    <button type="button" class="btn btn-primary" id="addNewListItem" onclick="addNewListItem()"  data-bs-dismiss="modal">保存</button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="p-0 m-1" id="show">
                    <button type="button" class="fa-regular fa-circle btn btn-none p-0 m-0" onclick="changeListState()" id="showItem">
                    </button>
                </div>
                <div class="p-0 m-1" id="setting">
                    
                    <button class="fa-solid fa-gears btn btn-none p-0 m-0" type="button" data-bs-toggle="offcanvas" onclick="showSetting()" data-bs-target="#offcanvasExample" aria-controls="offcanvasExample">
                    </button>
                      
                    <div class="offcanvas offcanvas-start" tabindex="-1" id="offcanvasExample" aria-labelledby="offcanvasExampleLabel">
                        <div class="offcanvas-header">
                            <h5 class="offcanvas-title" id="offcanvasExampleLabel">設定</h5>
                            <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
                        </div>
                        <div class="offcanvas-body">
                            <div class="setting-input-group mb-3">
                                <span class="input-group-text" id="basic-addon1">使用者名稱</span>
                                <input type="text" class="form-control" placeholder="" id="userName" aria-label="Username" aria-describedby="basic-addon1">
                            </div>
                            <div class="setting-input-group mb-3">
                                <span class="input-group-text" id="basic-addon1">主題（以逗號分隔）</span>
                                <input type="text" class="form-control" placeholder="" id="theme" aria-label="theme" aria-describedby="basic-addon1">
                            </div>
                            <div class="btn-control d-flex justify-content-start mb-5">
                                <button type="button" class="btn btn-danger" onclick="resetData()">清除所有待辦事項</button>
                            </div>
                            <div class="btn-control d-flex justify-content-end">
                                <button type="button" class="btn btn-primary" id="addNewListItem" onclick="updateSetting()" data-bs-dismiss="offcanvas">儲存設定</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script>
        function updateDate(){
            let clocktime = document.getElementById("time");
            let datetime = new Date();
            let hour = datetime.getHours();
            let min = datetime.getMinutes();
            let sec = datetime.getSeconds();
            if(hour < 10){
                hour = '0' + hour;
            }
            if(min < 10){
                min = '0' + min;
            }
            if(sec < 10){
                sec = '0' + sec;
            }
            
            let clockString = `${hour}:${min}:${sec}`;
            clocktime.textContent = clockString;
        }
        let interval = 1000 // ms
        updateDate();
        setInterval(updateDate, interval);
    </script>
    <script>
        let template = document.querySelector(".list-item-template").cloneNode(true);
        let showFinished = false;
        updateList();
        updateWindow();
        function addNewListItem(){
            let contentInput = document.getElementById("itemContent");
            let content = contentInput.value;
            let toDoListLocalStorage =  getData();
            toDoListLocalStorage.push(
                {
                    data: content,
                    isfinished: false,
                }
            );
            setData(toDoListLocalStorage);
            contentInput.value = '';
            
            updateList();
        }
        function updateList(){
            let toDoList = getData();
            let list = document.querySelector(".to-do-list");
            list.innerHTML = '';
            for(i = 0;i < toDoList.length;i++){
                if(!showFinished && toDoList[i].isfinished) continue;
                // to-do: clone element
                let listItem = document.querySelector(".list-item-template .list-item").cloneNode(true);
                let listItemBtn = listItem.getElementsByClassName("btn")[0];
                let listItemContent = listItem.getElementsByClassName("list-item-content")[0];
                
                if(toDoList[i].isfinished){
                    listItemBtn.classList.add("fa-solid");
                    listItemBtn.classList.add("fa-check");
                }else{
                    listItemBtn.classList.add("fa-regular");
                    listItemBtn.classList.add("fa-circle");
                }

                listItemBtn.setAttribute('onclick', `changeItemState(${i})`);
                listItemContent.textContent = toDoList[i].data;
                list.appendChild(listItem);
                // add listitem
                // let listItem = document.createElement("div");
                // listItem.setAttribute('class', "list-item d-flex justify-content-center");
                // list.appendChild(listItem);
                
                // add btn
                // let btn = document.createElement("button");
                // if(listState == 'showItem'){
                //     btn.setAttribute('class', "fa-regular fa-circle btn btn-none p-0 m-0");
                // }else{
                //     if(toDoList[i].isfinished){
                //         btn.setAttribute('class', "fa-solid fa-check btn btn-none p-0 m-0");
                //     }else{
                //         btn.setAttribute('class', "fa-regular fa-circle btn btn-none p-0 m-0");
                //     }
                // }
                // to-do:concrete button 

                // btn.setAttribute('type', "button");
                // btn.setAttribute('onclick', `changeItemState(${i})`);
                // console.log(btn);

                // add content
                // let contentblock = document.createElement("p");
                // contentblock.setAttribute('class', "list-item-content p-0 m-0 ms-2");
                // contentblock.textContent = toDoList[i].data;
                
                // append
                // listItem.appendChild(btn);
                // listItem.appendChild(contentblock);
            }
        }
        function changeItemState(i){
            let toDoList = getData();
            toDoList[i].isfinished = !toDoList[i].isfinished;
            setData(toDoList);
            updateList();
        }
        function getData(){
            let toDoListLocalStorage =  JSON.parse(localStorage.getItem('toDoList')) ?? [];
            return toDoListLocalStorage;
        }
        function setData(toDoListLocalStorage){
            localStorage.setItem("toDoList", JSON.stringify(toDoListLocalStorage));
        }
        function resetData(){
            localStorage.setItem('toDoList', JSON.stringify([]));
            updateList();
        }
        function changeListState(){
            let showItemBtn = document.getElementById("showItem");
            showFinished = !showFinished;

            if(showFinished){
                showItemBtn.classList.remove("fa-regular");
                showItemBtn.classList.remove("fa-circle");
                showItemBtn.classList.add("fa-solid");
                showItemBtn.classList.add("fa-check");
            }else{
                showItemBtn.classList.remove("fa-solid");
                showItemBtn.classList.remove("fa-check");
                showItemBtn.classList.add("fa-regular");
                showItemBtn.classList.add("fa-circle");
            }
            updateList();
        }
        
        function updateWindow(){
            let setting = getSetting();
            let userName = setting['userName'];
            let themeList = setting['theme'];
            showName(userName);
            showBackgroud(themeList);
        }
        function showName(userName){
            let userNameHTML = document.querySelector(".name span");
            userNameHTML.textContent = userName;
        }
        function showBackgroud(themeList){
            let banner = document.querySelector(".banner");
            let url = "https://source.unsplash.com/1280x720/"
            banner.style.background = `url(${url}?${themeList}) center / cover no-repeat fixed`;
        }
        function showSetting(){ // show on the input group
            let settingLocalStorage = getSetting();
            let settingInput = document.querySelectorAll(".setting-input-group");
            for(i = 0;i < settingInput.length;i++){
                let formControl = settingInput[i].querySelector(".form-control");
                let id = formControl.id;
                formControl.value = settingLocalStorage[id];
            }
        }
        function updateSetting(){
            let settingLocalStorage = getSetting();
            let settingInput = document.querySelectorAll(".setting-input-group");
            for(i = 0;i < settingInput.length;i++){
                let formControl = settingInput[i].querySelector(".form-control");
                let id = formControl.id;
                let value = formControl.value;
                settingLocalStorage[id] = value;
            }
            localStorage.setItem("setting", JSON.stringify(settingLocalStorage));
            updateWindow();
        }
        
        function getSetting(){
            if(localStorage.getItem('setting')){
                return JSON.parse(localStorage.getItem('setting'));
            }else{
                let setting = {
                    userName: "ZHAO-XIN, PAN",
                    theme: "city",
                }
                localStorage.setItem('setting', JSON.stringify(setting));
                return JSON.parse(localStorage.getItem('setting'));
            }
        }
    </script>
</body>
</html>
```

# 完成To-do list要寫的東西
* 背景置中 + 背景模糊化 + 內容置中
* 電子時鐘
* To-do list 區塊版面
* To-do list 內容太多會產生卷軸
* 按鈕樣式：fontawesome
* 按鈕效果
    * 左邊的按鈕：新增新的待辦事項
    * 中間的按鈕：可以切換顯示已經完成的待辦事項
    * 右邊的按鈕：打開設定頁面
* 處理待辦事項的資料(只處理資料)
    * 新增
    * 標示為完成
    * 刪除
* 處理設定的資料(只處理資料)
    * 更新


# 背景置中 + 背景模糊化 + 內容置中

![](https://i.imgur.com/mDBD8AK.jpg)

### HTML
```xml=
<div class="banner">
<!-- 背景區塊 -->
</div>
<div class="medium">
<!-- 讓背景模糊的區塊 -->
</div>
<div class="content text-center d-flex flex-column justify-content-center ">
    <p>To-do list 區塊</p>
</div>
```

### CSS

```xml=
<style>
    body, html{
        height: 100%;
    }
    .banner{
        height: 100%;
        background: 
            url(https://picsum.photos/id/16/1920/1080)
            center / cover
            no-repeat
            fixed; 
    }
    .medium{
        width: 40%;
        height: 80%;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        margin: auto;
        backdrop-filter: blur(20px);
    }
    .content{
        width: 40%;
        height: 80%;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        margin: auto;
    }
</style>
```


# 電子鐘

* HTML
```xml=
<div id="time">

</div>

```

* CSS

```xml=
<style>
    #date{
        width: 300px;
        height: 200px;
    }
</style>
```
* JS 

```javascript=
function updateDate(){
    let clocktime = document.getElementById("time");
    let datetime = new Date();
    let hour = datetime.getHours();
    let min = datetime.getMinutes();
    let sec = datetime.getSeconds();
    // 記得補0
    if(hour < 10){
        hour = '0' + hour;
    }
    if(min < 10){
        min = '0' + min;
    }
    if(sec < 10){
        sec = '0' + sec;
    }
    let clockString = `${hour}:${min}:${sec}`;
    clocktime.textContent = clockString;
}
let interval = 1000 // ms
updateDate();
setInterval(updateDate, interval);
```

# To-do List 區塊版面
![](https://i.imgur.com/Mwk3M9H.jpg)

* HTML
```xml=
<body>
    <div class="banner">

    </div>
    <div class="medium">

    </div>
    <div class="container content text-center d-flex flex-column justify-content-center">
        <div class="row time">
            <span class="clock" id="time">
                10:27
            </span>
        </div>
        <div class="row name">
            <span>
                潘兆新
            </span>
        </div>
        <div class="row to-do-list-block flex-column justify-content-start">
            <div class="to-do-list">
                <div class="list-item d-flex justify-content-center">
                    <button class="btn btn-primary" type="button"> 
                    </button>
                    <p class="list-item-content p-0 m-0 ms-2">Lorem ipsum dolor sit amet.</p>
                </div>
                <div class="list-item d-flex justify-content-center">
                    <button class="btn btn-primary" type="button"> 
                    </button>
                    <p class="list-item-content p-0 m-0 ms-2">Lorem ipsum dolor sit amet.</p>
                </div>
                <div class="list-item d-flex justify-content-center">
                    <button class="btn btn-primary" type="button"> 
                    </button>
                    <p class="list-item-content p-0 m-0 ms-2">Lorem ipsum dolor sit amet.</p>
                </div>
            </div>
            <div class="btn-control d-flex justify-content-center">
                <div class="m-1" id="addList">
                    <button class="btn btn-primary" type="button"> 
                    </button>
                </div>
                <div class="m-1" id="show">
                    <button class="btn btn-primary" type="button"> 
                    </button>
                </div>
                <div class="m-1" id="setting">
                    <button class="btn btn-primary" type="button"> 
                    </button>
                </div>
            </div>
        </div>
    </div>
</body>
```

* CSS 
```xml=
<style>
    body, html{
        height: 100%;
    }
    .banner{
        height: 100%;
        background: 
            url(https://picsum.photos/id/16/1920/1080)
            center / cover
            no-repeat
            fixed; 
    }
    .medium{
        width: 35%;
        height: 80%;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        margin: auto;
        backdrop-filter: blur(20px);
    }
    .content{
        height: 80%;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        margin: auto;
    }
</style>
```

# To-do list 內容太多產生卷軸

* HTML

```xml=
<body>
    <div class="to-do-list">
        內容還沒有超過<br>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat vitae quas expedita eum ad quasi modi. At deserunt maxime qui fugiat possimus praesentium dolor fuga? Fugit asperiores aliquam molestias obcaecati.
    </div>
    <div class="to-do-list">
        內容超過max-height<br>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat vitae quas expedita eum ad quasi modi. At deserunt maxime qui fugiat possimus praesentium dolor fuga? Fugit asperiores aliquam molestias obcaecati.
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat vitae quas expedita eum ad quasi modi. At deserunt maxime qui fugiat possimus praesentium dolor fuga? Fugit asperiores aliquam molestias obcaecati.
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat vitae quas expedita eum ad quasi modi. At deserunt maxime qui fugiat possimus praesentium dolor fuga? Fugit asperiores aliquam molestias obcaecati.
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat vitae quas expedita eum ad quasi modi. At deserunt maxime qui fugiat possimus praesentium dolor fuga? Fugit asperiores aliquam molestias obcaecati.
    </div>
</body>
```

* CSS 

```xml=
<style>
    body, html{
        height: 100%;
    }
    .to-do-list{
        width: 200px;
        max-height: 300px;
        margin: 20px;
        border: 2px red dashed;
        overflow-y: hidden;
    }
    .to-do-list:hover{
        overflow-y: auto;
    }
</style>
```

# 按鈕-左：新增待辦事項

# 按鈕-中：顯示/隱藏完成的待辦事項

# 按鈕-左：開啟設定