iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0

大家好,時間過得真快,Todo List也進入第三天了!今天主要新增一個「依日期與時間排序」的功能,另外也把 HTML 版面跟 CSS 樣式整個順過一遍。


一、排序功能:

let sortByDate = false;

先加一個布林值狀態,預設是 false(不排序,維持原本的順序)。

const sortCheckbox = getElement<HTMLInputElement>("#sort-by-date");

沿用昨天的getElement<T>函式,把HTML裡新加的排序勾選框抓出來。

sortCheckbox.addEventListener("change", () => {
	sortByDate = sortCheckbox.checked;
	renderTodos();
});

監聽checkbox的change事件:使用者一勾選或取消勾選,就把sortByDate更新成checkbox 目前的勾選狀態,然後重新渲染畫面。這裡沒有另外寫一個「排序按鈕」,而是直接用 checkbox的狀態當開關。

二、排序邏輯:

function sortTodos(todoItems: Todo[]): Todo[] {
	if (!sortByDate) {
	    return todoItems;
	}
	
	return [...todoItems].sort((first, second) => {
		const dateOrder = first.date.localeCompare(second.date);
	    return dateOrder === 0 ? first.time.localeCompare(second.time) : 
	    dateOrder;
	});
}

sortTodos函式會接收一個Todo[]陣列,回傳排序後的結果。

  1. 如果sortByDate是 false:直接把傳進來的陣列丟回去,不做任何處理。
  2. 如果是sortByDate是true:因為<input type="date">抓出來的值格式固定是YYYY-MM-DD,<input type="time">抓出來的值格式固定是HH:mm,這兩種格式都是由較大的時間單位排到較小的時間單位,因此可以使用localeCompare()比較字串,不一定要轉成 Date 物件。
  3. 先比較date,如果日期相同(dateOrder === 0)才進一步比較time。
  4. 這裡用了 [...todoItems] 先複製一份陣列再使用 sort(),因為 sort() 會直接修改原本的陣列。先建立新的陣列再排序,可以避免改動傳進來的 todoItems。

三、渲染:新增sortTodos

function renderTodos(): void {
	const visibleTodos = sortTodos(filterTodos());
}

先用filterTodos()篩出目前分類要顯示的項目,再把結果丟進sortTodos()決定要不要排序。

四、HTML優化:

<form id="todo-form" class="todo-form">
	<div class="form-grid">
	    <div class="form-field">
		      <label for="todo-input">任務</label>
		      <input id="todo-input" type="text" placeholder="輸入任務名稱" />
	    </div>
	    <div class="form-field">
		      <label for="category-select">分類</label>
		      <select id="category-select" aria-label="選擇任務分類"></select>
	    </div>
	    <div class="form-field">
		      <label for="date-input">日期</label>
		      <input id="date-input" type="date" />
	    </div>
	    <div class="form-field">
		      <label for="time-input">時間</label>
		      <input id="time-input" type="time" />
	    </div>
		    <button type="submit">新增 Todo</button>
	</div>
	<p id="form-message" class="form-message" role="status"></p>
</form>

每個輸入欄位都包了一層.form-field,裡面加上對應的<label>(label的for屬性有對應到input的id)。

再來是新加的排序checkbox:

<label class="sort-control" for="sort-by-date">
  <input id="sort-by-date" type="checkbox" />
  依日期與時間排序
</label>

這裡直接把<input>包在 <label> 裡面,這樣使用者點擊文字「依日期與時間排序」也可以觸發勾選,不用非得點在那個小方框上。

五、CSS 優化:

今天的CSS主要是配合HTML的結構調整跟著改,這裡就直接展示一下重點的樣式,不特別逐行說明:

.form-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}

.form-field {
  display: grid;
  gap: 6px;
}

.sort-control {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 14px 0;
  color: #65718a;
  cursor: pointer;
}

.sort-control input {
  flex: none;
  width: 18px;
  height: 18px;
  min-height: auto;
}
  • .form-grid跟.form-field:搭配今天新的表單結構,讓每個欄位跟它的label用Grid排整齊。
  • .sort-control:排序checkbox搭配文字的簡單樣式,順便把cursor: pointer加上去,滑鼠移過去會變成手指,提示使用者這個區塊可以點擊。

.todo-item 也微調成Grid版面:

.todo-item {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  column-gap: 10px;
  row-gap: 6px;
  padding: 12px;
  border: 1px solid #e1e5ed;
  border-radius: 8px;
}

.todo-details {
  grid-column: 2;
  color: #65718a;
  font-size: 0.85rem;
}

.delete-button {
  grid-column: 3;
  grid-row: 1 / span 3;
  color: #b42318;
  border-color: #f0c6c2;
}
  • grid-template-columns: auto 1fr auto:將每個待辦事項分成勾選框 / 內容 / 刪除按鈕三欄
  • grid-row: 1 / span 3:讓刪除按鈕從第1個grid row開始,跨越3個row,讓它能維持在右側。搭配align-items: center,讓刪除按鈕維持在右側並盡量保持垂直置中。

小結

今天將todo list的實作做了最後的修改,鐵人賽也漸漸來到了結尾,明天會寫點鐵人賽的心得,至於todo list是否會推送到github上,我會稍加考慮,今天就到這邊啦,大家再見!


上一篇
[Day-28]todo list實作(2)
下一篇
[Day-30]總結
系列文
從零開始學習TypeScript:菜鳥的入門介紹與概覽 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言