功能
創建有序列表
隨機產生列表
使用者可以隨意拖曳項目到不同位置
檢查按鈕檢查順序是否正確
錯誤顯示紅色,正確顯示綠色
學習到的知識點 HTML
draggable="true"
data-*
attribute
JS
Drag and Drop API
.setAttribute()
[...]
.sort()
map()
簡介 Demo
HTML 結構
1 2 3 4 5 6 7 8 9 10 <body > <h1 > TOP 10 World's Most Valuable Brands in 2021</h1 > <p > Drag and drop the items into their corresponding spots</p > <ul class ="draggable-list" id ="draggable-list" > </ul > <button class ="check-btn" id ="check" > Check Order <i class ="fas fa-paper-plane" > </i > </button > </body >
CSS 完整Code
flex
:flex-grow flex-shrink flex-basic
flex:
放大比例 縮小比例 計算元素是否有多餘空間,預設值為auto。
flex:1
的意思為flex: 1 1 0
,所以數字與<i class="fas fa-grip-lines"></i>
的空間,最小最大都會是1,所以不會變形。
1 2 3 4 5 .draggable-list li { background-color : #fff ; display : flex; flex : 1 ; }
1 2 3 4 5 6 7 8 .draggable { cursor : pointer; display : flex; align-items : center; justify-content : space-between; padding : 15px ; flex : 1 ; }
JS 完整Code
宣告變數 排序的內容為,2021世界十大最有價值的品牌 。
原生JS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const draggable_list = document .getElementById ("draggable-list" );const check = document .getElementById ("check" );const powerfulBrands = [ "Apple Inc." , "Amazon.com Inc." , "Microsoft" , "Google" , "Samsung" , "Coca-Cola" , "Toyota" , "Mercedes-Benz" , "McDonald’s" , "Disney" , ];const listItems = [];let dragStartIndex;
Jquery 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const draggable_list = $('#draggable-list' )[0 ]const powerfulBrands = [ "Apple Inc." , "Amazon.com Inc." , "Microsoft" , "Google" , "Samsung" , "Coca-Cola" , "Toyota" , "Mercedes-Benz" , "McDonald’s" , "Disney" , ];const listItems = [];let dragStartIndex;
將陣列放入DOM裡 展開運算子 在 ES6 中,新增了一個 [ … ]
的關鍵字,這個關鍵字在不同時間點,有不同的效果,有些時候它會被當作展開運算子(spread operator)使用,有時候則是被當作其餘運算子(rest operator)使用。
展開運算子,是把許多的參數轉換成一個陣列 ,而展開運算子則是可以把陣列中的元素取出 。
在實作電影定位介面 時,也有使用到此技巧。
.setAttribute()
Element.setAttribute(name, value)
增加指定名稱 和值 的新屬性,或者把一個現有的屬性設定為指定的值。
舉例來說
setAttribute Demo
用html設定一個Button
1 2 3 <body > <button > Hello World</button > </body >
setting設定字為紅色
1 2 3 .setting { color : red; }
b
變數取得button
,再利用setAttribute
增加樣式 與方法屬性 。
1 2 3 4 5 6 var b = document .querySelector ("button" ); b.setAttribute ("id" , "helloButton" ); b.setAttribute ("class" , "setting" ); b.setAttribute ("onclick" , "javascript:alert('test!')" );console .log (b)
HTML5 中的 data-*
attribute 屬性 在製作網頁的過程中,我們常常會添加一些自己需要用到的屬性名稱 ,以方便自己容易理解,為了要避免 大家在 HTML 結構中隨意的添加屬性,在 HTML5 中就多了 data-*
attribte 這個屬性,其中的 *
就是一個可以自定義 的名稱。例如:data-key='83'
或者是 data-item='1'
。
而這邊的範例就是data-index
。
原生JS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 createList ();function createList ( ) { [...powerfulBrands].forEach ((company, index ) => { const listItem = document .createElement ("li" ); listItem.setAttribute ("data-index" , index); listItem.innerHTML = ` <span class="number">${index + 1 } </span> <div class="draggable" draggable="true"> <p class="person-name">${company} </p> <i class="fas fa-grip-lines"></i> </div> ` ; listItems.push (listItem); draggable_list.appendChild (listItem); }); }
顯示出來的樣子
Jquery 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 .forEach ((company, index ) => { const listItem = document .createElement ("li" ); listItem.setAttribute ("data-index" , index); listItem.innerHTML = ` <span class="number">${index + 1 } </span> <div class="draggable" draggable="true"> <p class="company-name">${company} </p> <i class="fas fa-grip-lines"></i> </div> ` ; listItems.push (listItem); draggable_list.appendChild (listItem); }); addEventListeners (); }
隨機排列item順序 這邊使用的方法是
先產生隨機數
再利用sort()
方法由小排到大
完成每次都能產生隨機排列的順序
當只有.map((a) => ({ value: a, sort: Math.random() }))
時,person會印出map()
賦予的sort,可以注意到都是random( 0 - 1之間 )的值。
.map()
每個都取得隨機變數後,利用sort()
由小排到大。
排列完大小後,用map()
取出值就好。
sort()
用法解釋Array.prototype.sort()
1 2 3 4 5 6 const numbers = [1 , 3 , 110 , 40 , 302 ];console .log ( numbers.sort (function (a, b ) { return a - b; }) );
原生JS 1 2 3 4 5 6 7 8 9 [...powerfulBrands] .map ((a ) => ({ value : a, sort : Math .random () })) .sort ((a, b ) => a.sort - b.sort ) .map ((a ) => a.value ) .forEach ((person, index ) => { console .log (person)
Jquery 1 2 3 4 5 6 7 8 9 10 11 12 13 [...powerfulBrands] .map (function (data ) { return { value : data, sort : Math .random (), }; }) .sort (function (a, b ) { return a.sort - b.sort ; }) .map (function (data ) { return data.value ; })
設定事件監聽Function HTML5 Drag and Drop API 筆記
針對能夠被拖曳的元素,在其 HTML 標籤上添加屬性 draggable="true"
以下整理成表格,方便之後判斷。這次實作沒有運用到dropend
。
x
Drag Source
Drag Target
解釋
1
dragstart
開始 拖曳元素時觸發此事件()
2
drag
dragenter
拖曳 元素時觸發此事件
3
dragover
當元素拖曳到有效位置 放置則觸發此事件
4
dragleave
拖曳的元素離開有效的位置 時觸發
5
drop
在有效位置 上放置 元素時觸發此事件
6
dropend
當拖曳結束 時會觸發此事件
Javascript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function addEventListeners ( ) { const draggables = document .querySelectorAll (".draggable" ); const dragListItems = document .querySelectorAll (".draggable-list li" ); draggables.forEach ((draggable ) => { draggable.addEventListener ("dragstart" , dragStart); }); dragListItems.forEach ((item ) => { item.addEventListener ("dragover" , dragOver); item.addEventListener ("drop" , dragDrop); item.addEventListener ("dragenter" , dragEnter); item.addEventListener ("dragleave" , dragLeave); }); } check.addEventListener ("click" , checkOrder);
Jquery Jquery沒有相關的寫法,只有在 jqueryUI裡面才有。所以,以下都只有原生JS的寫法。
dragEnter()
,dragLeave()
dragenter
,拖曳元素時觸發的事件。
dragleave
,拖曳的元素離開有效的位置時觸發。
這邊做出的效果是,當拖曳的元素到其他元素上時,會變色。拖曳第一欄到第二欄上時,第二欄就變色。
但是當拖曳欄位不在有效範圍時,第二欄的欄位就不會變色。
原生JS 1 2 3 4 5 6 7 8 9 function dragEnter ( ) { this .classList .add ("over" ); }function dragLeave ( ) { this .classList .remove ("over" ); }
dragStart()
,dragDrop()
.closet是一個遍歷的方法。
.closetDemo
.getAttribute
就是前面提到的,增加指定名稱和值的新屬性,所以可以得到index。
dragstart
,開始拖曳 元素時觸發此事件。
drop
,在有效位置 上放置元素時,觸發此事件。
當開始拖曳元素欄位時,就開始記錄Index,停止拖曳元素時,也記錄當下的Index。
再利用swapItems()
,交換內容。
原生JS 1 2 3 4 5 6 7 8 9 10 11 12 function dragStart ( ) { dragStartIndex = +this .closest ("li" ).getAttribute ("data-index" ); console .log (dragStartIndex) }function dragDrop ( ) { const dragEndIndex = +this .getAttribute ("data-index" ); swapItems (dragStartIndex, dragEndIndex); this .classList .remove ("over" ); }
當我移動第一個時,dragStartIndex的改變,就會跳出0…以此類推。
dragOver()
避免預設行為,才使用swapItems()
避免被觸發提交。
dragOver
,當元素拖曳到有效位置放置則觸發此事件。
在dragOver函式裡加上避免預設行為的preventDefault()
,避免被不停觸發。
原生JS 1 2 3 4 function dragOver (e ) { e.preventDefault (); }
SwapItems()
交換items,使用到appendChild
方法。
原生JS 1 2 3 4 5 6 7 8 9 function swapItems (fromIndex, toIndex ) { const itemOne = listItems[fromIndex].querySelector (".draggable" ); const itemTwo = listItems[toIndex].querySelector (".draggable" ); listItems[fromIndex].appendChild (itemTwo); listItems[toIndex].appendChild (itemOne); }
檢查順序是否正確
原生JS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function checkOrder ( ) { listItems.forEach ((listItem, index ) => { const randomBrands = listItem.querySelector (".draggable" ).innerText .trim (); if (randomBrands !== powerfulBrands[index]) { listItem.classList .add ("wrong" ); } else { listItem.classList .remove ("wrong" ); listItem.classList .add ("right" ); } }); } check.addEventListener ("click" , checkOrder);
參考文章 JavaScript中setAttribute用法介紹
什麼是 HTML 5 中的資料屬性(data-* attribute)
製作可拖曳的元素(HTML5 Drag and Drop API)
Node Element 在 appendChild 後消失(disappear)!?