---
# System prepended metadata

title: JS_練習_可排序表單
tags: [javascript]

---

# JS_練習_可排序表單
能夠將電腦亂排順序的名單，自由選取排列，排列正確會變綠色，順序錯誤會是紅色。


- 純HTML畫面
![](https://i.imgur.com/PtjhA6y.png)
- 成品畫面
![](https://i.imgur.com/R7NgtJh.png)




### HTML
再HTML建立空白的無序列表，等JS再將內容加至設定好的`ul`，最後再加上一個`button`以執行JS已確認排序是否正確。

```.html
    <h1>Top 10 Videos on Youtube 2020</h1>
    <p>Drag and Drop items into their corresponding spots</p>
    <ul class="draggable-list" id="draggable-list">


    </ul>
    <button class="check-btn" id="check-btn">
        Check Order
        <i class="fas fa-paper-plane"></i>

    </button>
```
### CSS
#### body::after
在根元素`body`後面，透過偽元素選擇器，使整個body加上一層半透明的黑影，使畫面看起來質感加分。
```.css
body::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.6);
  }
  ```
#### 偽類
常見的偽類樣式：
- `:active`:滑鼠按下會產生的狀態。
- `:focus`:鍵盤聚焦會產生的狀態。
- `:hover`:滑鼠滑過會產生的狀態。
- `:link`:還沒訪過會產生的狀態。
- `:visited`:被訪過會產生的狀態。



## JS
### 實現步驟
- 創造一個陣列放入容器，陣列是有索引值與內容。
- 利用sort方法將隨機產生的索引值按照大小排列，
- 
### Drag and Drog API

- 創造要丟進DOM的陣列
```.js
- const hottestVideo = [
    '羅一鈞副組長',
    '蔡阿嘎與孕婦二伯',
    '粿粿-花蓮落下事件',
    '這群人-請假經典語錄',
    '黃氏兄弟-我想跟家人說',
    '中指通的歐美女優精選',
    '孫生去監獄看媽媽',
    '狠愛演-謝謝大家',
    '反正我很閒-卑鄙源之助的一天',
    '東森51-臨終前最後26秒奮力道別'
]
```
### creatList()
- 利用`...`將陣列展開變成個別的值。
- 利用`forEach()`將每個陣列的值，都放到 li 裡，並設定li裡的屬性及加上索引值。
- 填入HTML的內容在我們所定義好屬性的陣列裡。
- part II: 利用`map()`方法創造新陣列。回傳
   - 該影片的value
   - 創造一個sort，並用`Math.random`方法隨機產生0~1的數字
- part III: 利用sort方法將random出來的數字按照大小排列。
- part IV: 再利用map方法將我們part II產生的value 
[sort()小科普](https://realdennis.medium.com/javascript-%E5%BE%9Earray%E7%9A%84sort%E6%96%B9%E6%B3%95-%E8%81%8A%E5%88%B0%E5%90%84%E5%AE%B6%E7%80%8F%E8%A6%BD%E5%99%A8%E7%9A%84%E5%AF%A6%E4%BD%9C%E7%AE%97%E6%B3%95-c23a335b1b80)

```.js
creatList()

//creat List items into DOM
function creatList(){
    [...hottestVideo]
    //part II
    .map(function(a){
        return{
            value:a ,
            sort: Math.random()
        }
    })//part III
    .sort(function(a,b){
        return (a.sort - b.sort)
    })//part IV
    .map(function(a){
        return a.value
    })
    .forEach(function(video,index) {
        const listItem = document.createElement('li');
        //index =0~9
        listItem.setAttribute('data-index', index);
        listItem.innerHTML = `
        <span class ='number'>${ index + 1 }</span>
        <div class ="draggable" draggable ="true">
            <p class ="video-name">${video}</p>
            <i class = "fas fa-grip-lines></i>
        </div>
        `;
        listItems.push(listItem);
        draggableList.appendChild(listItem);
    });
}
```
- 函式外呼叫函式，畫面呈現。

![](https://i.imgur.com/GXgLXsv.png)
- 加入partII/ part III
![](https://i.imgur.com/0KdF1YZ.png)
- 加入part IV
![](https://i.imgur.com/s6PQD20.png)


#### addEventListener()
- [drag event 小科普](https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_Drag_and_Drop_API)
- 設定 ==drag==事件，其中分為兩項觸發
  - 被選擇的物件。
    - dragstart 方法就是在物件開始拖曳時觸發。
      - 在觸發事件時利用`closest()`方法取得當前元素最近得祖先元素，並透過`getAttribute得到該元素的index`
       [closest()小科普](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/closest) 
    
  - 可以當作目標的物件。
    - dragleave 是當被選擇物件被拖曳到可以當作目標物件的上方時，在離開目標物件上方時，瞬間觸發。
    - dragover 當被選擇物件被拖曳到可以當目標物件上方時觸發。是目標物件觸發！不是被選擇物件。
    - drop 當被選擇物件被拖曳到目標物件時觸發。是目標物件觸發！不是被選擇物件。
        - 建立函式`swapItem()`，利用最上面先建立一個空陣列listitems。再設立參數並將值選出且利用`appendChild`賦值，將內容進行交換。
```.js
 addEventListeners()
    });
}
function dragStart() {
    // console.log('event: ' , 'drapstart')
    dragStartIndex = this.closest('li').getAttribute('data-index');
    // console.log(dragStartIndex);
  }
  function dragOver() {
    console.log('event: ' , 'drapover')
  }
  function dragDrop() {
    // console.log('event: ' , 'drapDrop')
    
    const dragEndIndex = +this.getAttribute('data-index')
    swapItems(dragStartIndex, dragEndIndex);
    this.classList.remove('over');
  }

  function swapItems(fromIndex, toIndex){
    //   console.log(124);
    const itemOne = listItems[fromIndex].querySelector('.draggable')
    const itemTwo = listItems[toIndex].querySelector('.draggable');
    // console.log(itemOne, itemTwo);
    listItems[fromIndex].appendChild(itemTwo);
    listItems[toIndex].appendChild(itemOne);
  }
  function dragLeave() {
    console.log('event: ' , 'dragleave')
  }
  function dragEnter() {
    console.log('event: ' , 'dragenter')
  }

function addEventListeners(){
    const draggables = document.querySelectorAll('.draggable')
    const dragListItems = document.querySelectorAll('.draggable-list li')
    draggables.forEach(function(draggable){
        draggable.addEventListener('dragstart', dragStart);
    });
    dragListItems.forEach(function (item) {
        item.addEventListener('dragover', dragOver);
        item.addEventListener('drop', dragDrop);
        item.addEventListener('dragenter', dragEnter);
        item.addEventListener('dragleave', dragLeave);
      });

}
```
#### click event
將HTML所設定的button，綁定一個`click事件`，呼叫`checkOrder`函式。
#### checkOrder()
由於上面有設定random公式，當畫面reload時，是隨機排列的。我們將列表重新排序時，最後點下btn，確認是否排列正確。
設定變數`videoName`取得listItem裡的文字，
將`videoName`跟原先設定好的`listItem[]`裡相同位置的文字進行比對。
若不相同 ＝>加上屬性 `wrong`
若相同 ＝>移除屬性 `wrong`增加屬性`right`
```.js
$('#check-btn').click(checkOrder);

function checkOrder(){
    listItems.forEach(function(listItem, index){
        const videoName = listItem.querySelector('.draggable').innerText.trim();
        //  console.log(videoName)
         if(videoName !== hottestVideo[index]){
            listItem.classList.add('wrong');
        }else{
            listItem.classList.remove('wrong');
            listItem.classList.add('right');
        }

    })
}
```
###### tags: `javascript`